4 min readCalm pace · scan the outline anytime

Transactions and failures

Per-Use-Case transaction ownership, sync ACL failure modes, and compensations — without distributed transactions.

Transactions and failures

Modular Hexagonal DDD does not promise a distributed two-phase commit across modules. Each Use Case owns a clear consistency boundary.

Related: ACL · Event delivery · Orchestration.

Ownership rule

RuleDetail
One Use Case, one primary write setPrefer a single Shared DatabaseTransactionInterface (or host unit of work) around this module’s writes
Peers are remoteTreat sync ACL like an external call — even when in-process today
No cross-module DB transactionDo not open one SQL transaction that writes Ordering and Warehouse tables together

Sync ACL — fail before you commit

When the publisher needs a peer answer before it is safe to commit:

Prefer: run read/validate ACL calls before opening the write transaction (or before the first irreversible side effect).

If you must call ACL inside a transaction (e.g. reserve then write):

  • Keep the peer call short.
  • On peer failure → roll back this module’s transaction.
  • Never leave “Ordering committed + Warehouse half-reserved” without a defined compensation (see below).

Failure modes

SituationExpected behaviour
Peer ACL timeout / 5xxFail the Use Case; caller retries or shows error — do not pretend success
Peer says “no” (business)Domain/Application policy: reject or alternate path — still no silent partial write
Peer succeeded, local commit failsCompensate peer if the peer call was a write (release reservation); prefer peer APIs that are confirm/cancel capable
Local commit succeeded, async consumer failsEventual consistency + retry / dead-letter (event delivery)

Compensating actions

For multi-step sync writes across modules, prefer explicit steps:

  1. Reserve (peer write ACL) → returns a reservation code
  2. Commit local aggregate referencing that code
  3. On local failure → Release (peer write ACL)
  4. On success → later Confirm via Event or second ACL

That is still ACL + Events — not a shared transaction. Longer chains → orchestration.

What not to do

Anti-patternWhy
Single DB transaction spanning two modules’ tablesHidden deploy/runtime coupling; fails the HTTP-replace mental model
Catch peer errors and commit anyway “to fix later” without an outbox/eventSilent inconsistency
UI retries that double-create without idempotency keysDuplicate aggregates

Next: orchestration · anti-patterns.

Modular Hexagonal Domain-Driven Design
Core 1.0.0-draft