Skip to content

ADR-0002. Event-driven posting; record finance at payment, not checkout

FieldValue
StatusAccepted
Date2026-04-28
Decidersfinance-team, sale-team
Supersedes

Context

  • Finance must not be on the critical path of POS checkout — a finance outage cannot block taking money.
  • Revenue is only real when money actually settles. Recording at checkout (before payment) would book income for orders that are later abandoned or fail payment.
  • COGS and inventory-asset value are owned by @nx/inventory, which knows cost basis only after it issues stock — finance cannot compute it.
  • The chosen payment account/category is a customer-time decision carried on the sale payment payload (payment.attempt.finance), not known at checkout.

Decision

We will make finance react to Kafka events, never call it synchronously from checkout. Finance subscribes to five topics and posts vouchers as side effects:

Inbound eventVoucher
PAYMENT_SUCCESS (sale)RECEIPT against the account on attempt.finance.source.id
PURCHASE_ORDER_RECEIVED (inventory)PAYMENT to vendor (+ inventory asset leg)
INVENTORY_ISSUED_FOR_SALE (inventory)ADJUSTMENT — DEBIT COGS / CREDIT INVENTORY
INVENTORY_ADJUSTED (inventory)single-line ADJUSTMENT on INVENTORY
MERCHANT CDC (commerce)reconcile default + control accounts

Income is booked on PAYMENT_SUCCESS, not at checkout. If the payment payload carries no chosen account (attempt.finance.source.id), finance INFO-skips rather than guessing.

Consequences

ProsCons
Finance outage never blocks checkoutPostings are eventually-consistent, not synchronous
Income reflects settled money, not intentRequires idempotency for at-least-once delivery (see ADR-0003)
COGS/asset posting uses inventory's authoritative cost basisFinance depends on upstream payload completeness (account id)
Clean separation of concerns across servicesDebugging spans multiple services + the broker

Alternatives Considered

OptionProsConsWhy rejected
Synchronous HTTP from sale checkoutSimple, immediateCouples checkout latency/availability to financeViolates "finance off the critical path"
Record at checkout (intent)Earliest signalBooks revenue for unpaid/abandoned ordersWrong accounting
Finance computes COGS itselfFewer eventsFinance would duplicate inventory costing logicCost basis is inventory's domain

References

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.