configuration/principle/data/architecture.domain.data.json
configuration/principle/data/architecture.domain.data.json is a file in GovLab Context. 472 lines of code and 0 definitions.
{
"category": "Domain Architecture",
"check": {
"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: the catalog states this check as a class, so a watched run belongs to each system that adopts it",
"authority": "the domain model, which decides the context and the aggregate every rule belongs to"
},
"records": [
{
"id": "domain-driven-design",
"distinctFrom": [
{
"id": "architecture:event-driven-architecture",
"reason": "Domain-driven design models software on the domain's language, while event-driven architecture connects services through published events."
}
],
"aliases": ["DDD"],
"name": "Domain-Driven Design (DDD)",
"definition": "A convention of modeling software on the business domain's own language, split into bounded contexts with a domain model in each.",
"type": "style",
"scope": [
"domain",
"bounded context",
"system"
],
"requires": [
"Ubiquitous Language",
"Bounded Context"
],
"reinforces": [
"Domain Model",
"Semantic Consistency"
],
"enables": ["Domain Alignment"],
"conflicts_with": ["Anemic Transaction Script"],
"tensions_with": ["Simple CRUD"],
"violated_by": [
"architecture:anemic-domain-model",
"architecture:fat-controller"
],
"detected_by": [
"anemic models",
"scattered business rules"
],
"measured_by": ["domain logic locality"],
"refactored_by": [
"lexicon:extract-domain-model",
"architecture:aggregate",
"lexicon:split-bounded-context"
],
"enforced_by": [
"layer rules",
"domain tests"
],
"severity": "contextual",
"exemplar": {
"before": "function updateFoo(row: FooRow, name: string) {\n row.name = name;\n row.updated_at = Date.now();\n return fooTable.save(row);\n}",
"after": "class Foo {\n private constructor(readonly id: FooId, private name: string) {}\n rename(name: FooName) { this.name = name.value; }\n}\nfoo.rename(FooName.create(name));",
"lang": "ts"
}
},
{
"id": "domain-model",
"name": "Domain Model",
"definition": "A formal definition of the business concepts, rules and invariants of one context, written as types with behavior.",
"type": "artifact",
"scope": [
"domain",
"bounded context"
],
"requires": [
"Ubiquitous Language",
"Invariant"
],
"reinforces": [
"Correctness",
"Semantic Contracts"
],
"enables": ["Business Rule Encapsulation"],
"conflicts_with": ["Anemic Model"],
"tensions_with": ["Persistence Simplicity"],
"violated_by": ["architecture:anemic-domain-model"],
"detected_by": ["procedural domain logic in services/controllers"],
"measured_by": [
"rule locality",
"invariant coverage"
],
"refactored_by": [
"lexicon:move-logic-to-the-domain",
"architecture:value-object",
"architecture:aggregate"
],
"enforced_by": [
"domain layer rules",
"tests"
],
"severity": "contextual",
"exemplar": {
"before": "type Foo = { status: string; count: number };\nfunction closeFoo(foo: Foo) { foo.status = \"closed\"; }",
"after": "class Foo {\n #status: \"open\" | \"closed\" = \"open\";\n close() {\n if (this.#status === \"closed\") throw new Error(\"Foo already closed\");\n this.#status = \"closed\";\n }\n}",
"lang": "ts"
}
},
{
"id": "bounded-context",
"distinctFrom": [
{
"id": "lexicon:relationship-semantics",
"reason": "A bounded context fixes meaning inside one boundary, while relationship semantics fixes the meaning of each link between boundaries."
}
],
"name": "Bounded Context",
"definition": "A rule or precondition that each domain model holds inside one explicit boundary, where its terms have one meaning.",
"type": "constraint",
"scope": [
"domain",
"service",
"team"
],
"requires": [
"Explicit Boundaries",
"Ubiquitous Language"
],
"reinforces": [
"Modularity",
"Autonomy"
],
"enables": [
"Context Mapping",
"Microservices"
],
"conflicts_with": ["Shared Global Model"],
"tensions_with": ["Cross-Context Reuse"],
"violated_by": ["lexicon:shared-global-model"],
"detected_by": ["shared domain entities across contexts"],
"measured_by": ["context coupling"],
"refactored_by": [
"lexicon:split-bounded-context",
"architecture:anti-corruption-layer",
"architecture:context-mapping"
],
"enforced_by": ["package/service boundaries"],
"severity": "recommended",
"exemplar": {
"before": "type FooStatus = \"A\" | \"D\";\nfunction priceBar(status: FooStatus) { return status === \"A\" ? 10 : 0; }",
"after": "type FooStatus = \"active\" | \"disabled\";\ntype BarEligibility = \"eligible\" | \"ineligible\";\nfunction toBarEligibility(status: FooStatus): BarEligibility {\n return status === \"active\" ? \"eligible\" : \"ineligible\";\n}",
"lang": "ts"
}
},
{
"id": "context-mapping",
"name": "Context Mapping",
"definition": "The activity of recording how bounded contexts relate, including which one is upstream and how their models translate.",
"type": "activity",
"scope": [
"bounded contexts",
"integration"
],
"requires": [
"Bounded Context",
"Relationship Semantics"
],
"reinforces": [
"Explicit Boundaries",
"Integration Clarity"
],
"enables": ["Anti-Corruption Layer"],
"conflicts_with": ["Implicit Integration"],
"tensions_with": ["Documentation Overhead"],
"violated_by": ["lexicon:implicit-integration"],
"detected_by": [
"unclear ownership",
"ambiguous integration flows"
],
"measured_by": ["undocumented dependency count"],
"refactored_by": [],
"enforced_by": [
"architecture docs",
"dependency reviews"
],
"severity": "recommended",
"exemplar": {
"before": "fooService.writeDirectly(barDatabase, foo);\nbarService.readDirectly(fooDatabase, foo.id);",
"after": "const contextMap = {\n upstream: \"FooContext\",\n downstream: \"BarContext\",\n relationship: \"published-language\",\n} as const;\nfooEvents.publish(toBarIntegrationEvent(foo));",
"lang": "ts"
}
},
{
"id": "anti-corruption-layer",
"name": "Anti-Corruption Layer",
"definition": "A design pattern that translates an external system's model into the local domain's terms at the boundary.",
"type": "pattern",
"scope": [
"integration",
"bounded context boundary"
],
"requires": [
"Explicit Boundary",
"Translation Model"
],
"reinforces": [
"Domain Purity",
"Low Coupling"
],
"enables": ["Legacy/System Integration"],
"conflicts_with": [
"Shared Model Coupling",
"Vendor Lock-In Leakage"
],
"tensions_with": ["Mapping Overhead"],
"violated_by": ["lexicon:direct-external-coupling"],
"detected_by": ["external DTOs used in domain layer"],
"measured_by": [
"leakage count",
"adapter coverage"
],
"refactored_by": [
"lexicon:extract-adapter",
"lexicon:introduce-boundary-dto"
],
"enforced_by": [
"import rules",
"layer tests"
],
"severity": "recommended",
"exemplar": {
"before": "function createBar(fooResponse: FooApiResponse) {\n return barService.create({ foo_status: fooResponse.state_code });\n}",
"after": "type BarInput = { eligible: boolean };\nfunction fromFoo(response: FooApiResponse): BarInput {\n return { eligible: response.state_code === \"A\" };\n}\nbarService.create(fromFoo(fooResponse));",
"lang": "ts"
}
},
{
"id": "explicit-boundaries",
"distinctFrom": [
{
"id": "architecture:declared-jurisdiction",
"reason": "Explicit boundaries declare a module's interface and owner, while declared jurisdiction declares which roots and files a naming gate governs."
},
{
"id": "architecture:abstraction",
"reason": "Explicit boundaries fix where one module ends, while abstraction fixes how a concept is expressed without its detail."
},
{
"id": "architecture:encapsulation",
"reason": "Explicit boundaries govern access between modules, while encapsulation governs changes to one object's state."
},
{
"id": "architecture:governance",
"reason": "Explicit boundaries are the declared interfaces, while governance holds decisions to shared policy."
},
{
"id": "architecture:single-source-of-truth",
"reason": "Explicit boundaries give each module one public interface, while a single source of truth gives each fact one owning definition."
},
{
"id": "architecture:independence",
"reason": "Explicit boundaries declare the interface, while independence is testing and deploying a module without its neighbors."
},
{
"id": "architecture:modularity",
"reason": "Explicit boundaries declare each module's interface and owner, while modularity is the division into modules itself."
},
{
"id": "architecture:separation-of-concerns",
"reason": "Explicit boundaries declare interfaces, while separation of concerns decides which concerns sit on each side of them."
},
{
"id": "architecture:single-responsibility",
"reason": "Explicit boundaries declare interfaces, while single responsibility limits what sits behind one to one reason to change."
}
],
"name": "Explicit Boundaries",
"definition": "A design rule that each module declares its public interface and owner, and other modules reach it only through that interface.",
"type": "principle",
"scope": [
"module",
"component",
"service",
"domain"
],
"requires": [
"Stable Interfaces",
"Ownership"
],
"reinforces": [
"Modularity",
"Encapsulation"
],
"enables": [
"Replaceability",
"Governance"
],
"conflicts_with": ["Boundary Leakage"],
"tensions_with": ["Cross-Cutting Concerns"],
"violated_by": [
"architecture:boundary-leakage",
"architecture:inappropriate-intimacy"
],
"detected_by": [
"forbidden imports",
"cyclic dependencies"
],
"measured_by": ["boundary violation count"],
"refactored_by": [
"lexicon:move-behavior-to-its-owner",
"lexicon:define-contract",
"lexicon:restrict-exports"
],
"enforced_by": [
"module rules",
"architecture tests"
],
"severity": "mandatory",
"exemplar": {
"before": "import { fooDatabase } from \"../../foo/infrastructure/database\";\nexport function loadBar(id: string) { return fooDatabase.query(id); }",
"after": "export interface FooGateway { find(id: FooId): Promise<FooSnapshot>; }\nexport function loadBar(id: FooId, foos: FooGateway) { return foos.find(id); }",
"lang": "ts"
}
},
{
"id": "aggregate",
"name": "Aggregate",
"definition": "A design pattern that groups entities under one root, which is the only entry point and keeps the group's invariants.",
"type": "pattern",
"scope": [
"domain",
"consistency boundary",
"bounded context"
],
"requires": ["Explicit Boundaries"],
"reinforces": [
"Invariant",
"Encapsulation"
],
"enables": [
"Transactional Consistency Boundary",
"Root-Guarded Invariants"
],
"conflicts_with": ["Anemic Domain Model"],
"tensions_with": ["Aggregate Size"],
"violated_by": ["architecture:anemic-domain-model"],
"detected_by": ["cross-entity invariant checks scattered in services"],
"measured_by": ["out-of-aggregate invariant enforcement count"],
"refactored_by": ["lexicon:add-aggregate-invariant"],
"enforced_by": ["domain model review"],
"severity": "contextual",
"exemplar": {
"before": "fooOrder.total -= item.price;\nfooOrderItems.delete(item.id);",
"after": "class FooOrder {\n #items: FooItem[] = [];\n #total = 0;\n removeItem(id: FooItemId) {\n this.#items = this.#items.filter(item => item.id !== id);\n this.#total = this.#items.reduce((sum, item) => sum + item.price, 0);\n }\n}",
"lang": "ts"
}
},
{
"id": "value-object",
"name": "Value Object",
"definition": "A design pattern that models a domain value as an immutable, self-validating object compared by its attributes.",
"type": "pattern",
"scope": [
"domain",
"modeling",
"immutability"
],
"requires": ["Value Equality"],
"reinforces": [
"Immutability",
"Encapsulation"
],
"enables": [
"Self-Validating Values",
"Side-Effect-Free Equality"
],
"conflicts_with": [
"Primitive Obsession",
"Data Clumps",
"Long Parameter List"
],
"tensions_with": ["Object Count"],
"violated_by": ["architecture:primitive-obsession"],
"detected_by": ["repeated validation of the same primitive shape"],
"measured_by": ["primitive-typed domain concept count"],
"refactored_by": [],
"enforced_by": ["domain model review"],
"severity": "recommended",
"exemplar": {
"before": "function priceFoo(amount: number, currency: string) { return { amount, currency }; }",
"after": "class Money {\n private constructor(readonly amount: number, readonly currency: string) {}\n static of(amount: number, currency: string): Money {\n if (amount < 0) throw new Error(\"negative money\");\n return new Money(amount, currency);\n }\n equals(other: Money) { return this.amount === other.amount && this.currency === other.currency; }\n add(other: Money): Money { return Money.of(this.amount + other.amount, this.currency); }\n}",
"lang": "ts"
}
},
{
"id": "entity",
"distinctFrom": [
{
"id": "architecture:value-object",
"reason": "An entity is known by an identity that outlives its attributes, while a value object is known only by its attributes."
}
],
"name": "Entity",
"definition": "A design pattern that models a domain object by a stable identity, which stays the same while its attributes change.",
"type": "pattern",
"scope": [
"domain",
"identity",
"lifecycle"
],
"requires": ["Stable Identity"],
"reinforces": [
"Domain Model",
"Encapsulation"
],
"enables": [
"Identity-Based Equality",
"Lifecycle Tracking"
],
"conflicts_with": ["Anemic Domain Model"],
"tensions_with": ["Value Object"],
"violated_by": ["lexicon:attribute-compared-identity"],
"detected_by": ["equality by field value where identity is meant"],
"measured_by": ["attribute-equality misuse count"],
"refactored_by": [],
"enforced_by": ["domain model review"],
"severity": "contextual",
"exemplar": {
"before": "type Foo = { id: string; name: string; status: string };\nfoo.status = \"active\";",
"after": "class Foo {\n constructor(readonly id: FooId, private name: string, private status: FooStatus) {}\n equals(other: Foo) { return this.id === other.id; }\n activate() { this.status = \"active\"; }\n}",
"lang": "ts"
}
},
{
"id": "domain-service",
"distinctFrom": [
{
"id": "architecture:aggregate",
"reason": "A domain service holds a stateless rule spanning entities, while an aggregate holds entities and their invariants under one root."
}
],
"name": "Domain Service",
"definition": "A design pattern that places a domain rule spanning several entities in a stateless service named in the domain's language.",
"type": "pattern",
"scope": [
"domain",
"behavior",
"coordination"
],
"requires": ["Domain Model"],
"reinforces": [
"Single Responsibility Principle (SRP)",
"Ubiquitous Language"
],
"enables": ["Cross-Entity Domain Logic"],
"conflicts_with": [
"Fat Controller",
"Transaction Script Sprawl"
],
"tensions_with": ["Aggregate"],
"violated_by": ["architecture:fat-controller"],
"detected_by": ["domain logic in application/transport layers"],
"measured_by": ["misplaced domain-rule count"],
"refactored_by": [],
"enforced_by": ["domain model review"],
"severity": "contextual",
"exemplar": {
"before": "class FooAccount {\n transferTo(other: FooAccount, amount: number) {\n this.balance -= amount;\n other.balance += amount;\n }\n}",
"after": "class FooTransferService {\n transfer(from: FooAccount, to: FooAccount, amount: Money) {\n from.withdraw(amount);\n to.deposit(amount);\n }\n}",
"lang": "ts"
}
}
]
}