# Domain Architecture

> Every principle in this category is listed as a record.

Page: Ontology · Principles
Canonical: https://banes-lab.com/ontology#architecture-category-domain-architecture

Listed in [Ontology · Principles](https://banes-lab.com/api/pages/ontology/principles.md), after [Anti-patterns](https://banes-lab.com/ontology/principles/architecture-category-anti-patterns.md) and before [Runtime Discovery / Dynamic Binding](https://banes-lab.com/ontology/principles/architecture-category-runtime-discovery-dynamic-binding.md).

Every principle in this category is listed as a record. Each record carries its kind, its severity, the scopes it applies at and the layer it lives in, then the edge relations that join it to other records, the records that point back at it, the contracts that answer to it and the tensions it takes part in. The descriptors say how it is violated, detected, measured, repaired and enforced. Where the record carries one, an exemplar shows the shape before and after the principle is applied.

Relations diagram

The relations inside this category.

```mermaid
flowchart LR
n_domain_driven_design["Domain-Driven Design (DDD)"]
n_domain_model["Domain Model"]
n_bounded_context["Bounded Context"]
n_context_mapping["Context Mapping"]
n_anti_corruption_layer["Anti-Corruption Layer"]
n_explicit_boundaries["Explicit Boundaries"]
n_aggregate["Aggregate"]
n_value_object["Value Object"]
n_entity["Entity"]
n_domain_service["Domain Service"]
n_domain_driven_design --> n_bounded_context
n_domain_driven_design --> n_domain_model
n_bounded_context --> n_explicit_boundaries
n_bounded_context --> n_context_mapping
n_context_mapping --> n_bounded_context
n_context_mapping --> n_explicit_boundaries
n_context_mapping --> n_anti_corruption_layer
n_aggregate --> n_explicit_boundaries
n_entity --> n_domain_model
n_entity -.-> n_value_object
n_domain_service --> n_domain_model
n_domain_service -.-> n_aggregate
```

### Domain-Driven Design (DDD)

- Kind: [style](https://banes-lab.com/records/kind/style.md)
- Category: [Domain Architecture](https://banes-lab.com/ontology/principles/architecture-category-domain-architecture.md)
- Severity: [contextual](https://banes-lab.com/records/vocabulary/severity-contextual.md)
- Scope: domain, bounded context, system
- Aliases: DDD
- Layer: [Domain Modeling](https://banes-lab.com/records/layer/domain-modeling.md)

Details

Definition
A convention of modeling software on the business domain's own language, split into bounded contexts with a domain model in each.

Requires
[Ubiquitous Language](https://banes-lab.com/records/architecture/ubiquitous-language.md), [Bounded Context](https://banes-lab.com/records/architecture/bounded-context.md)

Reinforces
[Domain Model](https://banes-lab.com/records/architecture/domain-model.md), [Semantic Consistency](https://banes-lab.com/records/architecture/semantic-consistency.md)

Enables
[Domain Alignment](https://banes-lab.com/records/lexicon/domain-alignment.md)

In tension with
[Simple CRUD](https://banes-lab.com/records/lexicon/simple-crud.md)

Conflicts with
[Anemic Transaction Script](https://banes-lab.com/records/lexicon/anemic-transaction-script.md)

Referenced by
[Domain Events](https://banes-lab.com/records/architecture/domain-events.md), [Ubiquitous Language](https://banes-lab.com/records/architecture/ubiquitous-language.md)

Tensions
[Domain-Driven Design (DDD) / Simple CRUD](https://banes-lab.com/records/tension/domain-driven-design-ddd-simple-crud.md)

Distinct from
[Event-Driven Architecture](https://banes-lab.com/records/architecture/event-driven-architecture.md): Domain-driven design models software on the domain's language, while event-driven architecture connects services through published events.

Violated by
domain logic in infrastructure/controllers

Detected by
anemic models, scattered business rules

Measured by
domain logic locality

Refactored by
Extract Domain Model, Add Aggregate, Split Context

Enforced by
layer rules, domain tests

Before

```typescript
function updateFoo(row: FooRow, name: string) {
row.name = name;
row.updated_at = Date.now();
return fooTable.save(row);
}
```

After

```typescript
class Foo {
private constructor(readonly id: FooId, private name: string) {}
rename(name: FooName) { this.name = name.value; }
}
foo.rename(FooName.create(name));
```

How it is checked

Checked by
layer rules, domain tests

Population
Every domain type, business rule and context boundary in the domain layer

Freshness
A verdict stands until a domain type, a rule's location or a context boundary changes

Refusal
The layer rule or the domain test fails when a rule or a model leaves its context or its aggregate

Observation
Where each business rule and invariant is enforced, read from source and the domain tests

Evidence
None, because the catalog states this check as a class, so a watched run belongs to each system that adopts it

Authoritative side
The domain model, which decides the context and the aggregate every rule belongs to

Depends on
[Ubiquitous Language](https://banes-lab.com/records/architecture/ubiquitous-language.md), [Bounded Context](https://banes-lab.com/records/architecture/bounded-context.md), [Domain Model](https://banes-lab.com/records/architecture/domain-model.md), [Semantic Consistency](https://banes-lab.com/records/architecture/semantic-consistency.md), [Domain Alignment](https://banes-lab.com/records/lexicon/domain-alignment.md)

Shape it refuses
[Anemic Transaction Script](https://banes-lab.com/records/lexicon/anemic-transaction-script.md)

### Domain Model

- Kind: [artifact](https://banes-lab.com/records/kind/artifact.md)
- Category: [Domain Architecture](https://banes-lab.com/ontology/principles/architecture-category-domain-architecture.md)
- Severity: [contextual](https://banes-lab.com/records/vocabulary/severity-contextual.md)
- Scope: domain, bounded context
- Layer: [Domain Modeling](https://banes-lab.com/records/layer/domain-modeling.md)

Details

Definition
A formal definition of the business concepts, rules and invariants of one context, written as types with behavior.

Requires
[Ubiquitous Language](https://banes-lab.com/records/architecture/ubiquitous-language.md), [Invariant](https://banes-lab.com/records/architecture/invariant.md)

Reinforces
[Correctness](https://banes-lab.com/records/architecture/correctness.md), [Semantic Contracts](https://banes-lab.com/records/architecture/semantic-contracts.md)

Enables
[Business Rule Encapsulation](https://banes-lab.com/records/lexicon/business-rule-encapsulation.md)

In tension with
[Persistence Simplicity](https://banes-lab.com/records/lexicon/persistence-simplicity.md)

Conflicts with
[Anemic Model](https://banes-lab.com/records/lexicon/anemic-model.md)

Referenced by
[Domain-Driven Design (DDD)](https://banes-lab.com/records/architecture/domain-driven-design.md), [Entity](https://banes-lab.com/records/architecture/entity.md), [Domain Service](https://banes-lab.com/records/architecture/domain-service.md), [Semantic Contracts](https://banes-lab.com/records/architecture/semantic-contracts.md), [Domain Events](https://banes-lab.com/records/architecture/domain-events.md)

Tensions
[Domain Model / Persistence Simplicity](https://banes-lab.com/records/tension/domain-model-persistence-simplicity.md)

Violated by
business rules outside domain objects/services

Detected by
procedural domain logic in services/controllers

Measured by
rule locality, invariant coverage

Refactored by
Move Logic to Domain, Add Value Object, Add Aggregate

Enforced by
domain layer rules, [tests](https://banes-lab.com/records/lexicon/tests.md)

Before

```typescript
type Foo = { status: string; count: number };
function closeFoo(foo: Foo) { foo.status = "closed"; }
```

After

```typescript
class Foo {
#status: "open" | "closed" = "open";
close() {
if (this.#status === "closed") throw new Error("Foo already closed");
this.#status = "closed";
}
}
```

How it is checked

Checked by
domain layer rules, tests

Population
Every domain type, business rule and context boundary in the domain layer

Freshness
A verdict stands until a domain type, a rule's location or a context boundary changes

Refusal
The layer rule or the domain test fails when a rule or a model leaves its context or its aggregate

Observation
Where each business rule and invariant is enforced, read from source and the domain tests

Evidence
None, because the catalog states this check as a class, so a watched run belongs to each system that adopts it

Authoritative side
The domain model, which decides the context and the aggregate every rule belongs to

Depends on
[Ubiquitous Language](https://banes-lab.com/records/architecture/ubiquitous-language.md), [Invariant](https://banes-lab.com/records/architecture/invariant.md), [Correctness](https://banes-lab.com/records/architecture/correctness.md), [Semantic Contracts](https://banes-lab.com/records/architecture/semantic-contracts.md), [Business Rule Encapsulation](https://banes-lab.com/records/lexicon/business-rule-encapsulation.md)

Shape it refuses
[Anemic Model](https://banes-lab.com/records/lexicon/anemic-model.md)

### Bounded Context

- Kind: [constraint](https://banes-lab.com/records/kind/constraint.md)
- Category: [Domain Architecture](https://banes-lab.com/ontology/principles/architecture-category-domain-architecture.md)
- Severity: [recommended](https://banes-lab.com/records/vocabulary/severity-recommended.md)
- Scope: domain, service, team
- Aliases: Bounded Contexts
- Layer: [Domain Modeling](https://banes-lab.com/records/layer/domain-modeling.md)

Details

Definition
A rule or precondition that each domain model holds inside one explicit boundary, where its terms have one meaning.

Requires
[Explicit Boundaries](https://banes-lab.com/records/architecture/explicit-boundaries.md), [Ubiquitous Language](https://banes-lab.com/records/architecture/ubiquitous-language.md)

Reinforces
[Modularity](https://banes-lab.com/records/architecture/modularity.md), [Autonomy](https://banes-lab.com/records/architecture/autonomy.md)

Enables
[Context Mapping](https://banes-lab.com/records/architecture/context-mapping.md), [Microservices](https://banes-lab.com/records/architecture/microservices.md)

In tension with
[Cross-Context Reuse](https://banes-lab.com/records/lexicon/cross-context-reuse.md)

Conflicts with
[Shared Global Model](https://banes-lab.com/records/lexicon/shared-global-model.md)

Referenced by
[Domain-Driven Design (DDD)](https://banes-lab.com/records/architecture/domain-driven-design.md), [Context Mapping](https://banes-lab.com/records/architecture/context-mapping.md), [Package by Feature](https://banes-lab.com/records/architecture/package-by-feature.md), [Microservices](https://banes-lab.com/records/architecture/microservices.md)

Contracts
[Domain Modeling](https://banes-lab.com/records/algorithms/domain-modeling.md)

Tensions
[Bounded Context / Cross-Context Reuse](https://banes-lab.com/records/tension/bounded-context-cross-context-reuse.md)

Distinct from
[Relationship Semantics](https://banes-lab.com/records/lexicon/relationship-semantics.md): A bounded context fixes meaning inside one boundary, while relationship semantics fixes the meaning of each link between boundaries.

Violated by
cross-context model leakage

Detected by
shared domain entities across contexts

Measured by
context coupling

Refactored by
Split Model, Add Anti-Corruption Layer, Define Context Map

Enforced by
package/service boundaries

Before

```typescript
type FooStatus = "A" | "D";
function priceBar(status: FooStatus) { return status === "A" ? 10 : 0; }
```

After

```typescript
type FooStatus = "active" | "disabled";
type BarEligibility = "eligible" | "ineligible";
function toBarEligibility(status: FooStatus): BarEligibility {
return status === "active" ? "eligible" : "ineligible";
}
```

How it is checked

Checked by
package/service boundaries

Population
Every domain type, business rule and context boundary in the domain layer

Freshness
A verdict stands until a domain type, a rule's location or a context boundary changes

Refusal
The layer rule or the domain test fails when a rule or a model leaves its context or its aggregate

Observation
Where each business rule and invariant is enforced, read from source and the domain tests

Evidence
None, because the catalog states this check as a class, so a watched run belongs to each system that adopts it

Authoritative side
The domain model, which decides the context and the aggregate every rule belongs to

Depends on
[Explicit Boundaries](https://banes-lab.com/records/architecture/explicit-boundaries.md), [Ubiquitous Language](https://banes-lab.com/records/architecture/ubiquitous-language.md), [Modularity](https://banes-lab.com/records/architecture/modularity.md), [Autonomy](https://banes-lab.com/records/architecture/autonomy.md), [Context Mapping](https://banes-lab.com/records/architecture/context-mapping.md), [Microservices](https://banes-lab.com/records/architecture/microservices.md)

Shape it refuses
[Shared Global Model](https://banes-lab.com/records/lexicon/shared-global-model.md)

### Context Mapping

- Kind: [activity](https://banes-lab.com/records/kind/activity.md)
- Category: [Domain Architecture](https://banes-lab.com/ontology/principles/architecture-category-domain-architecture.md)
- Severity: [recommended](https://banes-lab.com/records/vocabulary/severity-recommended.md)
- Scope: bounded contexts, integration
- Layer: [Domain Modeling](https://banes-lab.com/records/layer/domain-modeling.md)

Details

Definition
The activity of recording how bounded contexts relate, including which one is upstream and how their models translate.

Requires
[Bounded Context](https://banes-lab.com/records/architecture/bounded-context.md), [Relationship Semantics](https://banes-lab.com/records/lexicon/relationship-semantics.md)

Reinforces
[Explicit Boundaries](https://banes-lab.com/records/architecture/explicit-boundaries.md), [Integration Clarity](https://banes-lab.com/records/lexicon/integration-clarity.md)

Enables
[Anti-Corruption Layer](https://banes-lab.com/records/architecture/anti-corruption-layer.md)

In tension with
[Documentation Overhead](https://banes-lab.com/records/lexicon/documentation-overhead.md)

Conflicts with
[Implicit Integration](https://banes-lab.com/records/lexicon/implicit-integration.md)

Referenced by
[Bounded Context](https://banes-lab.com/records/architecture/bounded-context.md)

Tensions
[Context Mapping / Documentation Overhead](https://banes-lab.com/records/tension/context-mapping-documentation-overhead.md)

Violated by
undocumented service/domain relationships

Detected by
unclear ownership, ambiguous integration flows

Measured by
undocumented dependency count

Refactored by
Define Context Map, Classify Upstream/Downstream

Enforced by
architecture docs, dependency reviews

Before

```typescript
fooService.writeDirectly(barDatabase, foo);
barService.readDirectly(fooDatabase, foo.id);
```

After

```typescript
const contextMap = {
upstream: "FooContext",
downstream: "BarContext",
relationship: "published-language",
} as const;
fooEvents.publish(toBarIntegrationEvent(foo));
```

How it is checked

Checked by
architecture docs, dependency reviews

Population
Every domain type, business rule and context boundary in the domain layer

Freshness
A verdict stands until a domain type, a rule's location or a context boundary changes

Refusal
The layer rule or the domain test fails when a rule or a model leaves its context or its aggregate

Observation
Where each business rule and invariant is enforced, read from source and the domain tests

Evidence
None, because the catalog states this check as a class, so a watched run belongs to each system that adopts it

Authoritative side
The domain model, which decides the context and the aggregate every rule belongs to

Depends on
[Bounded Context](https://banes-lab.com/records/architecture/bounded-context.md), [Relationship Semantics](https://banes-lab.com/records/lexicon/relationship-semantics.md), [Explicit Boundaries](https://banes-lab.com/records/architecture/explicit-boundaries.md), [Integration Clarity](https://banes-lab.com/records/lexicon/integration-clarity.md), [Anti-Corruption Layer](https://banes-lab.com/records/architecture/anti-corruption-layer.md)

Shape it refuses
[Implicit Integration](https://banes-lab.com/records/lexicon/implicit-integration.md)

### Anti-Corruption Layer

- Kind: [pattern](https://banes-lab.com/records/kind/pattern.md)
- Category: [Domain Architecture](https://banes-lab.com/ontology/principles/architecture-category-domain-architecture.md)
- Severity: [recommended](https://banes-lab.com/records/vocabulary/severity-recommended.md)
- Scope: integration, bounded context boundary
- Layer: [Domain Modeling](https://banes-lab.com/records/layer/domain-modeling.md)

Details

Definition
A design pattern that translates an external system's model into the local domain's terms at the boundary.

Requires
[Explicit Boundary](https://banes-lab.com/records/lexicon/explicit-boundary.md), [Translation Model](https://banes-lab.com/records/lexicon/translation-model.md)

Reinforces
[Domain Purity](https://banes-lab.com/records/lexicon/domain-purity.md), [Low Coupling](https://banes-lab.com/records/architecture/low-coupling.md)

Enables
[Legacy/System Integration](https://banes-lab.com/records/lexicon/legacy-system-integration.md)

In tension with
[Mapping Overhead](https://banes-lab.com/records/lexicon/mapping-overhead.md)

Conflicts with
[Vendor Lock-In Leakage](https://banes-lab.com/records/architecture/vendor-lock-in-leakage.md)

Referenced by
[Context Mapping](https://banes-lab.com/records/architecture/context-mapping.md), [Adapter Pattern](https://banes-lab.com/records/architecture/adapter-pattern.md)

Contracts
[Anti-Corruption Layer Over Cross-Context Leak](https://banes-lab.com/records/algorithms/no-leaky-context.md)

Tensions
[Anti-Corruption Layer / Mapping Overhead](https://banes-lab.com/records/tension/anti-corruption-layer-mapping-overhead.md)

Violated by
external model leaking into domain

Detected by
external DTOs used in domain layer

Measured by
leakage count, adapter coverage

Refactored by
Add Translator, Add Adapter, Introduce Boundary DTO

Enforced by
import rules, layer tests

Before

```typescript
function createBar(fooResponse: FooApiResponse) {
return barService.create({ foo_status: fooResponse.state_code });
}
```

After

```typescript
type BarInput = { eligible: boolean };
function fromFoo(response: FooApiResponse): BarInput {
return { eligible: response.state_code === "A" };
}
barService.create(fromFoo(fooResponse));
```

How it is checked

Checked by
import rules, layer tests

Population
Every domain type, business rule and context boundary in the domain layer

Freshness
A verdict stands until a domain type, a rule's location or a context boundary changes

Refusal
The layer rule or the domain test fails when a rule or a model leaves its context or its aggregate

Observation
Where each business rule and invariant is enforced, read from source and the domain tests

Evidence
None, because the catalog states this check as a class, so a watched run belongs to each system that adopts it

Authoritative side
The domain model, which decides the context and the aggregate every rule belongs to

Depends on
[Explicit Boundary](https://banes-lab.com/records/lexicon/explicit-boundary.md), [Translation Model](https://banes-lab.com/records/lexicon/translation-model.md), [Domain Purity](https://banes-lab.com/records/lexicon/domain-purity.md), [Low Coupling](https://banes-lab.com/records/architecture/low-coupling.md), [Legacy/System Integration](https://banes-lab.com/records/lexicon/legacy-system-integration.md)

Shape it refuses
[Vendor Lock-In Leakage](https://banes-lab.com/records/architecture/vendor-lock-in-leakage.md), [Vendor Lock-In Leakage](https://banes-lab.com/records/architecture/vendor-lock-in-leakage.md)

### Explicit Boundaries

- Kind: [principle](https://banes-lab.com/records/kind/principle.md)
- Category: [Domain Architecture](https://banes-lab.com/ontology/principles/architecture-category-domain-architecture.md)
- Severity: [mandatory](https://banes-lab.com/records/vocabulary/severity-mandatory.md)
- Scope: module, component, service, domain
- Layer: [Domain Modeling](https://banes-lab.com/records/layer/domain-modeling.md)

Details

Definition
A design rule that each module declares its public interface and owner, and other modules reach it only through that interface.

Requires
[Stable Interfaces](https://banes-lab.com/records/architecture/stable-interfaces.md), [Ownership](https://banes-lab.com/records/lexicon/ownership.md)

Reinforces
[Modularity](https://banes-lab.com/records/architecture/modularity.md), [Encapsulation](https://banes-lab.com/records/architecture/encapsulation.md)

Enables
[Replaceability](https://banes-lab.com/records/architecture/replaceability.md), [Governance](https://banes-lab.com/records/architecture/governance.md)

In tension with
[Cross-Cutting Concerns](https://banes-lab.com/records/lexicon/cross-cutting-concerns.md)

Conflicts with
[Boundary Leakage](https://banes-lab.com/records/architecture/boundary-leakage.md)

Referenced by
[Bounded Context](https://banes-lab.com/records/architecture/bounded-context.md), [Context Mapping](https://banes-lab.com/records/architecture/context-mapping.md), [Aggregate](https://banes-lab.com/records/architecture/aggregate.md), [Single Responsibility Principle (SRP)](https://banes-lab.com/records/architecture/single-responsibility.md), [Separation of Concerns](https://banes-lab.com/records/architecture/separation-of-concerns.md), [High Cohesion](https://banes-lab.com/records/architecture/high-cohesion.md), [Modularity](https://banes-lab.com/records/architecture/modularity.md), [Independence](https://banes-lab.com/records/architecture/independence.md), [Declared Jurisdiction](https://banes-lab.com/records/architecture/declared-jurisdiction.md)

Tensions
[Explicit Boundaries / Cross-Cutting Concerns](https://banes-lab.com/records/tension/cross-cutting-concerns-explicit-boundaries.md)

Distinct from
[Declared Jurisdiction](https://banes-lab.com/records/architecture/declared-jurisdiction.md): Explicit boundaries declare a module's interface and owner, while declared jurisdiction declares which roots and files a naming gate governs.

Distinct from
[Abstraction](https://banes-lab.com/records/architecture/abstraction.md): Explicit boundaries fix where one module ends, while abstraction fixes how a concept is expressed without its detail.

Distinct from
[Encapsulation](https://banes-lab.com/records/architecture/encapsulation.md): Explicit boundaries govern access between modules, while encapsulation governs changes to one object's state.

Distinct from
[Governance](https://banes-lab.com/records/architecture/governance.md): Explicit boundaries are the declared interfaces, while governance holds decisions to shared policy.

Distinct from
[Single Source of Truth](https://banes-lab.com/records/architecture/single-source-of-truth.md): Explicit boundaries give each module one public interface, while a single source of truth gives each fact one owning definition.

Distinct from
[Independence](https://banes-lab.com/records/architecture/independence.md): Explicit boundaries declare the interface, while independence is testing and deploying a module without its neighbors.

Distinct from
[Modularity](https://banes-lab.com/records/architecture/modularity.md): Explicit boundaries declare each module's interface and owner, while modularity is the division into modules itself.

Distinct from
[Separation of Concerns](https://banes-lab.com/records/architecture/separation-of-concerns.md): Explicit boundaries declare interfaces, while separation of concerns decides which concerns sit on each side of them.

Distinct from
[Single Responsibility Principle (SRP)](https://banes-lab.com/records/architecture/single-responsibility.md): Explicit boundaries declare interfaces, while single responsibility limits what sits behind one to one reason to change.

Violated by
internal imports, shared mutable internals

Detected by
forbidden imports, [cyclic dependencies](https://banes-lab.com/records/architecture/circular-dependency.md)

Measured by
boundary violation count

Refactored by
Move Code, Extract API, Restrict Exports

Enforced by
module rules, architecture tests

Before

```typescript
import { fooDatabase } from "../../foo/infrastructure/database";
export function loadBar(id: string) { return fooDatabase.query(id); }
```

After

```typescript
export interface FooGateway { find(id: FooId): Promise<FooSnapshot>; }
export function loadBar(id: FooId, foos: FooGateway) { return foos.find(id); }
```

How it is checked

Checked by
module rules, architecture tests

Population
Every domain type, business rule and context boundary in the domain layer

Freshness
A verdict stands until a domain type, a rule's location or a context boundary changes

Refusal
The layer rule or the domain test fails when a rule or a model leaves its context or its aggregate

Observation
Where each business rule and invariant is enforced, read from source and the domain tests

Evidence
None, because the catalog states this check as a class, so a watched run belongs to each system that adopts it

Authoritative side
The domain model, which decides the context and the aggregate every rule belongs to

Depends on
[Stable Interfaces](https://banes-lab.com/records/architecture/stable-interfaces.md), [Ownership](https://banes-lab.com/records/lexicon/ownership.md), [Modularity](https://banes-lab.com/records/architecture/modularity.md), [Encapsulation](https://banes-lab.com/records/architecture/encapsulation.md), [Replaceability](https://banes-lab.com/records/architecture/replaceability.md), [Governance](https://banes-lab.com/records/architecture/governance.md)

Shape it refuses
[Boundary Leakage](https://banes-lab.com/records/architecture/boundary-leakage.md)

### Aggregate

- Kind: [pattern](https://banes-lab.com/records/kind/pattern.md)
- Category: [Domain Architecture](https://banes-lab.com/ontology/principles/architecture-category-domain-architecture.md)
- Severity: [contextual](https://banes-lab.com/records/vocabulary/severity-contextual.md)
- Scope: domain, consistency boundary, bounded context
- Layer: [Domain Modeling](https://banes-lab.com/records/layer/domain-modeling.md)

Details

Definition
A design pattern that groups entities under one root, which is the only entry point and keeps the group's invariants.

Requires
[Explicit Boundaries](https://banes-lab.com/records/architecture/explicit-boundaries.md)

Reinforces
[Invariant](https://banes-lab.com/records/architecture/invariant.md), [Encapsulation](https://banes-lab.com/records/architecture/encapsulation.md)

Enables
[Transactional Consistency Boundary](https://banes-lab.com/records/lexicon/transactional-consistency-boundary.md), [Root-Guarded Invariants](https://banes-lab.com/records/lexicon/root-guarded-invariants.md)

In tension with
[Aggregate Size](https://banes-lab.com/records/lexicon/aggregate-size.md)

Conflicts with
[Anemic Domain Model](https://banes-lab.com/records/architecture/anemic-domain-model.md)

Referenced by
[Domain Service](https://banes-lab.com/records/architecture/domain-service.md)

Tensions
[Aggregate / Aggregate Size](https://banes-lab.com/records/tension/aggregate-aggregate-size.md)

Violated by
invariants enforced by services outside the entity cluster

Detected by
cross-entity invariant checks scattered in services

Measured by
out-of-aggregate invariant enforcement count

Refactored by
Define Aggregate Root, Enforce Invariants Within

Enforced by
domain model review

Before

```typescript
fooOrder.total -= item.price;
fooOrderItems.delete(item.id);
```

After

```typescript
class FooOrder {
#items: FooItem[] = [];
#total = 0;
removeItem(id: FooItemId) {
this.#items = this.#items.filter(item => item.id !== id);
this.#total = this.#items.reduce((sum, item) => sum + item.price, 0);
}
}
```

How it is checked

Checked by
domain model review

Population
Every domain type, business rule and context boundary in the domain layer

Freshness
A verdict stands until a domain type, a rule's location or a context boundary changes

Refusal
The layer rule or the domain test fails when a rule or a model leaves its context or its aggregate

Observation
Where each business rule and invariant is enforced, read from source and the domain tests

Evidence
None, because the catalog states this check as a class, so a watched run belongs to each system that adopts it

Authoritative side
The domain model, which decides the context and the aggregate every rule belongs to

Depends on
[Explicit Boundaries](https://banes-lab.com/records/architecture/explicit-boundaries.md), [Invariant](https://banes-lab.com/records/architecture/invariant.md), [Encapsulation](https://banes-lab.com/records/architecture/encapsulation.md), [Transactional Consistency Boundary](https://banes-lab.com/records/lexicon/transactional-consistency-boundary.md), [Root-Guarded Invariants](https://banes-lab.com/records/lexicon/root-guarded-invariants.md)

Shape it refuses
[Anemic Domain Model](https://banes-lab.com/records/architecture/anemic-domain-model.md)

### Value Object

- Kind: [pattern](https://banes-lab.com/records/kind/pattern.md)
- Category: [Domain Architecture](https://banes-lab.com/ontology/principles/architecture-category-domain-architecture.md)
- Severity: [recommended](https://banes-lab.com/records/vocabulary/severity-recommended.md)
- Scope: domain, modeling, immutability
- Layer: [Domain Modeling](https://banes-lab.com/records/layer/domain-modeling.md)

Details

Definition
A design pattern that models a domain value as an immutable, self-validating object compared by its attributes.

Requires
[Value Equality](https://banes-lab.com/records/lexicon/value-equality.md)

Reinforces
[Immutability](https://banes-lab.com/records/architecture/immutability.md), [Encapsulation](https://banes-lab.com/records/architecture/encapsulation.md)

Enables
[Self-Validating Values](https://banes-lab.com/records/lexicon/self-validating-values.md), [Side-Effect-Free Equality](https://banes-lab.com/records/lexicon/side-effect-free-equality.md)

In tension with
[Object Count](https://banes-lab.com/records/lexicon/object-count.md)

Conflicts with
[Primitive Obsession](https://banes-lab.com/records/architecture/primitive-obsession.md), [Data Clumps](https://banes-lab.com/records/architecture/data-clumps.md), [Long Parameter List](https://banes-lab.com/records/architecture/long-parameter-list.md)

Referenced by
[Entity](https://banes-lab.com/records/architecture/entity.md)

Tensions
[Value Object / Object Count](https://banes-lab.com/records/tension/object-count-value-object.md)

Violated by
domain concepts carried as bare primitives

Detected by
repeated validation of the same primitive shape

Measured by
primitive-typed domain concept count

Refactored by
Introduce Value Object

Enforced by
domain model review

Before

```typescript
function priceFoo(amount: number, currency: string) { return { amount, currency }; }
```

After

```typescript
class Money {
private constructor(readonly amount: number, readonly currency: string) {}
static of(amount: number, currency: string): Money {
if (amount < 0) throw new Error("negative money");
return new Money(amount, currency);
}
equals(other: Money) { return this.amount === other.amount && this.currency === other.currency; }
add(other: Money): Money { return Money.of(this.amount + other.amount, this.currency); }
}
```

How it is checked

Checked by
domain model review

Population
Every domain type, business rule and context boundary in the domain layer

Freshness
A verdict stands until a domain type, a rule's location or a context boundary changes

Refusal
The layer rule or the domain test fails when a rule or a model leaves its context or its aggregate

Observation
Where each business rule and invariant is enforced, read from source and the domain tests

Evidence
None, because the catalog states this check as a class, so a watched run belongs to each system that adopts it

Authoritative side
The domain model, which decides the context and the aggregate every rule belongs to

Depends on
[Value Equality](https://banes-lab.com/records/lexicon/value-equality.md), [Immutability](https://banes-lab.com/records/architecture/immutability.md), [Encapsulation](https://banes-lab.com/records/architecture/encapsulation.md), [Self-Validating Values](https://banes-lab.com/records/lexicon/self-validating-values.md), [Side-Effect-Free Equality](https://banes-lab.com/records/lexicon/side-effect-free-equality.md)

Shape it refuses
[Primitive Obsession](https://banes-lab.com/records/architecture/primitive-obsession.md), [Data Clumps](https://banes-lab.com/records/architecture/data-clumps.md), [Long Parameter List](https://banes-lab.com/records/architecture/long-parameter-list.md)

### Entity

- Kind: [pattern](https://banes-lab.com/records/kind/pattern.md)
- Category: [Domain Architecture](https://banes-lab.com/ontology/principles/architecture-category-domain-architecture.md)
- Severity: [contextual](https://banes-lab.com/records/vocabulary/severity-contextual.md)
- Scope: domain, identity, lifecycle
- Layer: [Domain Modeling](https://banes-lab.com/records/layer/domain-modeling.md)

Details

Definition
A design pattern that models a domain object by a stable identity, which stays the same while its attributes change.

Requires
[Stable Identity](https://banes-lab.com/records/lexicon/stable-identity.md)

Reinforces
[Domain Model](https://banes-lab.com/records/architecture/domain-model.md), [Encapsulation](https://banes-lab.com/records/architecture/encapsulation.md)

Enables
[Identity-Based Equality](https://banes-lab.com/records/lexicon/identity-based-equality.md), [Lifecycle Tracking](https://banes-lab.com/records/lexicon/lifecycle-tracking.md)

In tension with
[Value Object](https://banes-lab.com/records/architecture/value-object.md)

Conflicts with
[Anemic Domain Model](https://banes-lab.com/records/architecture/anemic-domain-model.md)

Tensions
[Entity / Value Object](https://banes-lab.com/records/tension/entity-value-object.md)

Distinct from
[Value Object](https://banes-lab.com/records/architecture/value-object.md): An entity is known by an identity that outlives its attributes, while a value object is known only by its attributes.

Violated by
identity equated by attribute comparison

Detected by
equality by field value where identity is meant

Measured by
attribute-equality misuse count

Refactored by
Model Identity Explicitly

Enforced by
domain model review

Before

```typescript
type Foo = { id: string; name: string; status: string };
foo.status = "active";
```

After

```typescript
class Foo {
constructor(readonly id: FooId, private name: string, private status: FooStatus) {}
equals(other: Foo) { return this.id === other.id; }
activate() { this.status = "active"; }
}
```

How it is checked

Checked by
domain model review

Population
Every domain type, business rule and context boundary in the domain layer

Freshness
A verdict stands until a domain type, a rule's location or a context boundary changes

Refusal
The layer rule or the domain test fails when a rule or a model leaves its context or its aggregate

Observation
Where each business rule and invariant is enforced, read from source and the domain tests

Evidence
None, because the catalog states this check as a class, so a watched run belongs to each system that adopts it

Authoritative side
The domain model, which decides the context and the aggregate every rule belongs to

Depends on
[Stable Identity](https://banes-lab.com/records/lexicon/stable-identity.md), [Domain Model](https://banes-lab.com/records/architecture/domain-model.md), [Encapsulation](https://banes-lab.com/records/architecture/encapsulation.md), [Identity-Based Equality](https://banes-lab.com/records/lexicon/identity-based-equality.md), [Lifecycle Tracking](https://banes-lab.com/records/lexicon/lifecycle-tracking.md)

Shape it refuses
[Anemic Domain Model](https://banes-lab.com/records/architecture/anemic-domain-model.md)

### Domain Service

- Kind: [pattern](https://banes-lab.com/records/kind/pattern.md)
- Category: [Domain Architecture](https://banes-lab.com/ontology/principles/architecture-category-domain-architecture.md)
- Severity: [contextual](https://banes-lab.com/records/vocabulary/severity-contextual.md)
- Scope: domain, behavior, coordination
- Layer: [Domain Modeling](https://banes-lab.com/records/layer/domain-modeling.md)

Details

Definition
A design pattern that places a domain rule spanning several entities in a stateless service named in the domain's language.

Requires
[Domain Model](https://banes-lab.com/records/architecture/domain-model.md)

Reinforces
[Single Responsibility Principle (SRP)](https://banes-lab.com/records/architecture/single-responsibility.md), [Ubiquitous Language](https://banes-lab.com/records/architecture/ubiquitous-language.md)

Enables
[Cross-Entity Domain Logic](https://banes-lab.com/records/lexicon/cross-entity-domain-logic.md)

In tension with
[Aggregate](https://banes-lab.com/records/architecture/aggregate.md)

Conflicts with
[Fat Controller](https://banes-lab.com/records/architecture/fat-controller.md), [Transaction Script Sprawl](https://banes-lab.com/records/architecture/transaction-script-sprawl.md)

Tensions
[Domain Service / Aggregate](https://banes-lab.com/records/tension/aggregate-domain-service.md)

Distinct from
[Aggregate](https://banes-lab.com/records/architecture/aggregate.md): A domain service holds a stateless rule spanning entities, while an aggregate holds entities and their invariants under one root.

Violated by
multi-entity domain rules living in controllers

Detected by
domain logic in application/transport layers

Measured by
misplaced domain-rule count

Refactored by
Extract Domain Service

Enforced by
domain model review

Before

```typescript
class FooAccount {
transferTo(other: FooAccount, amount: number) {
this.balance -= amount;
other.balance += amount;
}
}
```

After

```typescript
class FooTransferService {
transfer(from: FooAccount, to: FooAccount, amount: Money) {
from.withdraw(amount);
to.deposit(amount);
}
}
```

How it is checked

Checked by
domain model review

Population
Every domain type, business rule and context boundary in the domain layer

Freshness
A verdict stands until a domain type, a rule's location or a context boundary changes

Refusal
The layer rule or the domain test fails when a rule or a model leaves its context or its aggregate

Observation
Where each business rule and invariant is enforced, read from source and the domain tests

Evidence
None, because the catalog states this check as a class, so a watched run belongs to each system that adopts it

Authoritative side
The domain model, which decides the context and the aggregate every rule belongs to

Depends on
[Domain Model](https://banes-lab.com/records/architecture/domain-model.md), [Single Responsibility Principle (SRP)](https://banes-lab.com/records/architecture/single-responsibility.md), [Ubiquitous Language](https://banes-lab.com/records/architecture/ubiquitous-language.md), [Cross-Entity Domain Logic](https://banes-lab.com/records/lexicon/cross-entity-domain-logic.md)

Shape it refuses
[Fat Controller](https://banes-lab.com/records/architecture/fat-controller.md), [Transaction Script Sprawl](https://banes-lab.com/records/architecture/transaction-script-sprawl.md)

## Links to

- [style](https://banes-lab.com/records/kind/style.md)
- [contextual](https://banes-lab.com/records/vocabulary/severity-contextual.md)
- [Domain Modeling](https://banes-lab.com/records/layer/domain-modeling.md)
- [Ubiquitous Language](https://banes-lab.com/records/architecture/ubiquitous-language.md)
- [Bounded Context](https://banes-lab.com/records/architecture/bounded-context.md)
- [Domain Model](https://banes-lab.com/records/architecture/domain-model.md)
- [Semantic Consistency](https://banes-lab.com/records/architecture/semantic-consistency.md)
- [Domain Alignment](https://banes-lab.com/records/lexicon/domain-alignment.md)
- [Simple CRUD](https://banes-lab.com/records/lexicon/simple-crud.md)
- [Anemic Transaction Script](https://banes-lab.com/records/lexicon/anemic-transaction-script.md)
- [Domain Events](https://banes-lab.com/records/architecture/domain-events.md)
- [Domain-Driven Design (DDD) / Simple CRUD](https://banes-lab.com/records/tension/domain-driven-design-ddd-simple-crud.md)
- [Event-Driven Architecture](https://banes-lab.com/records/architecture/event-driven-architecture.md)
- [artifact](https://banes-lab.com/records/kind/artifact.md)
- [Invariant](https://banes-lab.com/records/architecture/invariant.md)
- [Correctness](https://banes-lab.com/records/architecture/correctness.md)
- [Semantic Contracts](https://banes-lab.com/records/architecture/semantic-contracts.md)
- [Business Rule Encapsulation](https://banes-lab.com/records/lexicon/business-rule-encapsulation.md)
- [Persistence Simplicity](https://banes-lab.com/records/lexicon/persistence-simplicity.md)
- [Anemic Model](https://banes-lab.com/records/lexicon/anemic-model.md)
- [Domain-Driven Design](https://banes-lab.com/records/architecture/domain-driven-design.md)
- [Entity](https://banes-lab.com/records/architecture/entity.md)
- [Domain Service](https://banes-lab.com/records/architecture/domain-service.md)
- [Domain Model / Persistence Simplicity](https://banes-lab.com/records/tension/domain-model-persistence-simplicity.md)
- [Tests](https://banes-lab.com/records/lexicon/tests.md)
- [constraint](https://banes-lab.com/records/kind/constraint.md)
- [recommended](https://banes-lab.com/records/vocabulary/severity-recommended.md)
- [Explicit Boundaries](https://banes-lab.com/records/architecture/explicit-boundaries.md)
- [Modularity](https://banes-lab.com/records/architecture/modularity.md)
- [Autonomy](https://banes-lab.com/records/architecture/autonomy.md)
- [Context Mapping](https://banes-lab.com/records/architecture/context-mapping.md)
- [Microservices](https://banes-lab.com/records/architecture/microservices.md)
- [Cross-Context Reuse](https://banes-lab.com/records/lexicon/cross-context-reuse.md)
- [Shared Global Model](https://banes-lab.com/records/lexicon/shared-global-model.md)
- [Package by Feature](https://banes-lab.com/records/architecture/package-by-feature.md)
- [Domain Modeling](https://banes-lab.com/records/algorithms/domain-modeling.md)
- [Bounded Context / Cross-Context Reuse](https://banes-lab.com/records/tension/bounded-context-cross-context-reuse.md)
- [Relationship Semantics](https://banes-lab.com/records/lexicon/relationship-semantics.md)
- [activity](https://banes-lab.com/records/kind/activity.md)
- [Integration Clarity](https://banes-lab.com/records/lexicon/integration-clarity.md)
- [Anti-Corruption Layer](https://banes-lab.com/records/architecture/anti-corruption-layer.md)
- [Documentation Overhead](https://banes-lab.com/records/lexicon/documentation-overhead.md)
- [Implicit Integration](https://banes-lab.com/records/lexicon/implicit-integration.md)
- [Context Mapping / Documentation Overhead](https://banes-lab.com/records/tension/context-mapping-documentation-overhead.md)
- [pattern](https://banes-lab.com/records/kind/pattern.md)
- [Explicit Boundary](https://banes-lab.com/records/lexicon/explicit-boundary.md)
- [Translation Model](https://banes-lab.com/records/lexicon/translation-model.md)
- [Domain Purity](https://banes-lab.com/records/lexicon/domain-purity.md)
- [Low Coupling](https://banes-lab.com/records/architecture/low-coupling.md)
- [Legacy/System Integration](https://banes-lab.com/records/lexicon/legacy-system-integration.md)
- [Mapping Overhead](https://banes-lab.com/records/lexicon/mapping-overhead.md)
- [Vendor Lock-In Leakage](https://banes-lab.com/records/architecture/vendor-lock-in-leakage.md)
- [Adapter Pattern](https://banes-lab.com/records/architecture/adapter-pattern.md)
- [Anti-Corruption Layer Over Cross-Context Leak](https://banes-lab.com/records/algorithms/no-leaky-context.md)
- [Anti-Corruption Layer / Mapping Overhead](https://banes-lab.com/records/tension/anti-corruption-layer-mapping-overhead.md)
- [principle](https://banes-lab.com/records/kind/principle.md)
- [mandatory](https://banes-lab.com/records/vocabulary/severity-mandatory.md)
- [Stable Interfaces](https://banes-lab.com/records/architecture/stable-interfaces.md)
- [Ownership](https://banes-lab.com/records/lexicon/ownership.md)
- [Encapsulation](https://banes-lab.com/records/architecture/encapsulation.md)
- [Replaceability](https://banes-lab.com/records/architecture/replaceability.md)
- [Governance](https://banes-lab.com/records/architecture/governance.md)
- [Cross-Cutting Concerns](https://banes-lab.com/records/lexicon/cross-cutting-concerns.md)
- [Boundary Leakage](https://banes-lab.com/records/architecture/boundary-leakage.md)
- [Aggregate](https://banes-lab.com/records/architecture/aggregate.md)
- [Single Responsibility Principle](https://banes-lab.com/records/architecture/single-responsibility.md)
- [Separation of Concerns](https://banes-lab.com/records/architecture/separation-of-concerns.md)
- [High Cohesion](https://banes-lab.com/records/architecture/high-cohesion.md)
- [Independence](https://banes-lab.com/records/architecture/independence.md)
- [Declared Jurisdiction](https://banes-lab.com/records/architecture/declared-jurisdiction.md)
- [Explicit Boundaries / Cross-Cutting Concerns](https://banes-lab.com/records/tension/cross-cutting-concerns-explicit-boundaries.md)
- [Abstraction](https://banes-lab.com/records/architecture/abstraction.md)
- [Single Source of Truth](https://banes-lab.com/records/architecture/single-source-of-truth.md)
- [Circular Dependency](https://banes-lab.com/records/architecture/circular-dependency.md)
- [Transactional Consistency Boundary](https://banes-lab.com/records/lexicon/transactional-consistency-boundary.md)
- [Root-Guarded Invariants](https://banes-lab.com/records/lexicon/root-guarded-invariants.md)
- [Aggregate Size](https://banes-lab.com/records/lexicon/aggregate-size.md)
- [Anemic Domain Model](https://banes-lab.com/records/architecture/anemic-domain-model.md)
- [Aggregate / Aggregate Size](https://banes-lab.com/records/tension/aggregate-aggregate-size.md)
- [Value Equality](https://banes-lab.com/records/lexicon/value-equality.md)
- [Immutability](https://banes-lab.com/records/architecture/immutability.md)
- [Self-Validating Values](https://banes-lab.com/records/lexicon/self-validating-values.md)
- [Side-Effect-Free Equality](https://banes-lab.com/records/lexicon/side-effect-free-equality.md)
- [Object Count](https://banes-lab.com/records/lexicon/object-count.md)
- [Primitive Obsession](https://banes-lab.com/records/architecture/primitive-obsession.md)
- [Data Clumps](https://banes-lab.com/records/architecture/data-clumps.md)
- [Long Parameter List](https://banes-lab.com/records/architecture/long-parameter-list.md)
- [Value Object / Object Count](https://banes-lab.com/records/tension/object-count-value-object.md)
- [Stable Identity](https://banes-lab.com/records/lexicon/stable-identity.md)
- [Identity-Based Equality](https://banes-lab.com/records/lexicon/identity-based-equality.md)
- [Lifecycle Tracking](https://banes-lab.com/records/lexicon/lifecycle-tracking.md)
- [Value Object](https://banes-lab.com/records/architecture/value-object.md)
- [Entity / Value Object](https://banes-lab.com/records/tension/entity-value-object.md)
- [Cross-Entity Domain Logic](https://banes-lab.com/records/lexicon/cross-entity-domain-logic.md)
- [Fat Controller](https://banes-lab.com/records/architecture/fat-controller.md)
- [Transaction Script Sprawl](https://banes-lab.com/records/architecture/transaction-script-sprawl.md)
- [Domain Service / Aggregate](https://banes-lab.com/records/tension/aggregate-domain-service.md)

## Linked from

- [The layer topology](https://banes-lab.com/ontology/schema/the-layer-topology.md)
- [The membership](https://banes-lab.com/ontology/schema/the-membership.md)
