ARC-74Recommended
Give each bounded context its own model and words
Split the code into bounded contexts, such as billing, catalogue and shipping. Each context has its own module, its own types and its own glossary, and other contexts use only its public entry point.
How to do it
One shared Customer type for billing, support and shipping grows a field for every team and breaks all of them on each change. In billing a customer has a VAT number and a payment method. In shipping it has an address. Let each context hold its own type, named with the words its business people use, and link them by ID. Write each context's terms in a short glossary file next to its code. This builds on Group code by feature and Keep internals private.
Examples
JavaScript example
// .dependency-cruiser.cjs
module.exports = {
forbidden: [{
name: 'contexts-use-public-entry-only',
severity: 'error',
from: { path: '^src/contexts/([^/]+)/' },
to: { path: '^src/contexts/([^/]+)/(?!index\\.ts$)', pathNot: '^src/contexts/$1/' },
}],
};
Java example
@ArchTest
static final ArchRule shippingUsesBillingApiOnly = noClasses()
.that().resideInAPackage("com.example.shipping..")
.should().dependOnClassesThat().resideInAPackage("com.example.billing.internal..");
How to check
An import rule, such as dependency-cruiser, import-linter or ArchUnit, fails the build when one context imports another context's internal files. Each context folder holds a glossary file.
Sources
- .NET architecture guide on designing a domain model
- .NET architectural principles on bounded contexts
Related rules
- ARC-65 Group code by feature in Clean code and architecture
- ARC-39 Keep internals private in Clean code and architecture
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/architecture/domain-model/bounded-contexts">Agent Wranglers clean code and architecture playbook</a>. In code you write, add a comment next to it with the same link.