it

Event-Driven Architecture คืออะไร? สอนออกแบบระบบ Event Sourcing, CQRS สำหรับ Backend 2026

event driven architecture guide
Event-Driven Architecture คืออะไร? สอนออกแบบระบบ Event Sourcing, CQRS สำหรับ Backend 2026

ในโลกของ Backend Development ปี 2026 ระบบที่ต้องรองรับผู้ใช้หลายล้านคนพร้อมกันไม่สามารถพึ่งพาสถาปัตยกรรมแบบ Request-Response แบบดั้งเดิมได้อีกต่อไป Event-Driven Architecture (EDA) กลายเป็นรูปแบบสถาปัตยกรรมที่สำคัญที่สุดสำหรับระบบ Distributed Systems ขนาดใหญ่ ตั้งแต่ระบบ E-commerce ไปจนถึงระบบ Financial Trading

บทความนี้จะพาคุณเข้าใจ Event-Driven Architecture อย่างลึกซึ้ง ตั้งแต่แนวคิดพื้นฐานไปจนถึง Pattern ขั้นสูงอย่าง Event Sourcing, CQRS, Saga Pattern และ Outbox Pattern พร้อมตัวอย่างการใช้งานจริงกับ Apache Kafka และ RabbitMQ

อ่านเพิ่ม: Stream Processing คืออะไร? สอน Kafka Streams, Apache Flink ส · อ่านเพิ่ม: Database Indexing คืออะไร? สอนสร้าง Index และ Optimize Query · อ่านเพิ่ม: SQLite คืออะไร? ฐานข้อมูลที่ใช้มากที่สุดในโลก สำหรับ Embedde

Event-Driven Architecture (EDA) คืออะไร?

Event-Driven Architecture คืออะไร? สอนออกแบบระบบ Event Sourcing, CQRS สำหรับ Backend 2026

Event-Driven Architecture คือรูปแบบสถาปัตยกรรมซอฟต์แวร์ที่ใช้ "เหตุการณ์" (Events) เป็นตัวขับเคลื่อนการทำงานของระบบ แทนที่จะเรียกใช้งานบริการอื่นโดยตรงแบบ Synchronous เหมือนระบบ Request-Response ทั่วไป ระบบจะ "ปล่อย" (Emit/Publish) เหตุการณ์ออกมา แล้วบริการอื่นที่สนใจจะ "รับฟัง" (Subscribe/Consume) เหตุการณ์เหล่านั้นไปทำงานต่อ

ลองนึกภาพง่ายๆ: ในร้านอาหาร ลูกค้าสั่งอาหาร (Event) → พนักงานเสิร์ฟเขียน Order → ส่งไปห้องครัว → เชฟรับ Order ไปทำ → อาหารเสร็จ (Event) → พนักงานเสิร์ฟรับไปส่ง ทุกขั้นตอนขับเคลื่อนด้วย "เหตุการณ์" ไม่ใช่การสั่งงานโดยตรง

ความแตกต่างระหว่าง Events, Commands และ Queries

การเข้าใจความแตกต่างระหว่างสามแนวคิดนี้เป็นพื้นฐานสำคัญ:

ประเภทความหมายตัวอย่างลักษณะ
Eventสิ่งที่เกิดขึ้นแล้ว (Past tense)OrderPlaced, UserRegisteredImmutable, ย้อนไม่ได้
Commandคำสั่งให้ทำบางอย่าง (Imperative)PlaceOrder, RegisterUserอาจสำเร็จหรือล้มเหลว
Queryคำถามเพื่อดึงข้อมูลGetOrderById, ListUsersไม่เปลี่ยนแปลง State

Event จะใช้ชื่อเป็น Past Tense เสมอ เช่น OrderPlaced ไม่ใช่ PlaceOrder (นั่นคือ Command) เพราะ Event คือสิ่งที่ "เกิดขึ้นแล้ว" เป็นข้อเท็จจริง (fact) ที่ไม่สามารถเปลี่ยนแปลงได้

ประเภทของ Events

Domain Events

เหตุการณ์ที่เกิดขึ้นภายใน Bounded Context เดียวกัน เป็นเรื่องของ Business Logic ภายใน เช่น PaymentReceived, InventoryReserved ใช้ภายใน Service เดียวกันเพื่อแยก Logic ให้ชัดเจน

