Series MapLesson 32 / 50
Focus mode active/Press Alt+Shift+R to toggle/Esc to exit
Deepen PracticeOrdered learning track

Dual-Write Problem and Distributed Consistency

Dual-write problem, database write plus Kafka publish, Kafka publish plus database write, external API plus event publish, transactional outbox, CDC, saga, compensation, idempotency, reconciliation, distributed transaction avoidance, two-phase commit limitation, failure matrix, consistency trade-off, dan review checklist.

17 min read3334 words
PrevNext
Lesson 3250 lesson track28–41 Deepen Practice
#kafka#dual-write#distributed-consistency#transactional-outbox+6 more

Part 032 — Dual-Write Problem and Distributed Consistency

Fokus part ini: memahami kenapa menulis ke dua sistem berbeda adalah sumber utama data inconsistency di event-driven architecture, dan bagaimana memilih solusi yang realistis: outbox, CDC, inbox, saga, compensation, idempotency, dan reconciliation.


1. Core Mental Model

Dual-write problem terjadi ketika satu business operation harus mengubah dua resource berbeda, tetapi tidak ada atomic transaction yang benar-benar melindungi keduanya.

Contoh paling umum:

sequenceDiagram participant API as Java/JAX-RS Service participant DB as PostgreSQL participant K as Kafka API->>DB: update quote status API->>K: publish QuoteSubmitted event

Dua write tersebut tidak atomic sebagai satu unit. Jika salah satu sukses dan yang lain gagal, sistem masuk ke state yang tidak konsisten.

Dual-write bukan hanya DB + Kafka. Pola yang sama muncul pada:

  • PostgreSQL + Kafka,
  • Kafka + PostgreSQL,
  • PostgreSQL + Redis,
  • PostgreSQL + external REST API,
  • external API + Kafka,
  • Kafka + object storage,
  • database A + database B,
  • local state + workflow engine,
  • domain state + audit event.

Distributed consistency adalah disiplin mendesain agar mismatch tersebut bisa dicegah, dideteksi, atau diperbaiki.


2. Why Dual-Write Exists in Enterprise Systems

Enterprise backend jarang hanya menulis satu tempat. Dalam CPQ/order management style system, satu command dapat menyebabkan:

  • update quote state,
  • insert approval request,
  • publish integration event,
  • invalidate Redis cache,
  • call pricing/eligibility service,
  • start workflow/saga,
  • create audit record,
  • notify downstream order service,
  • update read model.

Setiap side effect memiliki failure behavior berbeda. Database bisa commit. Kafka publish bisa timeout. External system bisa return 500 setelah sebenarnya memproses request. Redis bisa expire. Consumer bisa duplicate. Workflow bisa stuck.

Karena itu, architecture yang sehat tidak bertanya “bagaimana supaya tidak pernah gagal?”, tetapi:

  • failure mana yang mungkin terjadi,
  • state mana yang menjadi source of truth,
  • side effect mana yang bisa diulang,
  • event mana yang bisa direplay,
  • mismatch mana yang bisa direconcile,
  • invariant bisnis mana yang tidak boleh dilanggar.

3. The Basic Failure Matrix

Untuk operasi:

1. Write database
2. Publish Kafka event

Failure matrix:

DB writeKafka publishHasilRisiko
gagaltidak dilakukanaman relatifcommand gagal
gagalsuksesevent palsudownstream melihat state yang tidak ada
suksesgagalmissing eventdownstream tidak tahu state berubah
suksestimeout ambigutidak jelasduplicate atau missing tergantung retry
suksessuksesidealtetap butuh duplicate-safe consumer

Timeout adalah kasus paling licin. Producer bisa timeout walaupun broker akhirnya menerima record. Retry bisa menghasilkan duplicate jika producer/idempotency tidak benar atau jika event ID berubah.


4. Database Write + Kafka Publish

Pattern naïve:

transaction.begin();
quoteRepository.updateStatus(quoteId, SUBMITTED);
kafkaProducer.send(event).get();
transaction.commit();

