IT General
น้องๆ เคยเจอไหม เวลาเราสั่งอาหารผ่านแอป แล้วแอปมันแจ้งเตือนเราแทบจะเรียลไทม์เลยว่า "ร้านรับออเดอร์แล้ว" "เชฟกำลังทำอาหาร" "ไรเดอร์กำลังมารับ" อะไรแบบนี้แหละ นี่แหละคือ Event Driven Architecture (EDA) อย่างง่ายๆ เลย
สมัยผมทำร้านเน็ต SiamCafe.net เมื่อ 20 กว่าปีที่แล้ว ระบบมันไม่ซับซ้อนขนาดนี้หรอก (แวะไปอ่านเรื่องราวเก่าๆ ได้ที่ SiamCafe Blog) แต่ปัจจุบัน Application มันซับซ้อนขึ้นเยอะ การทำงานแบบเดิมๆ ที่ต้องรอให้ process นึงเสร็จก่อน ถึงจะไปทำอีก process นึงได้ มันไม่ทันกินแล้ว EDA เลยเข้ามาตอบโจทย์ตรงนี้แหละ
EDA คือการออกแบบระบบที่ทุกอย่างมันทำงานโดยการ "ฟัง" และ "ตอบสนอง" ต่อเหตุการณ์ (Event) ที่เกิดขึ้นในระบบ แทนที่จะต้องรอคำสั่ง หรือ query จากที่ไหนสักแห่ง พอเกิดเหตุการณ์อะไรขึ้นมา ระบบก็จะ "ปล่อย" Event ออกไป แล้วใครที่ "สนใจ" Event นั้นๆ ก็จะมา "รับ" ไปประมวลผลต่อเอง
ยกตัวอย่างง่ายๆ อีกที สมมติว่ามี Event "OrderCreated" (ออเดอร์ถูกสร้าง) ระบบ payment ก็จะ "รับ" Event นี้ไปจัดการเรื่องการจ่ายเงิน ระบบ inventory ก็จะ "รับ" Event นี้ไปจัดการเรื่องตัดสต็อกสินค้า ระบบแจ้งเตือนลูกค้าก็จะ "รับ" Event นี้ไปส่งข้อความแจ้งเตือนลูกค้า เห็นไหมว่าทุกอย่างมันทำงานแบบ Asynchronous (ไม่พร้อมกัน) และ decoupled (แยกส่วนกัน)
ทำไมมันถึงสำคัญ? เพราะมันทำให้ระบบเรา Scalable (ขยายได้ง่าย), Resilient (ทนทาน), และ Flexible (ยืดหยุ่น) ไงล่ะน้อง!
Event คือ "เหตุการณ์" ที่เกิดขึ้นในระบบ เช่น "OrderCreated", "PaymentReceived", "InventoryUpdated" Event มันจะมีข้อมูลที่เกี่ยวข้องกับเหตุการณ์นั้นๆ แนบไปด้วย เช่น ID ออเดอร์, จำนวนเงิน, รหัสสินค้า
Event Producer คือคนที่ "สร้าง" หรือ "ปล่อย" Event ออกมา เช่น ระบบสั่งอาหารจะปล่อย Event "OrderCreated" ออกมา
Event Consumer คือคนที่ "รับ" Event ไปประมวลผลต่อ เช่น ระบบ payment จะรับ Event "OrderCreated" ไปจัดการเรื่องการจ่ายเงิน
Message Broker คือตัวกลางที่ทำหน้าที่ "รับ" Event จาก Producer และ "ส่ง" Event ไปให้ Consumer ที่สนใจ สมัยผมทำร้านเน็ต ก็เหมือนมีพี่คนกลางคอยบอกว่าเครื่องไหนว่าง เครื่องไหนมีปัญหา (เปรียบเทียบให้เห็นภาพเฉยๆ นะ) ตัวอย่าง Message Broker ที่นิยมใช้กันก็เช่น Kafka, RabbitMQ
เอาล่ะน้อง มาถึงเรื่องวิธีใช้งานกันบ้าง บอกเลยว่ามันไม่ได้ยากอย่างที่คิด แต่ก็ไม่ง่ายเหมือนปอกกล้วยเข้าปากนะ (หัวเราะ)
หลักๆ เลยคือเราต้องเริ่มจากการ "ออกแบบ" ก่อน ว่าระบบเรามี Event อะไรบ้าง ใครเป็น Producer ใครเป็น Consumer แล้วเราจะใช้ Message Broker อะไร
ขั้นตอนนี้สำคัญมาก เราต้องคิดให้รอบคอบว่าระบบเรามี Event อะไรบ้าง แต่ละ Event มีข้อมูลอะไรบ้าง พยายามตั้งชื่อ Event ให้สื่อความหมาย ชัดเจน
ตัวอย่าง:
{
"eventName": "OrderCreated",
"eventVersion": "1.0",
"eventTime": "2024-01-01T12:00:00Z",
"producer": "OrderService",
"data": {
"orderId": "12345",
"customerId": "67890",
"totalAmount": 100.00
}
}
เลือก Message Broker ที่เหมาะสมกับขนาดและความต้องการของระบบเรา ถ้าเป็นระบบเล็กๆ RabbitMQ ก็อาจจะเพียงพอ แต่ถ้าเป็นระบบใหญ่ๆ ที่มีปริมาณ Event มหาศาล Kafka อาจจะเป็นตัวเลือกที่ดีกว่า
Implement Producer ให้สร้าง Event และส่งไปที่ Message Broker Implement Consumer ให้ "ฟัง" Event จาก Message Broker และประมวลผลตามที่ต้องการ
ตัวอย่าง Producer (ใช้ Python กับ RabbitMQ):
import pika
import json
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
channel.queue_declare(queue='order_queue')
message = {
'orderId': '12345',
'customerId': '67890',
'totalAmount': 100.00
}
channel.basic_publish(exchange='', routing_key='order_queue', body=json.dumps(message))
print(" [x] Sent 'OrderCreated'")
connection.close()
ตัวอย่าง Consumer (ใช้ Python กับ RabbitMQ):
import pika
import json
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
channel.queue_declare(queue='order_queue')
def callback(ch, method, properties, body):
message = json.loads(body)
print(" [x] Received %r" % message)
# Do something with the message
channel.basic_consume(queue='order_queue', on_message_callback=callback, auto_ack=True)
print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()
EDA มันไม่ใช่ Silver Bullet นะน้อง มันมีข้อดีข้อเสียของมัน ต้องเลือกใช้ให้เหมาะสมกับสถานการณ์ ถ้าเทียบกับทางเลือกอื่นอย่าง REST API หรือ Monolithic Architecture มันก็มีข้อแตกต่างกันอยู่
REST API เหมาะกับระบบที่ต้องการ Synchronous Communication (การสื่อสารแบบพร้อมกัน) และต้องการ Request/Response Cycle (วงจรการร้องขอ/ตอบกลับ) ที่ชัดเจน
Monolithic Architecture เหมาะกับระบบขนาดเล็กที่ไม่ซับซ้อนมาก เพราะมันง่ายต่อการพัฒนาและดูแลรักษา แต่พอระบบมันใหญ่ขึ้น มันจะเริ่มมีปัญหาเรื่อง Scalability และ Maintainability
ลองดูตารางเปรียบเทียบนี้เพื่อความเข้าใจที่มากขึ้น:
| Feature | Event Driven Architecture | REST API | Monolithic Architecture |
|---|---|---|---|
| Communication Style | Asynchronous | Synchronous | Internal |
| Coupling | Decoupled | Loosely Coupled | Tightly Coupled |
| Scalability | High | Moderate | Low |
| Complexity | High | Moderate | Low (Initially) |
| Use Cases | Real-time applications, microservices | Web APIs, CRUD applications | Small to medium-sized applications |
สุดท้ายนี้ อยากฝากน้องๆ ว่าเทคโนโลยีมันเปลี่ยนแปลงตลอดเวลา อย่าหยุดเรียนรู้ และหมั่นหาความรู้ใหม่ๆ อยู่เสมอ แล้วน้องๆ จะเป็น Developer ที่เก่งกาจแน่นอน! ถ้าอยากอ่านเรื่องราว IT สนุกๆ อีก อย่าลืมแวะไปที่ SiamCafe Blog นะครับ
เอาล่ะน้องๆ มาถึงส่วนที่สำคัญที่สุดแล้ว นั่นก็คือ Best Practices หรือเคล็ดลับที่พี่ได้จากการคลุกคลีกับ Event Driven Architecture (EDA) มานานโข ตั้งแต่สมัยร้านเน็ต SiamCafe ยุคบุกเบิกนู่นเลย
จำได้ว่าตอนนั้นยังไม่มี Framework อะไรให้ใช้เยอะแยะแบบสมัยนี้ ต้องเขียนเองหมด! เจอปัญหาเอง แก้เอง เรียนรู้เอง เจ็บมาเยอะ (หัวเราะ) แต่ก็คุ้มค่า เพราะมันทำให้เราเข้าใจแก่นของ EDA อย่างแท้จริง
ที่สำคัญคือ อย่ากลัวที่จะลองผิดลองถูก! EDA เป็น Paradigm ที่ค่อนข้างใหม่สำหรับหลายๆ คน ลองทำ Workshop เล็กๆ ภายในทีมดูก่อนก็ได้ แล้วค่อยๆ ขยายไปสู่ระบบใหญ่
จากประสบการณ์ที่ผ่านมา พี่ขอสรุปเทคนิคที่ใช้ได้จริง (และใช้ได้ผลดี) ในการ Implement EDA ดังนี้:
// ตัวอย่าง Code (pseudo-code)
function processOrder(orderId) {
// Check if order has already been processed
if (isOrderProcessed(orderId)) {
console.log("Order already processed!");
return; // Do nothing
}
// Process the order
// ...
// Mark order as processed
markOrderAsProcessed(orderId);
}
Code snippet ด้านบนแสดงให้เห็น Concept ของ Idempotency โดยการตรวจสอบก่อนว่า Order นี้เคยถูกประมวลผลไปแล้วหรือยัง ถ้าเคยแล้วก็ไม่ต้องทำอะไร
iCafeForexEventual Consistency คือ Concept ที่ว่าข้อมูลอาจจะไม่ Consistent ทันทีหลังจากเกิด Event แต่ในที่สุดแล้วข้อมูลจะ Consistent ในที่สุด (Eventually Consistent) ถามว่าน่ากลัวไหม? ก็ต้องบอกว่า "แล้วแต่" ขึ้นอยู่กับ Application ของเรา ถ้า Application เราต้องการ Strong Consistency (เช่น ระบบ Banking) EDA อาจจะไม่ใช่ทางเลือกที่ดีนัก แต่ถ้า Application เราต้องการ High Availability และ Scalability Eventual Consistency ก็อาจจะเป็น Trade-off ที่ยอมรับได้
Compensating Transaction คือ Transaction ที่ใช้เพื่อ "ยกเลิก" ผลกระทบของ Transaction ก่อนหน้า ในกรณีที่เกิด Error หรือ Failure ในระบบ EDA ตัวอย่างเช่น ถ้าเราตัดเงินลูกค้าไปแล้ว แต่ Order Cancel เราก็ต้องคืนเงินให้ลูกค้า Compensating Transaction นี่แหละที่จะช่วยเรา
Dead Letter Queue (DLQ) คือ Queue ที่ใช้เก็บ Event ที่ไม่สามารถประมวลผลได้ (Failed Events) การมี DLQ จะช่วยให้เราสามารถตรวจสอบและแก้ไขปัญหาที่เกิดขึ้นกับ Event เหล่านั้นได้
SiamCafe BlogEvent Driven Architecture เป็น Paradigm ที่ทรงพลังและยืดหยุ่น เหมาะสำหรับระบบที่ต้องการ Scalability และ Decoupling อย่างไรก็ตาม EDA ก็มี Complexity ที่ต้องจัดการ ต้องเข้าใจ Concept ให้ดี เลือก Tools ให้เหมาะสม และ Implement ด้วยความระมัดระวัง
หวังว่าบทความนี้จะเป็นประโยชน์กับน้องๆ นะครับ ถ้ามีคำถามเพิ่มเติม ถามมาได้เลย!