Integration Events

เหตุการณ์ที่ส่งข้าม Bounded Context หรือข้ามระหว่าง Microservices ใช้สำหรับการสื่อสารระหว่างบริการที่แตกต่างกัน เช่น Order Service ส่ง OrderPlaced event ไปยัง Payment Service และ Inventory Service

Best Practice: Integration Events ควรมีข้อมูลน้อยที่สุดที่จำเป็น อย่าส่งข้อมูลทั้ง Entity ไป เพราะจะสร้าง Coupling ระหว่าง Services

Event-Driven Patterns หลักที่ต้องรู้

1. Event Notification

Pattern ที่ง่ายที่สุด Service A ส่ง Event แจ้งว่ามีอะไรเกิดขึ้น โดย Event มีข้อมูลน้อยมาก (แค่ ID) Service ที่รับต้องกลับไปถาม Service A ถ้าต้องการข้อมูลเพิ่มเติม

# Event Notification - ข้อมูลน้อย

{

    "event_type": "order.placed",

    "order_id": "ORD-12345",

    "timestamp": "2026-04-08T10:00:00Z"

}

# Consumer ต้องเรียก GET /orders/ORD-12345 เพื่อดึงรายละเอียด

ข้อดีคือ Coupling ต่ำมาก แต่ข้อเสียคือต้องเรียก API กลับไปหา Producer ซึ่งสร้าง Load เพิ่มเติม

2. Event-Carried State Transfer

Event พกข้อมูลทั้งหมดที่ Consumer ต้องการมาด้วย ไม่ต้องเรียก API กลับไป ทำให้ Consumer สามารถเก็บ Local Copy ของข้อมูลไว้ได้

# Event-Carried State Transfer - ข้อมูลครบ

{

    "event_type": "order.placed",

    "order_id": "ORD-12345",

    "customer_id": "CUST-789",

    "customer_name": "สมชาย ใจดี",

    "customer_email": "somchai@example.com",

    "items": [

        {"product_id": "P001", "name": "Laptop", "qty": 1, "price": 35000},

        {"product_id": "P002", "name": "Mouse", "qty": 2, "price": 500}

    ],

    "total_amount": 36000,

    "shipping_address": "123 กรุงเทพฯ",

    "timestamp": "2026-04-08T10:00:00Z"

}

ข้อดีคือ Consumer ไม่ต้องเรียก API กลับ ลด latency และ coupling แต่ข้อเสียคือ Event มีขนาดใหญ่ และอาจมี Stale Data ได้

3. Event Sourcing

แทนที่จะเก็บสถานะปัจจุบัน (Current State) ในฐานข้อมูล Event Sourcing เก็บ "ทุกเหตุการณ์" ที่เกิดขึ้นตามลำดับ แล้ว Replay events เหล่านั้นเพื่อสร้าง Current State ขึ้นมาใหม่ได้ทุกเมื่อ

4. CQRS (Command Query Responsibility Segregation)

แยก Model สำหรับการเขียน (Command) และการอ่าน (Query) ออกจากกัน ทำให้สามารถ Optimize แต่ละด้านได้อย่างอิสระ

Event Sourcing Deep Dive

Event Sourcing เป็น Pattern ที่ทรงพลังที่สุดใน EDA แนวคิดคือ "อย่าเก็บแค่ผลลัพธ์ จงเก็บทุกอย่างที่เกิดขึ้น" เหมือนบัญชีธนาคารที่ไม่ได้เก็บแค่ยอดเงินคงเหลือ แต่เก็บทุก Transaction ที่เคยเกิดขึ้น

เนื้อหาเกี่ยวข้อง — บทความที่เกี่ยวข้อง: GCP Cloud Spanner Open Source Contribution

Event Store

Event Store คือฐานข้อมูลสำหรับเก็บ Events ทั้งหมด โดยมีคุณสมบัติสำคัญคือ Append-Only (เพิ่มได้อย่างเดียว ลบไม่ได้ แก้ไขไม่ได้) และเรียงตามลำดับเวลา

# โครงสร้าง Event Store Table