Masalah:

  1. Kafka publish sukses, DB commit gagal.
  2. Kafka publish timeout, DB transaction tertahan.
  3. Kafka publish lambat memperpanjang DB lock.
  4. Kafka publish retry bisa duplicate.
  5. Jika service crash setelah DB write sebelum publish, event hilang.

Pattern naïve lain:

transaction.begin();
quoteRepository.updateStatus(quoteId, SUBMITTED);
transaction.commit();
kafkaProducer.send(event).get();

Masalah:

  1. DB commit sukses, service crash sebelum publish.
  2. Publish gagal setelah state berubah.
  3. Reconciliation diperlukan untuk menemukan missing event.

Solusi utama: transactional outbox.


5. Kafka Publish + Database Write

Pattern ini kadang muncul pada consumer:

consume event
publish downstream event
write database state
commit offset

Failure matrix:

Kafka publishDB writeOffset commitRisiko
suksesgagaltidak commitduplicate downstream event saat retry
gagalsuksestidak commitDB berubah, output event hilang
suksessuksesgagalinput diproses ulang, duplicate side effect
suksessuksessuksesideal

Jika consumer menghasilkan output event dan menulis DB, Anda harus menentukan source of truth dan idempotency boundary.

Pilihan desain:

  • DB write dulu, lalu outbox untuk output event.
  • Kafka transaction untuk Kafka output + offset, tapi DB tetap idempotent/reconcile.
  • Inbox untuk dedup input event.
  • Saga state table untuk workflow.
  • Reconciliation untuk mismatch.

6. External API Call + Event Publish

External API membuat dual-write lebih sulit karena biasanya tidak ikut transaction.

Contoh:

sequenceDiagram participant S as Order Service participant P as Provisioning API participant K as Kafka S->>P: activate service P-->>S: timeout / unknown S->>K: publish ServiceActivationRequested or ServiceActivated

Masalah:

  • timeout bukan berarti gagal,
  • external system mungkin sudah memproses request,
  • retry bisa duplicate activation,
  • publish event bisa salah menggambarkan real external state,
  • compensation mungkin tidak sempurna.

Solusi bergantung use case:

  • idempotency key ke external API,
  • command ID/correlation ID,
  • status polling,
  • saga state table,
  • pending/unknown state,
  • reconciliation job,
  • compensation command,
  • manual intervention.

Jangan publish “Activated” jika status external masih unknown. Publish event yang merepresentasikan state yang benar: ActivationRequested, ActivationPending, ActivationConfirmed, ActivationFailed, atau ActivationStatusUnknown sesuai domain.


7. Transactional Outbox

Transactional outbox memindahkan publish intent ke database transaction yang sama dengan business state.

sequenceDiagram participant API as JAX-RS Service participant DB as PostgreSQL participant O as Outbox Publisher participant K as Kafka API->>DB: begin transaction API->>DB: update business row API->>DB: insert outbox row API->>DB: commit O->>DB: fetch unpublished outbox row O->>K: publish event O->>DB: mark published / advance CDC

Keuntungan:

  • business state dan event intent atomic di DB,
  • crash after commit tidak menghilangkan event intent,
  • publisher bisa retry,
  • outbox lag bisa dipantau,
  • data repair lebih jelas,
  • event ID bisa stabil.

Trade-off:

  • event tidak langsung published pada saat commit,
  • butuh publisher/CDC pipeline,
  • outbox table perlu cleanup/retention,
  • duplicate publish tetap mungkin,
  • consumer tetap harus idempotent,
  • ordering perlu dirancang.

8. CDC-Based Outbox

CDC-based outbox menggunakan Debezium atau CDC pipeline untuk membaca perubahan outbox dari WAL PostgreSQL.

flowchart LR S[Java Service] --> DB[(PostgreSQL Business Tables + Outbox)] DB --> WAL[WAL / Logical Decoding] WAL --> D[Debezium Connector] D --> K[Kafka Topic]

