configuration/principle/data/design.modular.data.json
configuration/principle/data/design.modular.data.json is a file in GovLab Context. 1109 lines of code and 0 definitions.
{
"category": "Core Modular Design",
"check": {
"population": "every class, module and package boundary in the codebase",
"freshness": "a verdict stands until the module's contents, exports or imports change",
"refusal": "the dependency, visibility or clone rule fails the build on the change that breaks the property",
"observation": "cohesion, coupling, duplication and export metrics read from source and the import graph",
"evidence": "none: the catalog states this check as a class, so a watched run belongs to each system that adopts it",
"authority": "the declared module boundary, which every import and export conforms to"
},
"records": [
{
"id": "single-responsibility",
"distinctFrom": [
{
"id": "architecture:interface-segregation",
"reason": "Single responsibility limits a module's reasons to change, while interface segregation limits what a client depends on."
},
{
"id": "architecture:open-closed",
"reason": "Single responsibility limits reasons to change, while the open/closed principle governs adding behavior without editing."
},
{
"id": "architecture:composability",
"reason": "Single responsibility limits what one part does, while composability governs how parts combine."
},
{
"id": "architecture:separation-of-concerns",
"reason": "Single responsibility is one reason to change per class or module, while separation of concerns separates the named concerns of a system."
}
],
"aliases": [
"SRP",
"Single Responsibility Principle"
],
"name": "Single Responsibility Principle (SRP)",
"definition": "A design rule that a class or module has one reason to change, because it serves one responsibility.",
"type": "principle",
"scope": [
"class",
"module",
"service"
],
"requires": [
"High Cohesion",
"Explicit Boundaries"
],
"reinforces": [
"Separation of Concerns",
"Modularity",
"Testability"
],
"enables": [
"Replaceability",
"Reusability"
],
"conflicts_with": [
"God Object",
"Blob Class",
"Divergent Change"
],
"tensions_with": ["Excessive Fragmentation"],
"violated_by": [
"architecture:god-object",
"architecture:divergent-change"
],
"detected_by": [
"high fan-in/fan-out",
"unrelated methods",
"unrelated dependencies"
],
"measured_by": [
"cohesion score",
"responsibility count",
"change-coupling"
],
"refactored_by": [
"lexicon:extract-class",
"lexicon:extract-module",
"lexicon:split-bounded-context",
"lexicon:move-behavior-to-its-owner"
],
"enforced_by": [
"architecture tests",
"package boundaries",
"static analysis"
],
"severity": "mandatory",
"exemplar": {
"before": "class FooService {\n save(foo: Foo) { fooDb.insert(foo); }\n send(foo: Foo) { fooMail.send(foo); }\n report(foo: Foo) { return `${foo.id}:${foo.name}`; }\n}",
"after": "class FooRepository { save(foo: Foo) { return fooDb.insert(foo); } }\nclass FooNotifier { send(foo: Foo) { return fooMail.send(foo); } }\nclass FooReporter { report(foo: Foo) { return `${foo.id}:${foo.name}`; } }",
"lang": "ts"
}
},
{
"id": "separation-of-concerns",
"distinctFrom": [
{
"id": "architecture:one-concern-per-file",
"reason": "Separation of concerns splits code by responsibility, while one concern per file binds each file to one declared role tag."
},
{
"id": "architecture:open-closed",
"reason": "Separation of concerns decides where concerns live, while the open/closed principle decides how behavior is added."
},
{
"id": "architecture:composability",
"reason": "Separation of concerns keeps concerns apart, while composability lets parts combine."
}
],
"aliases": ["SoC"],
"name": "Separation of Concerns",
"definition": "A design rule that parsing, business logic, persistence and presentation each live in their own part of the code.",
"canon": ["separation-of-concerns"],
"type": "principle",
"scope": [
"module",
"package",
"component",
"system"
],
"requires": [
"Explicit Boundaries",
"Abstraction"
],
"reinforces": [
"SRP",
"Modularity",
"Layered Architecture"
],
"enables": [
"Maintainability",
"Replaceability"
],
"conflicts_with": [
"Cross-Cutting Leakage",
"Mixed Layers"
],
"tensions_with": ["Over-Layering"],
"violated_by": ["lexicon:mixed-layers"],
"detected_by": [
"layer imports",
"mixed naming roles",
"cross-boundary logic"
],
"measured_by": [
"dependency direction",
"layer purity",
"concern overlap"
],
"refactored_by": [
"lexicon:extract-module",
"lexicon:move-behavior-to-its-owner",
"lexicon:define-module-boundaries"
],
"enforced_by": [
"import rules",
"dependency graph checks"
],
"severity": "mandatory",
"exemplar": {
"before": "function handleFoo(request: Request) {\n const foo = JSON.parse(request.body);\n fooDb.insert(foo);\n return `<div>${foo.name}</div>`;\n}",
"after": "function parseFoo(request: Request): Foo { return decodeFoo(request.body); }\nfunction saveFoo(foo: Foo) { return fooDb.insert(foo); }\nfunction renderFoo(foo: Foo) { return `<div>${foo.name}</div>`; }",
"lang": "ts"
}
},
{
"id": "duplicate-code",
"aliases": [
"DRY",
"Do Not Repeat Yourself",
"Don't Repeat Yourself"
],
"name": "Do Not Repeat Yourself (DRY)",
"definition": "A design rule that each piece of logic, constant or schema has one source in the code, and every other use refers to it.",
"canon": ["duplicate-code"],
"type": "principle",
"scope": [
"function",
"module",
"domain"
],
"requires": [
"Abstraction",
"Canonical Source"
],
"reinforces": [
"Single Source of Truth",
"Reusability"
],
"enables": [
"Consistency",
"Maintainability"
],
"conflicts_with": ["Copy-Paste Programming"],
"tensions_with": [
"Locality of Behavior",
"Simplicity"
],
"violated_by": ["lexicon:copy-paste-programming"],
"detected_by": [
"clone detection",
"duplicated branches",
"repeated literals"
],
"measured_by": [
"duplication percentage",
"clone count"
],
"refactored_by": [
"lexicon:extract-function",
"lexicon:extract-module",
"lexicon:introduce-parameter-object",
"lexicon:centralize-the-rule"
],
"enforced_by": [
"clone analyzers",
"lint rules",
"review gates"
],
"severity": "mandatory",
"exemplar": {
"before": "function validateFoo(foo: Foo) {\n if (!foo.name || foo.name.length > 40) throw new Error(\"invalid name\");\n}\nfunction validateBar(bar: Bar) {\n if (!bar.name || bar.name.length > 40) throw new Error(\"invalid name\");\n}",
"after": "function validateName(name: string) {\n if (!name || name.length > 40) throw new Error(\"invalid name\");\n}\nfunction validateFoo(foo: Foo) { validateName(foo.name); }\nfunction validateBar(bar: Bar) { validateName(bar.name); }",
"lang": "ts"
}
},
{
"id": "high-cohesion",
"distinctFrom": [
{
"id": "lexicon:over-specialization",
"reason": "High cohesion is members sharing one purpose, while over-specialization is the reuse lost by narrowing that purpose too far."
}
],
"name": "High Cohesion",
"definition": "The degree to which the members of a class or module work on the same data towards the same purpose.",
"type": "quality-attribute",
"scope": [
"class",
"module",
"component"
],
"requires": [
"SRP",
"Explicit Boundaries"
],
"reinforces": [
"Modularity",
"Encapsulation"
],
"enables": [
"Testability",
"Replaceability"
],
"conflicts_with": [
"God Object",
"Utility Dump",
"Shotgun Surgery"
],
"tensions_with": ["Over-Specialization"],
"violated_by": ["architecture:divergent-change"],
"detected_by": [
"low LCOM",
"scattered dependencies",
"unrelated public API"
],
"measured_by": [
"cohesion metrics",
"change locality"
],
"refactored_by": [
"lexicon:extract-class",
"lexicon:split-module",
"lexicon:move-behavior-to-its-owner"
],
"enforced_by": [
"module ownership",
"architecture review"
],
"severity": "mandatory",
"exemplar": {
"before": "class FooManager {\n saveFoo(foo: Foo) { return fooDb.insert(foo); }\n resizeImage(image: Image) { return image.resize(100, 100); }\n parseBar(raw: string) { return JSON.parse(raw) as Bar; }\n}",
"after": "class FooRepository {\n save(foo: Foo) { return fooDb.insert(foo); }\n load(id: FooId) { return fooDb.find(id); }\n remove(id: FooId) { return fooDb.delete(id); }\n}\nclass ImageResizer { resize(image: Image) { return image.resize(100, 100); } }\nclass BarParser { parse(raw: string) { return JSON.parse(raw) as Bar; } }",
"lang": "ts"
}
},
{
"id": "low-coupling",
"distinctFrom": [
{
"id": "architecture:high-cohesion",
"reason": "Low coupling is about the dependencies between modules, while high cohesion is about the purpose inside one."
},
{
"id": "architecture:portability",
"reason": "Low coupling limits how far a change ripples between modules, while portability is running on another platform unchanged."
},
{
"id": "architecture:replaceability",
"reason": "Low coupling limits how far a change spreads, while replaceability is swapping an implementation without editing its callers."
},
{
"id": "architecture:stable-interfaces",
"reason": "Low coupling is few forced changes between modules, while stable interfaces are signatures that keep across releases."
},
{
"id": "architecture:testability",
"reason": "Low coupling concerns change between modules, while testability is running code in isolation with supplied dependencies."
},
{
"id": "lexicon:domain-purity",
"reason": "Low coupling applies between any modules, while domain purity keeps the domain model free of infrastructure concerns."
}
],
"aliases": [
"Loose Coupling",
"Decoupling"
],
"name": "Low Coupling",
"definition": "The degree to which a module can change without forcing changes in the modules around it.",
"type": "quality-attribute",
"scope": [
"module",
"component",
"service"
],
"requires": [
"Abstraction",
"Stable Interfaces"
],
"reinforces": [
"Modularity",
"Replaceability",
"Portability"
],
"enables": [
"Independent Deployment",
"Test Isolation"
],
"conflicts_with": [
"Tight Coupling",
"Cyclic Dependencies",
"Inappropriate Intimacy",
"Message Chain"
],
"tensions_with": ["Runtime Indirection"],
"violated_by": [
"lexicon:tight-coupling",
"architecture:circular-dependency"
],
"detected_by": [
"dependency cycles",
"high afferent/efferent coupling"
],
"measured_by": [
"coupling metrics",
"dependency graph density"
],
"refactored_by": [
"lexicon:extract-interface",
"architecture:dependency-injection",
"lexicon:extract-adapter"
],
"enforced_by": [
"dependency rules",
"architecture fitness tests"
],
"severity": "mandatory",
"exemplar": {
"before": "class FooService {\n save(foo: Foo) {\n const db = new SqlDatabase(\"foo-prod\");\n barIndex.update(foo);\n return db.table(\"foos\").insert(foo);\n }\n}",
"after": "class FooService {\n constructor(private readonly events: EventSink) {}\n save(foo: Foo) { return this.events.emit({ type: \"FooSaved\", foo }); }\n}",
"lang": "ts"
}
},
{
"id": "encapsulation",
"distinctFrom": [
{
"id": "architecture:abstraction",
"reason": "Encapsulation guards an object's state behind its operations, while abstraction expresses what a thing does without how."
},
{
"id": "architecture:information-hiding",
"reason": "Encapsulation protects state and its invariants, while information hiding keeps implementation decisions private behind an interface."
},
{
"id": "architecture:single-responsibility",
"reason": "Encapsulation guards state, while single responsibility limits a module to one reason to change."
},
{
"id": "architecture:immutability",
"reason": "Encapsulation controls how state changes, while immutability forbids change after creation."
},
{
"id": "architecture:open-closed",
"reason": "Encapsulation protects existing state, while the open/closed principle governs how new behavior is added."
},
{
"id": "architecture:state-isolation",
"reason": "Encapsulation limits writes to an object's own operations, while state isolation limits writes to one unit of concurrent execution."
}
],
"name": "Encapsulation",
"definition": "A design rule that an object's state is changed only through its own operations, which keep its invariants.",
"canon": ["encapsulation"],
"type": "principle",
"scope": [
"class",
"module",
"component"
],
"requires": [
"Information Hiding",
"Stable Interfaces"
],
"reinforces": [
"Abstraction",
"Low Coupling"
],
"enables": [
"Change Isolation",
"Invariant Protection"
],
"conflicts_with": [
"Exposed Internals",
"Anemic Encapsulation",
"Feature Envy"
],
"tensions_with": ["Debuggability"],
"violated_by": [
"lexicon:exposed-internals",
"architecture:shared-mutable-state"
],
"detected_by": [
"public state",
"excessive setters",
"external invariant manipulation"
],
"measured_by": [
"public surface area",
"mutation exposure"
],
"refactored_by": [
"lexicon:encapsulate-state",
"lexicon:extract-function",
"lexicon:restrict-exports"
],
"enforced_by": [
"visibility rules",
"linting",
"API review"
],
"severity": "mandatory",
"exemplar": {
"before": "class FooCounter {\n count = 0;\n}\nconst counter = new FooCounter();\ncounter.count = -100;",
"after": "class FooCounter {\n #count = 0;\n increment() { this.#count += 1; }\n value() { return this.#count; }\n}",
"lang": "ts"
}
},
{
"id": "information-hiding",
"name": "Information Hiding",
"definition": "A design rule that a module exposes an interface and keeps the implementation decisions behind it private.",
"type": "principle",
"scope": [
"class",
"module",
"package"
],
"requires": [
"Encapsulation",
"Explicit Interfaces"
],
"reinforces": [
"Low Coupling",
"Replaceability"
],
"enables": ["Internal Refactoring"],
"conflicts_with": ["Leaky Abstraction"],
"tensions_with": ["Observability"],
"violated_by": [
"lexicon:leaky-abstraction",
"lexicon:exposed-internals"
],
"detected_by": [
"internal packages imported externally",
"exposed persistence models"
],
"measured_by": [
"internal API exposure",
"dependency leakage"
],
"refactored_by": [
"architecture:facade-pattern",
"lexicon:restrict-exports"
],
"enforced_by": [
"package visibility",
"module export rules"
],
"severity": "mandatory",
"exemplar": {
"before": "class FooStore {\n public readonly rows = new Map<string, Foo>();\n}\nfooStore.rows.set(foo.id, foo);",
"after": "interface FooStore {\n save(foo: Foo): void;\n find(id: string): Foo | undefined;\n}\nclass MapFooStore implements FooStore {\n #rows = new Map<string, Foo>();\n save(foo: Foo) { this.#rows.set(foo.id, foo); }\n find(id: string) { return this.#rows.get(id); }\n}",
"lang": "ts"
}
},
{
"id": "abstraction",
"distinctFrom": [
{
"id": "architecture:dependency-inversion",
"reason": "Abstraction expresses a concept by what it does, while dependency inversion fixes that the high-level policy owns that expression."
},
{
"id": "architecture:duplicate-code",
"reason": "Abstraction hides how a thing works behind what it does, while DRY keeps one source for each piece of logic."
},
{
"id": "architecture:interface-based-design",
"reason": "Abstraction is the general rule, while interface-based design applies it to module dependencies supplied from outside."
},
{
"id": "architecture:inversion-of-control",
"reason": "Abstraction hides detail, while inversion of control moves object creation and control flow to a framework or composition root."
},
{
"id": "architecture:open-closed",
"reason": "Abstraction hides detail, while the open/closed principle adds behavior at an extension point without editing existing code."
},
{
"id": "architecture:separation-of-concerns",
"reason": "Abstraction separates what from how, while separation of concerns separates parsing, logic, persistence and presentation."
}
],
"name": "Abstraction",
"definition": "A design rule that a concept is expressed by what it does for the code that uses it, and the detail of how it does it stays out of that expression.",
"type": "principle",
"scope": [
"class",
"module",
"service",
"system"
],
"requires": [
"Stable Semantics",
"Interface Definition"
],
"reinforces": [
"DIP",
"Low Coupling",
"Portability"
],
"enables": [
"Polymorphism",
"Replaceability"
],
"conflicts_with": [
"Concrete Coupling",
"Middle Man"
],
"tensions_with": ["Simplicity"],
"violated_by": [
"lexicon:leaky-abstraction",
"architecture:concrete-coupling"
],
"detected_by": ["concrete type usage across boundaries"],
"measured_by": [
"abstraction ratio",
"interface stability"
],
"refactored_by": [
"lexicon:extract-interface",
"lexicon:introduce-port",
"lexicon:invert-dependency"
],
"enforced_by": [
"architecture tests",
"dependency inversion rules"
],
"severity": "mandatory",
"exemplar": {
"before": "function saveFoo(foo: Foo) {\n return sqlClient.query(\"insert into foos(id,name) values($1,$2)\", [foo.id, foo.name]);\n}",
"after": "interface FooRepository { save(foo: Foo): Promise<void>; }\nclass SqlFooRepository implements FooRepository {\n save(foo: Foo) { return sqlClient.query(\"insert into foos(id,name) values($1,$2)\", [foo.id, foo.name]); }\n}\nclass HttpFooRepository implements FooRepository {\n save(foo: Foo) { return http.post(\"/foos\", foo); }\n}\nfunction saveFoo(foo: Foo, repository: FooRepository) { return repository.save(foo); }",
"lang": "ts"
}
},
{
"id": "modularity",
"distinctFrom": [
{
"id": "architecture:autonomy",
"reason": "Modularity divides a system into cohesive parts, while autonomy gives each owner its own data and decisions."
},
{
"id": "architecture:composability",
"reason": "Modularity divides a system, while composability lets its parts combine."
},
{
"id": "architecture:encapsulation",
"reason": "Modularity is division into modules, while encapsulation protects one object's state."
},
{
"id": "architecture:open-closed",
"reason": "Modularity divides the system, while the open/closed principle governs adding behavior at an extension point."
},
{
"id": "architecture:package-by-feature",
"reason": "Modularity is the rule to divide, while package by feature chooses the axis of division, the feature."
},
{
"id": "architecture:separation-of-concerns",
"reason": "Modularity divides into cohesive modules, while separation of concerns names which concerns must not share a part."
},
{
"id": "architecture:single-responsibility",
"reason": "Modularity is the division of a system, while single responsibility limits one class or module to one reason to change."
}
],
"name": "Modularity",
"definition": "A design rule that a system is divided into cohesive modules with explicit boundaries and few dependencies between them.",
"type": "principle",
"scope": [
"package",
"component",
"service",
"system"
],
"requires": [
"High Cohesion",
"Low Coupling",
"Explicit Boundaries"
],
"reinforces": [
"Separation of Concerns",
"Composability"
],
"enables": [
"Replaceability",
"Plugin Architecture"
],
"conflicts_with": ["Big Ball of Mud"],
"tensions_with": ["Cross-Cutting Concerns"],
"violated_by": [
"architecture:circular-dependency",
"architecture:shared-mutable-state",
"architecture:boundary-leakage"
],
"detected_by": [
"dependency cycles",
"unstable module graph"
],
"measured_by": [
"modularity score",
"graph density",
"instability"
],
"refactored_by": [
"lexicon:split-module",
"lexicon:define-module-boundaries",
"lexicon:invert-dependency"
],
"enforced_by": [
"module rules",
"package ownership",
"fitness functions"
],
"severity": "mandatory",
"exemplar": {
"before": "class FooApplication {\n parse(raw: string) { return JSON.parse(raw) as Foo; }\n save(foo: Foo) { return fooDb.insert(foo); }\n publish(foo: Foo) { return fooBus.emit(foo); }\n}",
"after": "export const fooParser = { parse: (raw: string) => decodeFoo(raw) };\nexport const fooRepository = { save: (foo: Foo) => fooDb.insert(foo) };\nexport const fooPublisher = { publish: (foo: Foo) => fooBus.emit(foo) };",
"lang": "ts"
}
},
{
"id": "composability",
"distinctFrom": [
{
"id": "architecture:duplicate-code",
"reason": "Composability lets parts combine through compatible interfaces, while DRY keeps one source for each piece of logic."
}
],
"name": "Composability",
"definition": "A design rule that parts share compatible interfaces and carry no hidden side effects, so they can be combined into larger parts.",
"type": "principle",
"scope": [
"function",
"component",
"system"
],
"requires": [
"Stable Interfaces",
"Low Coupling"
],
"reinforces": [
"Modularity",
"Reusability"
],
"enables": [
"Pipeline Architecture",
"Plugin Architecture"
],
"conflicts_with": ["Monolithic Procedures"],
"tensions_with": ["Performance Overhead"],
"violated_by": [
"architecture:hidden-side-effect",
"lexicon:implementation-specific-contracts"
],
"detected_by": [
"non-chainable APIs",
"incompatible contracts"
],
"measured_by": [
"composition count",
"interface compatibility"
],
"refactored_by": [
"lexicon:standardize-the-interface",
"lexicon:extract-module",
"architecture:adapter-pattern"
],
"enforced_by": [
"contract tests",
"type checks"
],
"severity": "recommended",
"exemplar": {
"before": "function processFoo(raw: string) {\n const foo = JSON.parse(raw) as Foo;\n const normalized = { ...foo, name: foo.name.trim() };\n return fooDb.insert(normalized);\n}",
"after": "const parseFoo = (raw: string): Foo => decodeFoo(raw);\nconst normalizeFoo = (foo: Foo): Foo => ({ ...foo, name: foo.name.trim() });\nconst saveFoo = (foo: Foo) => fooDb.insert(foo);\nconst processFoo = flow(parseFoo, normalizeFoo, saveFoo);",
"lang": "ts"
}
},
{
"id": "composition-over-inheritance",
"name": "Composition Over Inheritance",
"aliases": ["Composite Reuse Principle"],
"definition": "A design rule that behavior is reused by holding and delegating to another object instead of extending its class.",
"type": "principle",
"scope": [
"class",
"component"
],
"requires": [
"Delegation",
"Interface-Based Design"
],
"reinforces": [
"Low Coupling",
"Replaceability"
],
"enables": [
"Strategy Pattern",
"Decorator Pattern"
],
"conflicts_with": ["Deep Inheritance Hierarchy"],
"tensions_with": ["Simplicity for Trivial Reuse"],
"violated_by": [
"lexicon:deep-inheritance-hierarchy",
"lexicon:broken-inheritance"
],
"detected_by": [
"inheritance depth",
"overridden behavior conflicts"
],
"measured_by": [
"inheritance depth",
"composition ratio"
],
"refactored_by": [
"lexicon:replace-inheritance-with-delegation",
"architecture:strategy-pattern"
],
"enforced_by": [
"inheritance depth limits",
"review rules"
],
"severity": "recommended",
"exemplar": {
"before": "class RetryingSqlFooStore extends SqlFooStore {\n async save(foo: Foo) {\n for (let attempt = 0; attempt < 3; attempt += 1) {\n try { return await super.save(foo); } catch (error) { if (attempt === 2) throw error; }\n }\n }\n}",
"after": "class RetryingFooStore implements FooStore {\n constructor(private readonly inner: FooStore, private readonly attempts: number) {}\n async save(foo: Foo) {\n for (let attempt = 0; attempt < this.attempts; attempt += 1) {\n try { return await this.inner.save(foo); } catch (error) { if (attempt === this.attempts - 1) throw error; }\n }\n }\n}",
"lang": "ts"
}
},
{
"id": "reusability",
"distinctFrom": [
{
"id": "lexicon:over-generalization",
"reason": "Reusability serves new callers unchanged, while over-generalization is the clarity lost by making code serve every case."
}
],
"name": "Reusability",
"definition": "The degree to which a function or module can serve a new caller without change, because it carries no context of its first one.",
"type": "quality-attribute",
"scope": [
"function",
"module",
"component"
],
"requires": [
"Abstraction",
"Stable Contracts"
],
"reinforces": [
"DRY",
"Composability"
],
"enables": [
"Shared Libraries",
"Product Lines"
],
"conflicts_with": ["Context-Specific Coupling"],
"tensions_with": [
"YAGNI",
"Over-Generalization"
],
"violated_by": ["lexicon:context-specific-coupling"],
"detected_by": ["environment-specific logic in reusable code"],
"measured_by": [
"reuse count",
"dependency portability"
],
"refactored_by": [
"lexicon:introduce-parameter-object",
"lexicon:extract-module",
"lexicon:pass-context-explicitly"
],
"enforced_by": [
"API review",
"dependency rules"
],
"severity": "recommended",
"exemplar": {
"before": "function saveAdminFoo(foo: Foo) { return adminFooDb.insert(foo); }\nfunction savePublicFoo(foo: Foo) { return publicFooDb.insert(foo); }",
"after": "function saveFoo(store: FooStore, foo: Foo) { return store.save(foo); }\nconst saveAdminFoo = (foo: Foo) => saveFoo(adminFooStore, foo);\nconst savePublicFoo = (foo: Foo) => saveFoo(publicFooStore, foo);",
"lang": "ts"
}
},
{
"id": "replaceability",
"distinctFrom": [
{
"id": "architecture:high-cohesion",
"reason": "Replaceability is swapping an implementation, while high cohesion is members sharing one purpose."
},
{
"id": "architecture:interchangeability",
"reason": "Replaceability is substituting one implementation when the code changes, while interchangeability is selecting among several live implementations at runtime."
},
{
"id": "architecture:portability",
"reason": "Replaceability swaps one component, while portability moves the whole software to another platform."
},
{
"id": "architecture:reusability",
"reason": "Replaceability puts a new implementation behind the same callers, while reusability serves a new caller with the same implementation."
},
{
"id": "architecture:testability",
"reason": "Replaceability swaps an implementation in production, while testability supplies dependencies in a test."
},
{
"id": "architecture:stable-interfaces",
"reason": "Replaceability swaps what sits behind an interface, while stable interfaces keep the interface itself unchanged across releases."
},
{
"id": "lexicon:maintainability",
"reason": "Replaceability makes one kind of change cheap, while maintainability is the ease of every kind of correction and extension."
}
],
"name": "Replaceability",
"definition": "The degree to which a component can be swapped for another implementation of its interface without editing its callers.",
"type": "quality-attribute",
"scope": [
"component",
"service",
"infrastructure"
],
"requires": [
"Stable Interfaces",
"Low Coupling"
],
"reinforces": [
"DIP",
"Ports and Adapters"
],
"enables": [
"Vendor Swap",
"Plugin Architecture"
],
"conflicts_with": ["Concrete Coupling"],
"tensions_with": ["Deep Optimization"],
"violated_by": ["architecture:vendor-lock-in-leakage"],
"detected_by": ["infrastructure imports in core layers"],
"measured_by": [
"adapter coverage",
"boundary purity"
],
"refactored_by": [
"lexicon:introduce-port",
"lexicon:extract-adapter",
"lexicon:invert-dependency"
],
"enforced_by": [
"import restrictions",
"adapter tests"
],
"severity": "recommended",
"exemplar": {
"before": "class FooService {\n private readonly store = new SqlFooStore();\n save(foo: Foo) { return this.store.save(foo); }\n}",
"after": "class FooService {\n constructor(private readonly store: FooStore) {}\n save(foo: Foo) { return this.store.save(foo); }\n}\ntest(\"saves without a database\", async () => {\n const store = new MemoryFooStore();\n await new FooService(store).save(foo);\n expect(store.find(foo.id)).toEqual(foo);\n});",
"lang": "ts"
}
},
{
"id": "interchangeability",
"distinctFrom": [
{
"id": "lexicon:specialized-optimization",
"reason": "Interchangeability lets implementations be swapped, while specialized optimization is the tuning for one implementation that it gives up."
}
],
"name": "Interchangeability",
"definition": "The degree to which several implementations conform to one contract closely enough to be selected at runtime.",
"type": "quality-attribute",
"scope": [
"component",
"plugin",
"service"
],
"requires": [
"Contract Compatibility",
"Interface Conformance"
],
"reinforces": [
"Replaceability",
"Polymorphism"
],
"enables": [
"Strategy Swap",
"Plugin Swap"
],
"conflicts_with": ["Implementation-Specific Contracts"],
"tensions_with": ["Specialized Optimization"],
"violated_by": ["lexicon:implementation-specific-contracts"],
"detected_by": [
"contract test failure",
"incompatible schema"
],
"measured_by": [
"conformance score",
"compatibility tests"
],
"refactored_by": [
"lexicon:standardize-the-interface",
"lexicon:extract-adapter",
"lexicon:contract-testing"
],
"enforced_by": [
"contract tests",
"schema validation"
],
"severity": "recommended",
"exemplar": {
"before": "function loadFoo(kind: \"sql\" | \"memory\", id: FooId) {\n if (kind === \"sql\") return sqlFooStore.find(id);\n return memoryFooStore.get(id);\n}",
"after": "interface FooStore { find(id: FooId): Promise<Foo | undefined>; }\nfunction loadFoo(store: FooStore, id: FooId) { return store.find(id); }",
"lang": "ts"
}
},
{
"id": "independence",
"name": "Independence",
"definition": "A design rule that a module can be tested and deployed without the modules around it being present or released.",
"type": "principle",
"scope": [
"module",
"service",
"deployment"
],
"requires": [
"Low Coupling",
"Explicit Boundaries"
],
"reinforces": [
"Autonomy",
"Portability"
],
"enables": [
"Independent Testing",
"Independent Deployment"
],
"conflicts_with": ["Shared Runtime Dependency"],
"tensions_with": ["Coordination Cost"],
"violated_by": [
"lexicon:shared-database",
"architecture:synchronous-chain-trap"
],
"detected_by": [
"shared mutable resources",
"deployment coupling"
],
"measured_by": [
"independent deployability",
"dependency count"
],
"refactored_by": [
"lexicon:split-bounded-context",
"architecture:domain-events",
"lexicon:introduce-port"
],
"enforced_by": [
"deployment rules",
"service ownership"
],
"severity": "recommended",
"exemplar": {
"before": "class FooModule {\n create(foo: Foo) {\n barModule.refresh(foo.id);\n bazModule.rebuild(foo.id);\n return fooDb.insert(foo);\n }\n}",
"after": "class FooModule {\n constructor(private readonly store: FooStore, private readonly events: EventSink) {}\n async create(foo: Foo) {\n await this.store.save(foo);\n this.events.emit({ type: \"FooCreated\", fooId: foo.id });\n }\n}",
"lang": "ts"
}
},
{
"id": "autonomy",
"distinctFrom": [
{
"id": "architecture:explicit-contracts",
"reason": "Autonomy is ownership of data and decisions, while explicit contracts are the declared boundaries others use to reach them."
},
{
"id": "architecture:governance",
"reason": "Autonomy leaves decisions with their owner, while governance holds decisions to shared policy."
},
{
"id": "architecture:standardization",
"reason": "Autonomy lets each owner choose, while standardization fixes one choice everywhere."
},
{
"id": "architecture:decentralization",
"reason": "Autonomy is one unit owning its data and decisions, while decentralization is the system-wide placement of control with those units."
},
{
"id": "architecture:independence",
"reason": "Autonomy is ownership, while independence is being tested and deployed without the neighboring modules."
}
],
"name": "Autonomy",
"definition": "A design rule that a service or team owns its data and decisions, so it alone writes its data and others reach or change it only through its published API or events.",
"type": "principle",
"aliases": ["Service Autonomy"],
"scope": [
"service",
"team",
"bounded context"
],
"requires": [
"Independence",
"Own Data",
"Explicit Contracts"
],
"reinforces": [
"Decentralization",
"Resilience"
],
"enables": [
"Microservices",
"Bounded Context Ownership",
"Independent Deployment"
],
"conflicts_with": [
"Centralized Runtime Control",
"Shared Database",
"Cyclic Deployment Dependency"
],
"tensions_with": [
"Governance",
"Standardization",
"Global Consistency"
],
"violated_by": [
"lexicon:shared-database",
"architecture:distributed-monolith"
],
"detected_by": [
"external writes to owned data",
"cross-team coupling"
],
"measured_by": [
"ownership clarity",
"deployment independence"
],
"refactored_by": [
"lexicon:own-data-per-service",
"lexicon:split-bounded-context",
"architecture:domain-events"
],
"enforced_by": [
"ownership boundaries",
"API policies"
],
"severity": "contextual",
"exemplar": {
"before": "async function createFoo(foo: Foo) {\n const bar = await barService.get(foo.barId);\n await bazService.validate(foo, bar);\n return fooStore.save(foo);\n}",
"after": "async function createFoo(foo: Foo) {\n await fooStore.save(foo);\n await outbox.append({ type: \"FooCreated\", fooId: foo.id, barId: foo.barId });\n}",
"lang": "ts"
}
}
]
}