CREATE TABLE event_store (

    event_id        UUID PRIMARY KEY,

    aggregate_id    UUID NOT NULL,          -- เช่น order_id

    aggregate_type  VARCHAR(100) NOT NULL,  -- เช่น 'Order'

    event_type      VARCHAR(100) NOT NULL,  -- เช่น 'OrderPlaced'

    event_data      JSONB NOT NULL,         -- ข้อมูล Event

    metadata        JSONB,                  -- correlation_id, user_id

    version         INT NOT NULL,           -- ลำดับของ Event ใน Aggregate

    created_at      TIMESTAMP DEFAULT NOW(),



    UNIQUE(aggregate_id, version)           -- ป้องกัน Concurrent writes

);



-- Index สำหรับ query ตาม aggregate

CREATE INDEX idx_event_store_aggregate

    ON event_store(aggregate_id, version);
# ตัวอย่าง Events ของ Order Aggregate

# Version 1: สร้าง Order

{

    "event_type": "OrderCreated",

    "aggregate_id": "ORD-001",

    "version": 1,

    "event_data": {

        "customer_id": "CUST-789",

        "items": [{"product": "Laptop", "qty": 1, "price": 35000}]

    }

}



# Version 2: เพิ่มสินค้า

{

    "event_type": "ItemAdded",

    "aggregate_id": "ORD-001",

    "version": 2,

    "event_data": {

        "product": "Mouse",

        "qty": 2,

        "price": 500

    }

}



# Version 3: ยืนยัน Order

{

    "event_type": "OrderConfirmed",

    "aggregate_id": "ORD-001",

    "version": 3,

    "event_data": {

        "confirmed_by": "CUST-789",

        "total_amount": 36000

    }

}



# Version 4: ชำระเงิน

{

    "event_type": "PaymentReceived",

    "aggregate_id": "ORD-001",

    "version": 4,

    "event_data": {

        "payment_method": "credit_card",

        "amount": 36000,

        "transaction_id": "TXN-456"

    }

}

Projections (Read Models)

เนื่องจาก Event Store เก็บ Events แต่ไม่สะดวกสำหรับการ Query ข้อมูลจึงต้องมี Projections คือกระบวนการอ่าน Events แล้วสร้าง Read Model (View) ขึ้นมาในรูปแบบที่เหมาะสำหรับการ Query

Snapshots

เมื่อ Aggregate มี Events จำนวนมาก (เช่น บัญชีธนาคารที่มีหลายพัน Transactions) การ Replay ทุก Event จะช้ามาก Snapshots คือการเก็บสถานะ ณ จุดใดจุดหนึ่ง เพื่อไม่ต้อง Replay ตั้งแต่ต้น

เมื่อไหร่ควรใช้ Snapshots: เมื่อ Aggregate มีมากกว่า 50-100 Events ถ้าน้อยกว่านี้ Replay ทั้งหมดก็เร็วพอ อย่า Optimize ก่อนเวลาอันควร

CQRS Pattern (Command Query Responsibility Segregation)

CQRS คือการแยก Model สำหรับเขียนและอ่านออกจากกัน ในระบบทั่วไปเราใช้ Model เดียวกันทั้งอ่านและเขียน แต่ใน CQRS เราแยกเป็น Command Model (Write) และ Query Model (Read) โดยแต่ละด้านสามารถ Optimize ได้อย่างอิสระ

ทำไมต้อง CQRS?

  • Read/Write Ratio: ระบบส่วนใหญ่อ่านมากกว่าเขียน 10-100 เท่า การแยก Model ทำให้ Scale ด้านอ่านได้ง่าย
  • Optimization: Write Model optimize สำหรับ consistency, Read Model optimize สำหรับ query performance
  • Complexity: Write side มี Business Logic ซับซ้อน แต่ Read side ต้องการแค่ Data ที่พร้อมแสดง
  • Scaling: Scale Read replicas แยกจาก Write database ได้

การ Implement EDA ด้วย Kafka และ RabbitMQ

Apache Kafka

Kafka เหมาะสำหรับ High-throughput, Event Streaming ที่ต้องการเก็บ Events ระยะยาว รองรับหลายล้าน Events ต่อวินาที