Kelebihan:

  • tidak perlu polling manual,
  • publish mengikuti commit order WAL,
  • mengurangi query polling ke database,
  • cocok untuk event streaming dari DB changes.

Risiko:

  • replication slot lag,
  • WAL retention membengkak,
  • connector stopped,
  • snapshot surprise,
  • schema change break,
  • tombstone/delete semantics,
  • connector offset corruption,
  • operational ownership sering lintas tim.

CDC bukan magic. Ia memindahkan problem publish ke pipeline WAL/connector yang harus diobservasi dan dioperasikan.


9. Polling Outbox

Polling outbox membaca row yang belum published.

Contoh query konseptual:

SELECT id, aggregate_id, event_type, payload, headers
FROM outbox_event
WHERE status = 'PENDING'
ORDER BY created_at, id
LIMIT 100
FOR UPDATE SKIP LOCKED;

Lalu publisher:

  1. claim rows,
  2. publish ke Kafka,
  3. mark published,
  4. retry jika gagal,
  5. move to failed jika retry exhausted.

Kelebihan:

  • lebih mudah dikontrol aplikasi,
  • tidak butuh Debezium,
  • retry state eksplisit,
  • mudah dibuat per service.

Risiko:

  • polling load,
  • locking bug,
  • publisher concurrency issue,
  • mark published failure after Kafka publish,
  • duplicate publish,
  • cleanup/retention debt.

Polling outbox tetap butuh idempotent event ID dan idempotent consumer.


10. Inbox Pattern for Consumer Side

Outbox melindungi producer side. Inbox melindungi consumer side.

sequenceDiagram participant C as Kafka Consumer participant DB as PostgreSQL C->>DB: begin transaction C->>DB: insert processed_event(event_id) C->>DB: apply business state change C->>DB: commit C->>C: commit Kafka offset after DB commit

Jika event yang sama diterima lagi:

INSERT INTO processed_event(event_id, consumer_name, processed_at)
VALUES (?, ?, now())
ON CONFLICT (event_id, consumer_name) DO NOTHING;

Jika insert tidak terjadi karena conflict, event sudah diproses. Consumer bisa skip dengan aman.

Inbox penting karena outbox/CDC/publisher retry dapat menghasilkan duplicate publish. Kafka at-least-once delivery juga dapat menghasilkan duplicate processing.


11. Saga

Saga digunakan saat satu business process melibatkan beberapa local transaction lintas service.

stateDiagram-v2 [*] --> QuoteSubmitted QuoteSubmitted --> PricingReserved PricingReserved --> ApprovalRequested ApprovalRequested --> OrderCreated OrderCreated --> FulfillmentRequested FulfillmentRequested --> Completed PricingReserved --> CompensationRequired: approval rejected OrderCreated --> CompensationRequired: fulfillment failed CompensationRequired --> Compensated

Saga bukan distributed transaction. Saga adalah koordinasi local transaction + event/command + compensation.

Saga cocok ketika:

  • proses panjang,
  • ada human approval,
  • ada external integration,
  • partial failure harus direcover,
  • state intermediate harus terlihat,
  • compensation mungkin diperlukan.

Saga tetap membutuhkan:

  • saga ID,
  • correlation ID,
  • state table,
  • idempotent command handler,
  • timeout,
  • retry policy,
  • stuck saga monitoring,
  • manual repair path.

12. Compensation

Compensation adalah aksi bisnis untuk membatalkan/mengimbangi efek sebelumnya.

Contoh:

ActionPossible compensation
reserve pricerelease price reservation
create ordercancel order
allocate resourcerelease resource
request fulfillmentsend cancellation if not completed
publish integration requestpublish compensating event

Compensation tidak selalu perfect rollback. Dalam sistem enterprise, beberapa side effect tidak bisa benar-benar dibatalkan. Misalnya notifikasi sudah terkirim, external partner sudah menerima request, atau fulfillment sudah masuk proses manual.

Karena itu, desain harus membedakan:

  • reversible step,
  • irreversible step,
  • compensatable step,
  • manual intervention step,
  • audit-only step.

