Shared Kernel
How to share a controlled subset of the domain model across bounded contexts to reduce duplication without sacrificing team autonomy.
What problem it solves
In a microservices architecture with multiple bounded contexts, each context has its own domain model. But sometimes two or more contexts need to share certain concepts that are identical on both sides:
Sin Shared Kernel:
Order Service Shipping Service
ββββββββββββββββββ ββββββββββββββββββ
β Address { β β Address { β
β street β β street β
β city β β city β
β state β β state β
β zipCode β β zipCode β
β country β β country β
β validate() β β validate() β
β } β β } β
ββββββββββββββββββ ββββββββββββββββββ
Misma clase duplicada en ambos servicios
Si cambia la validaciΓ³n, hay que cambiar en ambos
Riesgo de divergencia silenciosa
The problems are:
- Duplication: The same code is maintained in multiple places
- Divergence: Over time, the copies evolve differently and become inconsistent
- Synchronization effort: Every change must be manually replicated across all contexts
- Subtle bugs: A validation updated in one service but not another causes inconsistencies
How it works
The Shared Kernel defines a small, explicit subset of the domain model that is shared between two or more bounded contexts. This shared kernel is managed as an independent library or package with its own versioning.
Con Shared Kernel:
βββββββββββββββββββββββββββββββββββ
β Shared Kernel β
β (librerΓa versionada) β
β β
β Address { street, city, ... } β
β Money { amount, currency } β
β DateRange { from, to } β
β OrderStatus enum β
βββββββββββ¬ββββββββββββ¬ββββββββββββ
β β
βΌ βΌ
Order Service Shipping Service
(usa v2.1.0) (usa v2.1.0)
What to include in the Shared Kernel
| Include | Do not include |
|---|---|
| Shared Value Objects (Address, Money, DateRange) | Entities with context-specific business logic |
| Shared status enums (OrderStatus) | Repositories or application services |
| Integration event DTOs | Business logic belonging to a single context |
| Contract interfaces (API contracts) | Persistence models (DB entities) |
| Common validations | Infrastructure-specific configuration |
Rules of the Shared Kernel
The Shared Kernel operates under strict rules to keep it from becoming a problem:
- Minimum necessary: Only include what is truly shared between contexts
- Shared ownership: Both teams must agree on any change
- Semantic versioning: Breaking changes require a new major version
- Shared tests: The kernel has its own tests that both teams run
- Joint review: Changes to the kernel require approval from all consuming teams
Example: Shared Kernel as a package
shared-kernel/
βββ src/
β βββ value-objects/
β β βββ Address.ts
β β βββ Money.ts
β β βββ DateRange.ts
β βββ events/
β β βββ OrderCreatedEvent.ts
β β βββ PaymentProcessedEvent.ts
β βββ enums/
β β βββ OrderStatus.ts
β β βββ PaymentMethod.ts
β βββ index.ts
βββ tests/
β βββ Address.test.ts
β βββ Money.test.ts
β βββ DateRange.test.ts
βββ package.json (version: "2.1.0")
βββ CHANGELOG.md
Each service consumes it as a dependency:
// Order Service - package.json
{
"dependencies": {
"@company/shared-kernel": "^2.1.0"
}
}
// Shipping Service - package.json
{
"dependencies": {
"@company/shared-kernel": "^2.1.0"
}
}
Evolution of the Shared Kernel
VersiΓ³n 1.0.0: Address, Money
VersiΓ³n 1.1.0: + DateRange (backward compatible)
VersiΓ³n 2.0.0: Address cambia validaciΓ³n de zipCode (breaking change)
β Ambos equipos deben actualizar y testear
β Se coordina el despliegue
Shared Kernel vs other strategies
| Strategy | Coupling | Autonomy | When to use |
|---|---|---|---|
| Shared Kernel | Medium (controlled) | Medium | Identical concepts across 2-3 contexts |
| ACL (Anti-Corruption Layer) | Low | High | Different models that need translation |
| Copy and diverge | None | Full | Similar concepts that evolve differently |
| Shared service | High | Low | Complete shared functionality |
Advantages
- Eliminates duplication: A single place for shared code
- Guaranteed consistency: All contexts use the same version of the model
- Coordinated evolution: Changes propagate in a controlled way
- Centralized testing: The kernelβs tests validate shared behavior
- Explicit contracts: The kernel formally defines what is shared
Trade-offs / Disadvantages
- Coupling between teams: Changes to the kernel require coordination between teams
- Development speed: A team can get blocked waiting on a kernel change
- Risk of growth: If the kernel grows too large, it becomes a shared monolith
- Complex versioning: Managing versions and compatibility across consumers requires discipline
- Coordinated deployment: Breaking changes require all consumers to update
- Governance: Needs clear rules about who can modify the kernel and how
When to use
- Two or more bounded contexts share identical value objects (Address, Money, DateRange)
- Integration events that must have the same schema on both producer and consumer
- Status enums that must remain consistent across services
- When duplication causes frequent bugs due to divergence
- Teams working in closely related domains that can coordinate easily
When to avoid
- More than 3-4 consuming contexts (coordination becomes unmanageable)
- When the βsharedβ concepts actually have different semantics in each context
- Geographically distributed teams with limited communication
- When team autonomy matters more than avoiding duplication
- If the kernel tends to grow unchecked (a sign that the bounded contexts are poorly defined)
Common technologies and implementations
| Category | Options |
|---|---|
| Distribution | npm packages, Maven artifacts, NuGet packages, Git submodules |
| Versioning | Semantic Versioning (SemVer), Conventional Commits |
| Monorepo | Nx, Turborepo, Lerna (kernel as an internal package) |
| Registry | Private npm registry, Artifactory, GitHub Packages |
| CI/CD | Dedicated pipeline for the kernel with compatibility tests |
Relationship to other patterns
- DDD (Bounded Context): The Shared Kernel is a relationship between bounded contexts defined by Eric Evans
- Anti-Corruption Layer: An alternative to the Shared Kernel when the models differ
- Event-Driven Architecture: Integration event schemas are natural candidates for the kernel
- Microservices: The Shared Kernel must stay small so it doesnβt compromise service independence
- Contracts (API Contracts): API contracts can live in the Shared Kernel
Next steps
The Shared Kernel is a powerful tool, but it demands discipline. To understand the alternative when models are incompatible, check out Anti-Corruption Layer. To see how integration events benefit from a shared kernel, explore Event-Driven Architecture.