แนะนำเพิ่มเติม — สัญญาณเทรดรายวัน XM Signal

RabbitMQ

Event-Driven Architecture คืออะไร? สอนออกแบบระบบ Event Sourcing, CQRS สำหรับ Backend 2026

RabbitMQ เหมาะสำหรับ Task Queue และ Routing ที่ซับซ้อน รองรับหลาย Exchange types (Direct, Fanout, Topic, Headers)

Kafka vs RabbitMQ เลือกอันไหน?

เกณฑ์Apache KafkaRabbitMQ
รูปแบบDistributed Log (Pull)Message Queue (Push)
Throughputสูงมาก (ล้าน msg/sec)ปานกลาง (หมื่น msg/sec)
Message Retentionเก็บถาวรได้ลบหลัง Consume
Orderingภายใน Partitionภายใน Queue
Replayได้ (offset reset)ไม่ได้
RoutingTopic + PartitionExchange + Routing Key (ยืดหยุ่นกว่า)
เหมาะกับEvent Streaming, Log AggregationTask Queue, Complex Routing
กฎง่ายๆ: ถ้าต้อง Replay Events หรือรองรับ Throughput สูงมาก เลือก Kafka ถ้าต้องการ Routing ที่ซับซ้อนและ Priority Queue เลือก RabbitMQ ในปี 2026 หลายทีมใช้ทั้งสองร่วมกัน

Saga Pattern สำหรับ Distributed Transactions

ในระบบ Microservices ไม่สามารถใช้ Database Transaction แบบเดิมข้ามหลาย Services ได้ Saga Pattern เป็นวิธีจัดการ Distributed Transaction โดยแบ่งเป็นหลายขั้นตอน (Steps) และมี Compensating Transaction สำหรับ Rollback แต่ละขั้นตอน

Choreography Saga

แต่ละ Service ตัดสินใจเองว่าจะทำอะไรต่อ โดย Listen Events จาก Services อื่น ไม่มีตัวกลางควบคุม

Choreography Saga Flow - สั่งซื้อสินค้า

Order Service → OrderPlaced event

Payment Service → PaymentProcessed event (หรือ PaymentFailed)

Inventory Service → InventoryReserved event (หรือ InventoryFailed)

เนื้อหาเกี่ยวข้อง — แนะนำให้อ่าน Ansible Vault Container Orchestration

Shipping Service → ShipmentCreated event

ถ้า Payment ล้มเหลว:

Payment Service → PaymentFailed event

Order Service → ยกเลิก Order (Compensating Transaction)

ถ้า Inventory ล้มเหลว:

Inventory Service → InventoryFailed event

Payment Service → Refund (Compensating Transaction)

Order Service → ยกเลิก Order (Compensating Transaction)

แนะนำเพิ่มเติม — คอร์สเทรด Forex ที่ iCafeForex

Orchestration Saga

มี Saga Orchestrator เป็นตัวกลางควบคุมทุกขั้นตอน รู้ว่าต้องทำอะไรต่อ และ Rollback อย่างไรเมื่อเกิดความผิดพลาด

Choreography vs Orchestration

เกณฑ์ChoreographyOrchestration
Couplingต่ำ (Decentralized)สูงกว่า (Centralized)
Complexityซับซ้อนเมื่อ Services เยอะจัดการง่ายกว่า
Visibilityยากที่จะเห็นภาพรวม Flowเห็น Flow ชัดเจนใน Orchestrator
Single Point of Failureไม่มีOrchestrator เป็น SPOF
Testingยากกว่า (ต้อง Test Integration)ง่ายกว่า (Test Orchestrator)
เหมาะกับ2-4 Services, Flow ง่าย4+ Services, Flow ซับซ้อน

Outbox Pattern

ปัญหาสำคัญของ EDA คือ Dual Write Problem: เมื่อ Service ต้องทั้งบันทึกลง Database และ Publish Event ไป Message Broker พร้อมกัน ถ้าอย่างใดอย่างหนึ่งล้มเหลว ข้อมูลจะไม่สอดคล้องกัน