13. Idempotency

Idempotency adalah kemampuan memproses command/event berulang tanpa mengubah hasil akhir secara salah.

Idempotency boundary berbeda-beda:

BoundaryIdempotency key
HTTP commandIdempotency-Key / command ID
Kafka eventevent ID
Consumer processingevent ID + consumer name
Business aggregatequote/order ID + target state/version
External APIexternal idempotency key/request ID
Outboxoutbox event ID
Sagasaga ID + step ID

Idempotency bukan hanya “cek event ID”. Untuk state machine, idempotency harus memahami valid transition.

Contoh:

Current state: SUBMITTED
Incoming event: QuoteSubmitted
Action: no-op, already applied

Tetapi:

Current state: CANCELLED
Incoming event: QuoteApproved
Action: reject / compensate / flag inconsistency

14. Reconciliation

Reconciliation adalah proses membandingkan state antar sistem dan memperbaiki mismatch.

Contoh reconciliation:

  • DB state SUBMITTED, tetapi event QuoteSubmitted tidak pernah muncul.
  • Outbox row pending terlalu lama.
  • Downstream projection count berbeda dari source of truth.
  • External provisioning status berbeda dari order state.
  • Kafka consumer lag/replay membuat read model tertinggal.

Reconciliation job biasanya:

  1. menentukan source of truth,
  2. mencari mismatch,
  3. mengklasifikasikan mismatch,
  4. membuat repair action,
  5. memastikan repair idempotent,
  6. mencatat audit trail,
  7. menghasilkan report.

Reconciliation bukan alasan untuk desain asal-asalan. Ia adalah safety net untuk realitas distributed system.


15. Distributed Transaction Avoidance

Dalam sistem microservices modern, distributed transaction sering dihindari karena:

  • coupling tinggi,
  • lock lintas service mahal,
  • failure recovery rumit,
  • coordinator menjadi bottleneck,
  • participant heterogen tidak mendukung protocol yang sama,
  • cloud-managed systems sering tidak compatible,
  • long-running business process tidak cocok dengan ACID transaction.

Alternatif yang lebih umum:

  • local transaction per service,
  • transactional outbox,
  • inbox/idempotent consumer,
  • saga,
  • compensation,
  • retry/DLQ,
  • reconciliation,
  • explicit status model,
  • audit trail.

Ini bukan berarti consistency diabaikan. Consistency dipindahkan dari “global lock” menjadi “desain lifecycle dan recovery”.


16. Two-Phase Commit Limitation

Two-phase commit mencoba membuat beberapa resource commit secara atomic melalui coordinator.

sequenceDiagram participant C as Coordinator participant DB as PostgreSQL participant K as Kafka/Resource C->>DB: prepare C->>K: prepare DB-->>C: ready K-->>C: ready C->>DB: commit C->>K: commit

Keterbatasan praktis:

  • tidak semua resource mendukung XA/2PC,
  • operational complexity tinggi,
  • blocking jika coordinator gagal,
  • latency meningkat,
  • coupling antar resource meningkat,
  • sulit untuk external REST API,
  • tidak cocok untuk long-running workflow.

Untuk Java/JAX-RS service enterprise, 2PC biasanya bukan default pilihan untuk Kafka + PostgreSQL. Outbox lebih praktis dan lebih observable.


17. Consistency Trade-Offs

Tidak semua use case butuh consistency yang sama.

Use caseConsistency concernPattern
Quote submitted eventdownstream harus tahu state committedoutbox
Read model updateboleh lag beberapa detikevent projection + lag monitoring
Fulfillment commandside effect external harus idempotentsaga + command ID
Audit trailtidak boleh hilangDB audit + outbox/audit topic
Cache invalidationstale cache tolerated terbatasevent + TTL + explicit invalidation
Pricing reservationdouble reservation berbahayaidempotency + state/version guard
Workflow approvallong-running/human stepsaga/orchestration

Senior engineer harus mendesain berdasarkan risk, bukan dogma.


