บทที่ 22 · Part 4 — Model Behavior in Code

Domain Events without Event Hype

แยก business fact, internal Domain Event, Integration Event, audit record และ Event Sourcing ออกจากกัน

Domain Events without Event Hype

ทีมหนึ่ง publish event ทุกครั้งที่ column เปลี่ยนและเรียกระบบว่า event-driven แต่ consumers ต้องรู้ database semantics และเรียง events เอง อีกทีมใช้ EventStorming แล้วสรุปว่าต้อง Event Sourcing Domain Event ใน DDD คือ fact ที่มีความหมายต่อ domain ไม่ใช่ชื่อเดียวกับ broker message หรือ source of record

จบบทนี้คุณจะ

แยก Domain Event, Integration Event, Event Notification, audit record และ Event Sourcing ออกแบบ event จาก business meaning/authority รู้ว่า internal event อาจไม่ออกนอก process และแปล public contract หลัง transaction อย่างปลอดภัย

Domain Event

Domain Event ของ Martin Fowler อธิบายสิ่งที่ เกิดและมีความหมายต่อ domain:

type TransferIntentConfirmed struct {
	IntentID string
	Amount   Money
	Terms    TermsVersion
	At       time.Time
}

ชื่อ past tense และเป็น fact ที่ model รับรอง ไม่ใช่ TransferRowUpdated

Domain Event ใช้ภายใน Aggregate/application เพื่อ decouple reactions ได้โดยไม่ต้องมี broker

Integration Event

Integration Event เป็น Published Language ที่ contexts อื่นพึ่งพา ต้องมี:

  • producer authority
  • stable schema/identity/causation
  • minimum necessary data/privacy
  • version/deprecation
  • delivery/duplicate/order expectations
  • consumer recovery semantics

อย่า serialize internal Domain Event ตรง ๆ เพราะ model ภายในต้อง evolve และ data อาจรั่ว

5 สิ่งที่มักเรียก Event

สิ่งPurpose
Domain Eventbusiness fact ภายใน model/context
Integration Eventpublic fact contract ข้าม context
Event Notificationบอกให้ consumer ไป query รายละเอียด
Audit Recordevidence ว่าใครทำอะไรภายใต้ control
Event-sourced Eventsource-of-record transition ที่ต้อง replay/evolve

Fowler: What do you mean by Event-Driven? เตือนว่าคำ event-driven ครอบหลาย patterns ที่ trade-off ต่างกัน

Event Authority

TransferCompleted เป็นชื่ออันตรายถ้าไม่มี context qualifier Payment Execution อาจ publish ExecutionOutcomeEstablished ส่วน Ledger publish EntriesPosted และ Customer Journey ตัดสิน เมื่อใดจะแสดง complete จาก facts เหล่านั้นตาม policy

Event ไม่โอน Authority โดยอัตโนมัติ

Consumer รับ fact ตาม contract แต่ต้องตัดสิน rule ของ context ตนเอง Portal ห้ามใช้ event/cache อนุมัติเงินหาก authority อยู่ Funds Context

Transaction และ Outbox

ถ้า local state save สำเร็จแต่ publish ล้ม event หาย Transactional Outbox บันทึก state+outbox ใน transaction เดียวแล้ว relay ภายหลัง Relay อาจส่งซ้ำ consumer จึง idempotent

Internal Domain Event อาจถูกแปลงเป็น Integration Event หลัง commit โดย application/translator

Ordering และ Time

อย่าพึ่ง global order ใช้ aggregate/entity version, occurredAt, producer sequence, causation/correlation ตาม need Callback จาก provider อาจไม่เรียง ให้ consumer query authoritative state หรือรับ transitions แบบ monotonic ตาม contract

EventStorming ไม่เท่ากับ Event Sourcing

Orange notes เป็น discovery grammar หลาย event ไม่ต้องถูก persisted/published Event Sourcing เลือกเมื่อ history/reconstruction/temporal value คุ้ม operational cost ซึ่งเรียนบท 27

แบบฝึกปฏิบัติ: Event Classification

จำแนก:

Transfer Confirmed
Risk Decision Stored
Provider Callback Received
Execution Outcome Established
Ledger Row Inserted
Entries Posted
Customer Screen Refreshed
Reconciliation Case Closed

ระบุ domain/integration/audit/technical, authority, public name, data, ordering, duplicate handling และเหตุผลที่บางอันไม่ publish

รายการตรวจสอบ

  • Event เป็น business fact past tense
  • Producer มี authority ต่อ fact
  • Internal กับ integration models แยก
  • Contract มี identity/version/delivery semantics
  • Audit/log/event sourcing ไม่ถูกปน
  • Outbox/consumer duplicate ถูกออกแบบเมื่อข้าม transaction
  • EventStorming ไม่บังคับ broker/Event Sourcing

สรุปบทนี้

Domain Event ทำให้สิ่งที่ domain สนใจเป็น first-class fact แต่การเผยแพร่ข้าม context เพิ่ม contract, delivery และ governance Event-driven vocabulary ต้องระบุความหมายให้ชัด มิฉะนั้น row changes จะ กลายเป็น distributed coupling รูปแบบใหม่

อ่านเพิ่มเติม