Outbox Pattern แก้ปัญหานี้โดยเขียน Event ลง Outbox Table ใน Database เดียวกันกับ Business Data ภายใน Transaction เดียวกัน จากนั้นมี Background Process (Relay/Poller) อ่าน Outbox แล้ว Publish ไป Message Broker

Alternative: Change Data Capture (CDC) ใช้ Debezium อ่าน Database WAL/Binlog โดยตรง แล้ว Publish Events ไป Kafka โดยไม่ต้องเขียน Outbox Relay เอง เป็นวิธีที่นิยมมากในปี 2026

Idempotency — ประมวลผลซ้ำได้อย่างปลอดภัย

ในระบบ Event-Driven ที่ใช้ at-least-once delivery อาจได้รับ Event เดียวกันมากกว่าหนึ่งครั้ง ทุก Consumer ต้องเป็น Idempotent คือประมวลผลซ้ำกี่ครั้งก็ได้ผลลัพธ์เหมือนเดิม

Eventual Consistency

ระบบ Event-Driven ไม่มี Strong Consistency แบบ Database Transaction เดี่ยว แต่ใช้ Eventual Consistency คือข้อมูลจะ "สอดคล้องกันในที่สุด" แม้ว่าในช่วงเวลาสั้นๆ อาจมีความไม่สอดคล้อง

ตัวอย่าง: เมื่อลูกค้าสั่งซื้อสินค้า Order Service บันทึก Order แล้ว แต่ Inventory Service อาจยังไม่ได้อัปเดตสต็อก ในช่วงเวลาสั้นๆ นี้ ข้อมูลไม่สอดคล้อง แต่เมื่อ Event ถูกประมวลผลเสร็จ ข้อมูลจะสอดคล้องกัน

วิธีจัดการ Eventual Consistency

  • UI/UX: แสดงสถานะ "กำลังดำเนินการ" แทน "สำเร็จ" ทันที ให้ผู้ใช้เข้าใจว่าต้องรอ
  • Retry + Dead Letter Queue: ถ้า Event ประมวลผลไม่สำเร็จ ส่งเข้า DLQ เพื่อตรวจสอบภายหลัง
  • Compensating Transactions: เมื่อพบว่าข้อมูลไม่สอดคล้อง ทำ Compensation เพื่อแก้ไข
  • Reconciliation: มี Background Job ตรวจสอบความสอดคล้องของข้อมูลเป็นระยะ

Event Schema Evolution

เมื่อระบบพัฒนาไป Event Schema จะต้องเปลี่ยนแปลง ต้องจัดการ Versioning ให้ดีเพื่อไม่ให้ Consumer เก่าพัง

เนื้อหาเกี่ยวข้อง — ทำความเข้าใจ Apache Arrow Progressive Delivery

Schema Evolution Strategies

1. Backward Compatible Changes (ปลอดภัย)

  • เพิ่ม Field ใหม่ (Optional)
  • เพิ่ม Default value

Version 1

{"event_type": "order.placed", "order_id": "ORD-001", "amount": 1000}

Version 2 (เพิ่ม currency field - backward compatible)

{"event_type": "order.placed", "order_id": "ORD-001", "amount": 1000, "currency": "THB"}

Consumer เก่าที่ไม่รู้จัก "currency" จะข้ามไป ไม่พัง

2. Breaking Changes (ต้องจัดการ)

  • ลบ Field
  • เปลี่ยน Type
  • เปลี่ยนความหมาย

ใช้ Event Type Versioning

{"event_type": "order.placed.v2", "version": 2, ...}

หรือใช้ Schema Registry (Avro/Protobuf)

Confluent Schema Registry จัดการ compatibility check อัตโนมัติ

Event-Driven Microservices Architecture

ในระบบ Microservices ขนาดใหญ่ EDA เป็นหัวใจสำคัญ แต่ละ Service สื่อสารกันผ่าน Events ทำให้ Loosely Coupled และ Scale ได้อย่างอิสระ

ตัวอย่าง E-commerce Event-Driven Architecture

[API Gateway]

เนื้อหาเกี่ยวข้อง — Terraform State Capacity Planning

|

[Order Service] --publish--> [Kafka: orders.events]

| |

| +---------+---------+----------+

| | | | |

[Payment Svc] [Inventory] [Notification] [Analytics]

| | | |