18. Source of Truth Decision

Untuk setiap flow, tentukan source of truth.

Pertanyaan:

  • Apakah PostgreSQL service ini source of truth untuk quote/order state?
  • Apakah Kafka event adalah notification dari state yang sudah committed?
  • Apakah downstream projection boleh berbeda sementara?
  • Apakah external system punya authoritative state sendiri?
  • Apakah ada state yang harus direconcile dua arah?
  • Apakah event replay boleh mengubah source of truth?

Jika source of truth tidak jelas, incident akan sulit diselesaikan karena setiap tim menunjuk sistem berbeda.


19. Event Semantics and Truthfulness

Event harus jujur terhadap state.

Buruk:

OrderCompleted

padahal fulfillment baru dikirim dan belum confirmed.

Lebih jujur:

OrderFulfillmentRequested
OrderFulfillmentAccepted
OrderFulfillmentCompleted
OrderFulfillmentFailed

Dual-write sering diperparah oleh event yang terlalu optimistis. Event harus merepresentasikan fakta yang sudah benar pada boundary producer.

Rule:

  • publish fact, bukan harapan,
  • command topic untuk instruksi,
  • event topic untuk fakta,
  • status unknown harus dimodelkan eksplisit,
  • jangan menyembunyikan pending state.

20. Failure Matrix Template

Gunakan template ini saat review desain:

StepResourceOperationIf fails beforeIf fails afterDetectionRecovery
1PostgreSQLupdate quoteno state changestate committedDB error/logretry command
2PostgreSQLinsert outboxtransaction rollbackevent intent committedDB constraint/logretry command/outbox scan
3Kafkapublish eventoutbox remains pendingevent may be duplicateoutbox lag/producer metricsretry publisher/idempotent consumer
4Consumer DBapply projectionevent will retryprojection updated, offset may failconsumer lag/loginbox dedup/retry
5External APIsend commandno external side effectambiguous side effecttimeout/status pollingidempotency/reconcile

Review belum selesai sampai failure matrix punya detection dan recovery untuk setiap ambiguous state.


21. Java/JAX-RS Design Impact

Untuk endpoint command:

POST /quotes/{quoteId}/submit
Idempotency-Key: abc-123

Desain yang lebih sehat:

sequenceDiagram participant Client participant API as JAX-RS Resource participant S as Application Service participant DB as PostgreSQL participant K as Kafka via Outbox Client->>API: POST command with idempotency key API->>S: validate and execute command S->>DB: begin transaction S->>DB: insert command/idempotency record S->>DB: update quote state if valid transition S->>DB: insert outbox event S->>DB: commit API-->>Client: return accepted/current result DB-->>K: outbox polling/CDC publishes event

Hal penting:

  • resource layer tidak memegang distributed consistency,
  • service layer menentukan transaction boundary,
  • DB transaction menyimpan business state + event intent,
  • response HTTP tidak menjanjikan downstream sudah selesai,
  • idempotency key melindungi duplicate submit,
  • outbox melindungi missing event.

22. PostgreSQL/MyBatis/JDBC Design Impact

Di PostgreSQL/MyBatis/JDBC, perhatikan:

  • semua write yang harus atomic masuk transaction yang sama,
  • MyBatis mapper tidak boleh membuka connection/transaction terpisah diam-diam,
  • outbox insert harus ikut rollback jika business write rollback,
  • unique constraint untuk idempotency key,
  • unique constraint untuk event ID,
  • state transition guard di SQL atau service layer,
  • locking strategy untuk concurrent command,
  • migration outbox/inbox harus backward compatible,
  • JSONB payload schema harus versioned,
  • created_at/sequence harus mendukung ordering yang diharapkan.

Contoh constraint konseptual:

CREATE UNIQUE INDEX uq_command_idempotency
ON command_idempotency(service_name, idempotency_key);

CREATE UNIQUE INDEX uq_outbox_event_id
ON outbox_event(event_id);

23. Kafka Design Impact

