داده، SQL، قراردادها و ماشین حالت · فصل ۶ — Event، Outbox، Reconciliation و State Machine · 76
Outbox, Durable Intent و Reconciliation
دانش فعلی دربارهی «Outbox, Durable Intent و Reconciliation» ممکن است ابزارمحور باشد و failure/recovery boundary را به evidence قابل بازبینی تبدیل نکند. میتواند «Outbox, Durable Intent و Reconciliation» را برای یک domain محدود مدل کند و constraint، compatibility یا recovery evidence لازم را مشخص کند.
خلاصهٔ درس
دانش فعلی دربارهی «Outbox, Durable Intent و Reconciliation» ممکن است ابزارمحور باشد و failure/recovery boundary را به evidence قابل بازبینی تبدیل نکند.
میتواند «Outbox, Durable Intent و Reconciliation» را برای یک domain محدود مدل کند و constraint، compatibility یا recovery evidence لازم را مشخص کند.
خروجی این درس
میتواند «Outbox, Durable Intent و Reconciliation» را برای یک domain محدود مدل کند و constraint، compatibility یا recovery evidence لازم را مشخص کند.
این درس فقط یک پرسش محدود را مالک است: یک سرویس چگونه تغییر domain و publish intent را در یک مرز محلی durable کند، retry رله را تحمل کند و کارهای گیرکرده را قابل کشف نگه دارد، بدون اینکه exactly-once سراسری ادعا کند؟ artifact شواهدی درس Durable Outbox Reconciliation Record v1 است و فقط مرز محلی و مشاهدههای fixture مصنوعی را پوشش میدهد.
1. Outcome محدود درس
در Lab، سرویس سفارش O-1001 را تأیید میکند و قصد انتشار OrderConfirmed دارد. domain mutation، outbox intent، event identity، relay attempt، publish observation، mark state، age، reconciliation decision و residual consumer risk باید جدا و قابل بازرسی باشند.
2. Dual-write gap
اگر طراحی مستقیم بهشکل commit database -> publish message باشد، یک failure window داریم. ممکن است commit دیتابیس موفق شود ولی publish بیرونی شکست بخورد. در این حالت state میگوید سفارش تأیید شده، اما هیچ رکورد durable محلی نداریم که نشان دهد انتشار هنوز بدهکار است. Lab این وضعیت را DUAL_WRITE_GAP_OBSERVED طبقهبندی میکند.
3. Durable intent
در transactional outbox، publish intent داخل همان local transaction قرار میگیرد که domain mutation را ثبت میکند. در fixture محدود، state مربوط به OrderConfirmed و یک outbox row با وضعیت PENDING با هم commit میشوند. اگر transaction قبل از commit شکست بخورد، هیچکدام durable نمیشوند.
4. Local atomicity با broker atomicity یکی نیست
transaction دیتابیس محلی به message broker کشیده نمیشود. موفقیت commit فقط نشان میدهد domain change و outbox intent در local store ثبت شدهاند. این موفقیت ثابت نمیکند broker رویداد را دریافت کرده است.
5. هویت رویداد Outbox
outbox row یک event_id پایدار و یک aggregate_key دارد. در fixture، event ID برابر evt-order-O-1001-confirmed و aggregate key برابر order:O-1001 است. این هویت برای traceability و duplicate handling مفید است، اما UUID بهتنهایی ordering یا business uniqueness را تعریف نمیکند.
6. رفتار Relay
relay یک intent با وضعیت PENDING را میخواند، event را publish میکند و سپس row را mark میکند. publish و mark یک عملیات واحد نیستند. بنابراین طراحی باید retry را صریح بپذیرد.
7. Crash بعد از Publish و قبل از Mark
failure اصلی Lab این است که publish مصنوعی انجام شود اما قبل از PUBLISHED شدن row، crash رخ دهد. بعد از restart هنوز row PENDING است و relay همان intent را دوباره publish میکند. classification مورد انتظار DUPLICATE_DELIVERY_CANDIDATE است.
8. چرا Duplicate delivery هنوز ممکن است
Outbox یک dual-write gap را حذف میکند، اما exactly-once سراسری نمیسازد. retry میتواند همان event منطقی را بیش از یکبار قابل مشاهده کند. پس downstream idempotency همچنان یک نیاز جداست و مرز آن از prerequisite یعنی DAT-017 میآید.
9. Mark state و شواهد Retry
برای هر relay attempt باید attempt number، publish observation و local mark state ثبت شوند. اگر publish انجام شده ولی mark بهعلت crash ثبت نشده، صرف بازگشت موفق یک publish call دلیل کافی برای complete دانستن intent نیست.
10. Reconciliation
durable intent چیزی باقی میگذارد که بعداً قابل جستوجو است. reconciliation query میتواند rowهای pending، پرتکرار در failure یا قدیمیتر از threshold را پیدا کند. Lab یک intent قدیمی را عمداً باقی میگذارد و آن را STUCK_INTENT_DETECTED طبقهبندی میکند.
11. Reconciliation همهچیز را اثبات نمیکند
خالی بودن گزارش reconciliation فقط یعنی query محلی تعریفشده موردی پیدا نکرده است. این گزارش ثابت نمیکند همه downstream consumerها side effect خود را انجام دادهاند یا هیچ پیام بیرونی خارج از مرز مدلشده از بین نرفته است.
12. کامل بودن Evidence
رکورد شواهد ده فیلد دارد: Intent ID، Aggregate Key، Domain Commit، Outbox Commit، Relay Attempt، Publish Observation، Mark State، Age، Reconciliation Decision و Residual Consumer Risk. حذف Residual Consumer Risk باید EVIDENCE_INCOMPLETE تولید کند.
13. Baseline probe
در baseline_transactional_outbox_commit سفارش و intent داخل یک transaction محلی ثبت میشوند. classification مورد انتظار DOMAIN_AND_INTENT_COMMITTED است. self-test همچنین failure قبل از commit را بررسی میکند تا روشن شود domain state و outbox هر دو rollback میشوند.
14. Probe مربوط به Direct dual write
در direct_dual_write_publish_failure عمداً طراحی شکسته اجرا میشود: order state commit میشود، publish شکست میخورد و outbox intent durable وجود ندارد. classification مورد انتظار DUAL_WRITE_GAP_OBSERVED است. این negative evidence بعد از repair نباید حذف شود.
15. Probe مربوط به Crash و Retry Relay
در relay_publish_before_mark_crash_retry یک publish انجام میشود، قبل از mark crash رخ میدهد و سپس همان intent هنوز pending دوباره اجرا میشود. event_id یکسان دو بار در synthetic delivery log دیده میشود و در نهایت row PUBLISHED میشود. classification مورد انتظار DUPLICATE_DELIVERY_CANDIDATE است.
16. Probe مربوط به Reconciliation
در reconciliation_old_pending_intent یک pending row از threshold محلی پیرتر میشود و چند attempt ثبت میکند. reconciliation باید آن را surfacing کند و نباید خودکار موفق اعلامش کند. classification مورد انتظار STUCK_INTENT_DETECTED است.
17. مرز منبع و Freshness
مرز frozen دقیقاً سه منبع رسمی دارد: Debezium Outbox Event Router، PostgreSQL 18 Transactions و Azure Idempotent Consumer. snapshot در 2026-09-02 گرفته شده و اولین review signal در 2026-10-17 و بعدی در 2026-12-31 است. در 2026-09-16 هنوز review_due نرسیده؛ بنابراین Query 1 همان frozen source boundary را بدون scope expansion و بدون ادعای recheck جدید حفظ میکند.
18. Transfer و تصمیم محدود نهایی
در changed context، یک billing service نمیتواند transaction مشترکی با message broker داشته باشد. باید durable-intent record، relay retry rule و reconciliation signal را طراحی کنی و residual consumer risk را صریح بنویسی، بدون ادعای exactly-once. fixture محلی میتواند dual-write gap، local atomic durable intent، duplicate candidate ناشی از publish-before-mark و شناسایی stuck intent را نشان دهد؛ اما broker-wide exactly-once، downstream idempotency، total ordering یا production reliability را اثبات نمیکند.
ارزیابی
پاسخنامهی این درس در بستهی آموزش وجود ندارد؛ خودت جواب بده و صادقانه علامت بزن. «نیاز به مرور» درس را به صف تکرار میفرستد.
DAT-018-S1 retrieval
State the local durable-intent rule and one non-goal.
DAT-018-S2 application
Run the transactional-outbox baseline and record domain/outbox evidence.
DAT-018-S3 diagnosis
Explain the direct dual-write failure and publish-before-mark retry without claiming exactly-once.
DAT-018-S4 decision
Define the reconciliation threshold/action boundary and residual consumer risk.
DAT-018-S5 transfer
Design durable intent, relay retry, and reconciliation for the billing-service changed context.
ملاکهای ارزیابی (rubric)
Critical dimensions: bounded model correctness; evidence quality; scope discipline; transfer independence. Happy-path-only proof or an exactly-once claim is insufficient.
منابع و تازگی
مطالعهٔ موارد (case studies)
تصاویر درس
فایلهای آزمایشگاه
رسانه و اسلایدهای درس
فایلهای آموزشی (دانلود)