In a monolithic architecture, a transaction is a single, atomic database operation. It either happens in full, or it doesn't happen at all. It’s safe, but it can become a bottleneck when millions of users are trying to process transactions simultaneously. As we scale into distributed systems, we trade that localized safety for a "relay race" across multiple services.
To prevent this relay race from ending in a catastrophe, we must move beyond the basic "microservices" buzzword and master the orchestration between services.
1. The System: International Wire Transfer
This architecture draft draws a microscope to a banking engine, specifically, the part responsible for wire transfers. Its mission is to move funds across borders while satisfying three distinct business requirements:
- Integrity: Ensuring the math adds up and funds are accounted for.
- Security: Screening every transaction against global watchlists.
- Market Accuracy: Locking in volatile currency exchange rates in real-time.
Instead of a single "Banking Service," we have three autonomous "islands" of logic: Ledger (Core), Fraud (Compliance), and FX (Currency).
2. Establishing the concepts
I. Bounded Context: The "Territory" of Data
The term "Bounded Context" was originated by Eric Evans, who defines it as:
A description of a boundary (typically a subsystem, or the work of a particular team) within which a particular model is defined and applicable.
Looking at the diagram, you’ll notice that the Entity: Account exists in all three services, but the Account Model is different in each one. This is the Bounded Context in action.
-
In the Ledger Context: An account is a Balance Container. It only cares about
Available BalanceandCurrency. -
In the Fraud Context: An account is a Legal Risk Profile. It cares about the
Owner Legal NameandWatchlist Flags. -
In the FX Context: An account is a Regional Wallet. It cares about the
Home ISOand theLocal Market Rate.
By isolating these models, we ensure that a change in the Compliance department's risk-rating logic never accidentally breaks the code responsible for calculating account balances. Each service owns its own "version of the truth."
II. Event-Driven Operations: The "Nervous System"
In the provided architecture, notice that the services never call each other's APIs directly. They communicate via a Message Bus.
When a request arrives, the Ledger doesn't "tell" Fraud to start a check; it simply announces a fact: TransferRequested.
- Decoupling: The Ledger doesn't know (or care) if the Fraud service is currently online. It drops the message on the Bus and continues its work.
- Autonomy: The Fraud and FX services are "Subscribers." They react to the message when they have the resources to do so. This prevents a slow-down in one service from cascading into a total system failure.
3. The Saga: Orchestrating the Distributed "Undo"
Now that we have established what the system does and why it is organized this way, one question might pop up: If these services are independent islands, who ensures the process actually finishes?
This is where the Saga Pattern enters.
In a distributed system, there is no "Global Rollback." If the money is locked in the Ledger (Step 2) and the Fraud service emits a SecurityCleared message, but the FX system finds a problem, there is no way to automatically revert all the other database records. We must perform a Compensating Transaction.
How the Saga Orchestrates the System:
-
The Happy Path:
-
Ledger locks the funds emits
TransferRequested. -
Fraud clears the user emits
SecurityCleared. -
FX locks the conversion rate emits
RateLocked. - Result: The transfer is finalized.
-
Ledger locks the funds emits
-
The Failure Path (The "Undo" Button):
-
Ledger locks $10,000 and emits
TransferRequested. -
Fraud Service checks pass and it emits a
SecurityClearedmessage. -
FX Service fails to process the transaction and emits
TransactionNotAllowed. -
The Compensation: The Ledger and Fraud services are "listening" for failure events. Upon hearing
TransactionNotAllowed, they trigger their own internal "Undo" logic to unlock the $10,000 and notify the user.
-
Ledger locks $10,000 and emits
Choreography vs. Orchestration
Our system uses Choreography. There is no central "Boss" or "Orchestrator" service. The Ledger works as an entry point for the operation, but it is not responsible for managing the state of the entire flow.
If we required more granular control over the operation, we could implement Orchestration, where a dedicated service directs the transaction and has the final word to approve or deny it. For many decentralized systems, however, the Choreography shown here offers the best balance of scale and simplicity.
Conclusion: Resilience by Design
By combining Bounded Contexts (to isolate logic), Event-Driven Operations (to decouple communication), and Sagas (to handle failures), we build a system that is far more resilient and performant than any monolith. We accept "Eventual Consistency" in exchange for a system that can scale globally and fail gracefully.


Top comments (0)