Kafka side perlu menjawab:

  • Apakah event ID stabil dari outbox row?
  • Apakah partition key sesuai aggregate ID?
  • Apakah event order per aggregate dijaga?
  • Apakah producer retry aman?
  • Apakah consumer idempotent?
  • Apakah DLQ menyimpan metadata cukup untuk replay?
  • Apakah replay tidak melanggar business state?
  • Apakah event schema berevolusi kompatibel?
  • Apakah topic retention cukup untuk recovery?
  • Apakah consumer lag berdampak ke business SLA?

Kafka hanya transport/log. Correctness tetap datang dari desain end-to-end.


24. Redis Design Impact

Redis sering muncul dalam dual-write sebagai cache, lock, dedup, atau idempotency store.

Risiko:

  • DB commit sukses, cache invalidation gagal,
  • Redis idempotency key expired terlalu cepat,
  • Redis lock expired saat proses masih berjalan,
  • Redis write sukses, DB write gagal,
  • cache menyimpan state yang belum committed,
  • cache rebuild membaca projection stale.

Rule:

  • Redis bukan source of truth untuk state bisnis kritis kecuali memang dirancang demikian,
  • cache invalidation harus tolerate missing event dengan TTL/rebuild,
  • lock tidak menggantikan database constraint,
  • idempotency kritis sebaiknya punya backing durable store atau recovery strategy.

25. Kubernetes/Cloud/On-Prem Impact

Dual-write failure sering muncul saat platform event:

  • pod killed setelah DB commit sebelum publish,
  • rolling update menghentikan outbox publisher,
  • network partition ke Kafka,
  • DNS/TLS issue ke broker,
  • Debezium connector stopped,
  • replication slot lag,
  • Redis unavailable,
  • external service timeout,
  • cloud-managed Kafka throttling,
  • node disk pressure,
  • secret rotation memutus producer.

Karena itu, deployment harus mendukung:

  • graceful shutdown,
  • readiness/liveness yang benar,
  • retry bounded,
  • outbox lag alert,
  • DLQ alert,
  • connector health alert,
  • reconciliation job,
  • runbook data repair.

26. Observability for Distributed Consistency

Metrics/log/tracing yang penting:

SignalTujuan
outbox pending countmendeteksi publish backlog
outbox oldest pending agemengukur risk missing downstream update
outbox publish failure ratemendeteksi Kafka/serialization issue
consumer duplicate countmemvalidasi idempotency pressure
inbox conflict countmendeteksi replay/duplicate rate
DLQ count by event typemendeteksi poison/systematic failure
reconciliation mismatch countmendeteksi consistency drift
projection lagstale read risk
saga stuck countworkflow partial failure
external unknown status countside-effect ambiguity

Logging harus menyertakan:

  • event ID,
  • command ID,
  • idempotency key,
  • correlation ID,
  • causation ID,
  • aggregate ID,
  • saga ID,
  • topic/partition/offset,
  • outbox row ID,
  • consumer name.

27. Data Repair and Replay Safety

Repair harus idempotent dan auditable.

Jangan lakukan:

  • reset offset production tanpa memahami side effect,
  • replay topic ke consumer non-idempotent,
  • insert missing event manual tanpa event ID/correlation,
  • update DB state tanpa audit,
  • delete outbox row pending tanpa root cause,
  • republish DLQ tanpa memperbaiki poison cause.

Lakukan:

  • tentukan source of truth,
  • buat repair plan,
  • dry-run jika memungkinkan,
  • batasi scope by aggregate/time/window,
  • gunakan idempotency guard,
  • catat audit trail,
  • monitor lag/DLQ setelah repair,
  • validasi business count.

28. Common Anti-Patterns

28.1 Publish inside DB transaction

Kafka call memperpanjang DB transaction dan tidak menjamin atomicity DB+Kafka.

28.2 Publish after commit without outbox

Crash setelah commit membuat missing event.

28.3 Random event ID on retry

Duplicate tidak bisa didedup dengan stabil.