payment.events inventory. notification. (consume only)

| events events

|

[Shipping Svc]

เครื่องมือสำหรับ Event-Driven Architecture

เครื่องมือประเภทจุดเด่น
Apache KafkaEvent StreamingHigh throughput, durability, replay
RabbitMQMessage BrokerFlexible routing, priority queue
EventStoreDBEvent StorePurpose-built สำหรับ Event Sourcing
Axon FrameworkCQRS/ES FrameworkJava/Kotlin, built-in Saga support
DebeziumCDCCapture DB changes เป็น Events
Apache PulsarEvent StreamingMulti-tenancy, geo-replication
AWS EventBridgeServerless Event BusManaged, schema registry
NATSMessage SystemLightweight, low latency

Debugging และ Observability

ระบบ Event-Driven ยากต่อการ Debug เพราะ Flow กระจายไปหลาย Services สิ่งที่จำเป็น:

  • Correlation ID: ทุก Event ต้องมี Correlation ID เดียวกันตลอด Flow เพื่อ Trace ได้ว่า Request หนึ่งผ่านอะไรบ้าง
  • Distributed Tracing: ใช้ OpenTelemetry + Jaeger/Zipkin เพื่อดู Flow ทั้งหมดข้าม Services
  • Event Catalog: เอกสารรวบรวม Events ทั้งหมดในระบบ ใครเป็น Producer ใครเป็น Consumer
  • Dead Letter Queue: Events ที่ประมวลผลไม่สำเร็จต้องไปอยู่ใน DLQ เพื่อตรวจสอบ
  • Metrics: วัด Event processing latency, throughput, error rate, consumer lag

เมื่อไหร่ควรใช้ EDA vs Request-Response?

ใช้ EDA เมื่อใช้ Request-Response เมื่อ
ต้องการ Loose Coupling ระหว่าง Servicesต้องการ Response ทันที
Throughput สูง ต้องรับ Load มากๆFlow ง่ายๆ 2-3 Services
ต้องการ Audit Trail / Event Historyต้องการ Strong Consistency
หลาย Services ต้องรับรู้เหตุการณ์เดียวกันทีมเล็ก ระบบไม่ซับซ้อน
ต้อง Scale แต่ละ Service แยกกันต้องการ Simple debugging
รองรับ Eventual Consistency ได้ต้องการ Transactional guarantee
คำแนะนำสำหรับผู้เริ่มต้น: อย่าใช้ EDA ทุกที่ เริ่มจาก Request-Response ก่อน แล้วค่อยแยกส่วนที่เหมาะสมออกมาเป็น Event-Driven เมื่อระบบเติบโต Over-engineering ด้วย EDA ตั้งแต่ต้นจะสร้างความซับซ้อนที่ไม่จำเป็น

สรุป

Event-Driven Architecture เป็นสถาปัตยกรรมที่ทรงพลังสำหรับระบบ Distributed Systems ในปี 2026 การเข้าใจ Patterns ต่างๆ อย่าง Event Sourcing, CQRS, Saga Pattern และ Outbox Pattern จะช่วยให้คุณออกแบบระบบที่ Scale ได้ มีความยืดหยุ่น และจัดการ Failure ได้ดี

สิ่งสำคัญคือต้องเลือกใช้ Pattern ที่เหมาะสมกับปัญหา ไม่ใช่ใช้ทุก Pattern ทุกที่ เริ่มจากสิ่งง่ายๆ แล้วค่อยเพิ่มความซับซ้อนเมื่อจำเป็น Event-Driven Architecture ไม่ได้เหมาะกับทุกระบบ แต่เมื่อเหมาะสมแล้ว มันจะเป็นหัวใจสำคัญที่ทำให้ระบบของคุณรองรับการเติบโตได้อย่างยั่งยืน

XM Legend · เทรดเดอร์ & ผู้สอน Forex 13 ปี

ผู้ก่อตั้ง SiamCafe ตั้งแต่ปี 1997 · เทรดเดอร์สาย Forex มากกว่า 13 ปี ได้รับการยกย่องเป็น XM Legend · แบ่งปันความรู้ Forex, ไอที, AI และการเทรด จากประสบการณ์จริงในตลาดจริง