Definition
- Bounded Context – a DDD concept defining an explicit boundary within which a domain model and ubiquitous language are consistent.
- Ubiquitous language – the exact words the team uses in code, docs, and conversation, consistent only inside the boundary.
- Microservices mapping – each bounded context becomes one service with a private database.
- Context maps – how contexts relate: shared kernel, customer-supplier, anti-corruption layer, domain events.
- Key principle – models are valid only within their context; never build one universal model for the whole enterprise.
At National Bank of India the word 'account' means different things to different teams. To the Deposits team it is a savings account with a balance. To the Lending team it is a loan account with principal and EMI. To the Cards team it is a credit-card account with a limit. A Bounded Context says: stop trying to force one definition on everybody. Draw a boundary around each world and let each team speak its own precise language inside its own walls. When they must talk, they translate at the border.
Understanding the Concepts
What is a Bounded Context?
A Bounded Context is a DDD concept that defines an explicit boundary within which a particular domain model is consistent and unambiguous. Inside the boundary, every term has exactly one meaning.
package com.bank.deposits.model;
import java.math.BigDecimal;
// In the Deposits bounded context: "Account" = deposit account
public class Account {
private String accountId; // e.g., SAV2001
private String holderName; // Rajesh
private BigDecimal balance; // 500000.00
private String type; // SAV, CUR
}
package com.bank.lending.model;
import java.math.BigDecimal;
// In the Lending bounded context: "Account" = loan account
public class Account {
private String loanAccountId; // LOAN-001
private BigDecimal principal; // 2000000.00
private BigDecimal interestRate; // 8.5%
private int tenureMonths; // 240
}
Same word "Account", different meanings:
Deposits: balance, holder, type
Lending: principal, interest rate, tenure
Cards: limit, outstanding, due date
These are DIFFERENT classes in DIFFERENT services. This is deliberate.
What is ubiquitous language?
The ubiquitous language is the exact words the team uses in code, docs, and conversation. It is consistent only inside the boundary.
Deposits team language:
"account" = savings or current account
"balance" = current available amount
"holder" = person who owns the account
Lending team language:
"account" = loan account
"principal" = original loan amount
"EMI" = equated monthly installment
"sanction" = approve the loan
These are different languages. Each is correct INSIDE its context.
How does bounded context map to microservices?
Each bounded context becomes one service with a private database. This is why Bounded Context and Database-per-Service reinforce each other.
Bounded Context Service Database ───────────────── ─────────────── ────────────── Deposits Context -> Account Service -> account_db Lending Context -> Loan Service -> loan_db Payment Context -> Payment Service -> payment_db Notification Ctx -> Notif Service -> notification_db The boundary in the domain model = the boundary in deployment = the boundary in data.
What are context maps?
Context maps describe how bounded contexts relate to each other.
package com.bank.lending.adapter;
import com.bank.lending.model.FundsAvailability;
import org.springframework.stereotype.Component;
// Anti-Corruption Layer: translates Deposits context into Lending terms.
// The Deposits model never leaks into the Lending model.
@Component
public class DepositsAntiCorruptionLayer {
private final DepositsClient depositsClient;
public DepositsAntiCorruptionLayer(DepositsClient depositsClient) {
this.depositsClient = depositsClient;
}
public FundsAvailability checkFundsForEmiDeduction(String accountId) {
// Call Deposits Service -> get DepositAccount (their model)
var depositAccount = depositsClient.getAccount(accountId);
// Translate into Lending's own model
return new FundsAvailability(
accountId,
depositAccount.getBalance(),
depositAccount.getBalance().compareTo(
java.math.BigDecimal.ZERO) > 0);
}
}
Context map types:
Shared Kernel: small model both teams agree to change together
Customer-Supplier: one context depends on another, they negotiate
Anti-Corruption Layer: translation so another context's model does not leak in
Domain Events: contexts react to events without sharing a model
At National Bank of India:
Deposits publishes FundsDebited event.
Lending consumes it through an anti-corruption layer
that translates it into an EMI payment.
Neither model contaminates the other.
What are the advantages of bounded contexts?
| Advantage | Explanation |
|---|---|
| Clear boundaries | Each team knows exactly what they own |
| Autonomous teams | Teams work independently within their context |
| Consistent language | Reduces miscommunication: one word, one meaning |
| Independent evolution | Schema and code changes are internal to each context |
| Clean data ownership | Database-per-service follows naturally |
What are the disadvantages of bounded contexts?
| Disadvantage | Explanation |
|---|---|
| Model duplication | Same real-world thing represented differently in each context |
| Translation overhead | Need anti-corruption layers and event mappers at borders |
| Wrong boundaries are costly | Redrawing boundaries once services are live is expensive |
| Coordination for cross-context operations | Sagas and eventual consistency instead of simple joins |
What are common mistakes?
The most common mistake is drawing service boundaries without the domain model.
package com.bank.antipattern;
// WRONG: Split services without DDD. One shared "Account" model everywhere.
public class WrongBoundary {
// SharedAccount class used by Deposits, Lending, Cards.
// Every change touches every service.
// Technically split, conceptually still a monolith.
}
// CORRECT: Each context has its own Account model.
public class CorrectBoundary {
// DepositAccount in Deposits context.
// LoanAccount in Lending context.
// CreditCardAccount in Cards context.
// Each evolves independently.
}
What should a senior engineer know?
A bounded context is first a modeling boundary and only then a deployment boundary. If you draw service boundaries without doing the domain modeling, you get technically separated services that still share concepts and therefore still change together, defeating the whole purpose.
Visual Overview
Bounded Contexts at National Bank of India:
┌─────────────────┐ events ┌─────────────────┐
│ DEPOSITS CTX │─────────────►│ LENDING CTX │
│ │ │ │
│ Account = SAV │ FundsDebited│ Account = LOAN │
│ Balance, Holder │ │ Principal, EMI │
│ │ │ │
│ Deposits DB │ │ Lending DB │
└─────────────────┘ └─────────────────┘
│ │
│ events │ events
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ PAYMENT CTX │ │ CARDS CTX │
│ │ │ │
│ Transfer, Settl │ │ Account = CARD │
│ Amount, Status │ │ Limit, Due Date │
│ │ │ │
│ Payment DB │ │ Cards DB │
└─────────────────┘ └─────────────────┘
Each context: own model, own language, own database.
Communication: domain events + anti-corruption layers.
Key Takeaway
A Bounded Context defines an explicit boundary within which one domain model and one ubiquitous language are consistent. At National Bank of India, 'account' means a deposit account to the Deposits team, a loan account to the Lending team, and a credit-card account to the Cards team. Instead of one universal Account model, each team has its own precise definition inside its own walls.
In microservices, each bounded context becomes one service with its own private database. Contexts communicate through domain events and context maps like anti-corruption layers that translate at the border. The Deposits context publishes a FundsDebited event, and the Lending context consumes it through an adapter that translates it into an EMI payment, so neither model contaminates the other.
The disadvantages are real: model duplication, translation overhead at borders, and the cost of fixing boundaries drawn wrong early. The senior point is that a bounded context is first a modeling boundary and only then a deployment boundary. Split services without domain modeling and you get technically separated services that still share concepts and still change together.
Remember one sentence: a Bounded Context is the boundary where one model and one language are consistent, and each one maps to one microservice with its own database so teams can evolve independently.
---