28.4 Consumer commits offset before DB write

Crash menyebabkan event hilang secara processing.

28.5 External call without idempotency key

Retry bisa menggandakan side effect.

28.6 “Exactly-once” claim without boundary

Exactly-once harus menyebut boundary. Kafka-only? DB included? External included?

28.7 No reconciliation

Sistem distributed tanpa reconciliation hanya menunggu drift menjadi incident.


29. Architecture Decision Framework

Gunakan pertanyaan ini:

  1. Apa command/event/fact yang sedang diproses?
  2. Apa source of truth?
  3. Resource apa saja yang ditulis?
  4. Apakah write harus atomic atau boleh eventually consistent?
  5. Jika partial failure, state apa yang mungkin terjadi?
  6. Apakah side effect bisa diulang?
  7. Apa idempotency key-nya?
  8. Apakah butuh outbox?
  9. Apakah butuh inbox?
  10. Apakah butuh saga/compensation?
  11. Apakah butuh reconciliation?
  12. Bagaimana mendeteksi stuck/missing/duplicate?
  13. Bagaimana repair dilakukan?
  14. Apa customer impact saat lag/failure?
  15. Apa yang harus ada di PR/ADR?

30. Dual-Write Review Checklist

Gunakan checklist ini saat review PR/ADR:

  • Apakah ada lebih dari satu resource yang ditulis?
  • Apakah source of truth eksplisit?
  • Apakah ada failure matrix?
  • Apakah DB write + Kafka publish memakai outbox?
  • Apakah outbox row dibuat dalam transaction yang sama dengan business row?
  • Apakah event ID stabil?
  • Apakah partition key sesuai aggregate/order requirement?
  • Apakah consumer idempotent?
  • Apakah inbox/processed event table tersedia untuk side effect kritis?
  • Apakah external API memakai idempotency key?
  • Apakah timeout external dimodelkan sebagai unknown/pending, bukan sukses/gagal palsu?
  • Apakah compensation tersedia untuk step yang compensatable?
  • Apakah irreversible step jelas?
  • Apakah retry bounded dan DLQ tersedia?
  • Apakah reconciliation job tersedia untuk mismatch penting?
  • Apakah observability mencakup outbox lag, DLQ, consumer lag, stuck saga?
  • Apakah repair/replay runbook ada?
  • Apakah schema/event contract compatible?
  • Apakah security/privacy impact event sudah direview?

31. Internal Verification Checklist

Cek di internal CSG/team:

  • Flow mana yang menulis PostgreSQL dan publish Kafka/RabbitMQ event?
  • Apakah semua flow penting memakai outbox atau CDC?
  • Apakah ada direct producer call dari service transaction?
  • Apakah ada event yang dipublish setelah commit tanpa outbox?
  • Apakah outbox publisher polling atau Debezium?
  • Apakah ada consumer yang menulis DB tanpa inbox/idempotency?
  • Apakah idempotency key untuk HTTP command tersedia?
  • Apakah external integration punya idempotency/correlation ID?
  • Apakah saga state disimpan eksplisit?
  • Apakah stuck saga/reconciliation dashboard tersedia?
  • Apakah DLQ replay runbook ada?
  • Apakah ada incident missing event, duplicate event, stale projection, atau inconsistent order state?
  • Apakah PR template meminta failure matrix?
  • Apakah ADR event-driven decision menyebut consistency trade-off?

32. Senior Engineer Heuristic

Gunakan prinsip ini:

Tidak ada distributed consistency gratis. Kalau satu command menyentuh dua resource, Anda harus memilih: atomic local transaction + outbox, idempotent side effect, saga/compensation, reconciliation, atau menerima risiko inconsistency secara sadar.

Desain event-driven yang matang bukan desain yang tidak pernah gagal. Desain yang matang tahu cara gagal, cara mendeteksi, cara retry, cara dedup, cara compensate, dan cara repair tanpa memperbesar kerusakan.

Lesson Recap

You just completed lesson 32 in deepen practice. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.