@open-domain-specification/skill 0.1.11 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,11 +17,11 @@ independently of the name.
17
17
  | `Subdomain` | `addBoundedcontext(name, { description, bigBallOfMud?, team? })` | a context serving this subdomain |
18
18
  | `BoundedContext` | `serves(subdomain)` | adds a served subdomain |
19
19
  | `BoundedContext` | `ownedBy(team)` | sets the owning team |
20
- | `BoundedContext` | `upstreamOf(other, { type?, upstreamRoles?, downstreamRoles?, description? })` | directed relationship, this side upstream; `type` defaults to `"upstream-downstream"`, or `"customer-supplier"` |
20
+ | `BoundedContext` | `upstreamOf(other, { type?, upstreamRoles?, downstreamRoles?, description?, comments?, disposition? })` | directed relationship, this side upstream; `type` defaults to `"upstream-downstream"`, or `"customer-supplier"` |
21
21
  | `BoundedContext` | `downstreamOf(other, options)` | the same, this side downstream |
22
- | `BoundedContext` | `partnerOf(other, description?)` | partnership |
23
- | `BoundedContext` | `sharesKernelWith(other, description?)` | shared kernel |
24
- | `BoundedContext` | `separateWaysFrom(other, description?)` | separate ways |
22
+ | `BoundedContext` | `partnerOf(other, { description?, comments?, disposition? })` | partnership |
23
+ | `BoundedContext` | `sharesKernelWith(other, options)` | shared kernel |
24
+ | `BoundedContext` | `separateWaysFrom(other, options)` | separate ways |
25
25
  | `BoundedContext` | `addAggregate(name, { description })` | an aggregate |
26
26
  | `BoundedContext` | `addService(name, { type, description })` | a service; `type` is `"application" \| "domain"` |
27
27
  | `BoundedContext` | `addPolicy(name, { description })` | a policy; chain `.on(...events).then(...operations)` |
@@ -31,8 +31,8 @@ independently of the name.
31
31
  | `Aggregate` | `addEntity(name, { description, root? })` | an entity |
32
32
  | `Aggregate` | `addValueObject(name, { description })` | a value object |
33
33
  | `Aggregate` | `addInvariant(name, { description })` | an invariant; chain `.constrains(...entities, valueObjects or attributes)` |
34
- | `Aggregate`, `Service` | `provides(name, { type, description, pattern?, internal?, schema? })` | a consumable; `type` is `"event" \| "operation"`, `pattern` is `"open-host-service" \| "published-language"` |
35
- | `Aggregate`, `Service` | `consumes(consumable, { pattern? })` | a consumption; `pattern` is `"conformist" \| "anti-corruption-layer"` |
34
+ | `Aggregate`, `Service` | `provides(name, { type, description, pattern?, internal?, schema?, comments?, disposition? })` | a consumable; `type` is `"event" \| "operation"`, `pattern` is `"open-host-service" \| "published-language"` |
35
+ | `Aggregate`, `Service` | `consumes(consumable, { pattern?, comments?, disposition? })` | a consumption; `pattern` is `"conformist" \| "anti-corruption-layer"` |
36
36
  | `Consumable` | `raises(...events)` | the events an operation raises |
37
37
  | `Entity`, `ValueObject`, `DataSchema` | `addAttribute(name, { type, description?, identity?, valueobject? })` | an attribute; `type` is free text |
38
38
  | `Entity`, `ValueObject` | `uses(target, label, cardinality?)` | a `uses` relation |
@@ -41,5 +41,13 @@ independently of the name.
41
41
  | `Entity`, `ValueObject` | `addRelation(target, { relation, label?, cardinality? })` | any relation explicitly |
42
42
  | `Entity` | `.attributes.get("name")` | look an attribute up, e.g. to constrain it |
43
43
 
44
+ Every relationship, consumable and consumption also takes the evidence pair from
45
+ RFC-002: `comments` is a list of `{ text, link? }`, where a link is
46
+ `{ kind, url, label? }` and `kind` is
47
+ `"code" | "contract" | "adr" | "runbook" | "dashboard"`; `disposition` is
48
+ `"by-design" | "tolerated" | "refactor"` and defaults to `by-design`, which is never
49
+ written to JSON. Read one back with `dispositionOf(element)`, and list the intents
50
+ nobody has documented with `intentsWithoutComments(workspace)`.
51
+
44
52
  `cardinality` is `"1" | "0..1" | "*" | "1..*"`. Chainable methods (`raises`, `on`, `then`,
45
53
  `constrains`, `embody`, `serves`, `ownedBy`) return their receiver.
@@ -61,6 +61,7 @@ job is to get the model out of their head without making them learn the vocabula
61
61
  - "Does the upstream side publish a documented API, or a documented message format?" →
62
62
  `open-host-service` / `published-language`. Goes on `upstreamRoles` and on each exposed
63
63
  consumable's `pattern`.
64
+ - Then the two evidence questions (see below), once for the relationship you just recorded.
64
65
 
65
66
  ## Phase E: inside one context (produces Aggregates, Entities, Value Objects, Invariants, Glossary)
66
67
 
@@ -100,9 +101,39 @@ Repeat for each context the user wants detailed. Ask which one to start with.
100
101
  with a downstream `pattern`.
101
102
  - Close: "Which of the words we used should I define, and does each map to one of the things
102
103
  we modelled?" → glossary terms with `embodiedBy`.
104
+ - Ask the two evidence questions (see below) for each consumable or consumption that came out
105
+ of this phase with a `pattern` on it.
103
106
 
104
107
  ## Phase G: validate and reflect
105
108
 
106
109
  Run validation. Explain each diagnostic in one plain sentence, propose the fix, and ask before
107
110
  applying fixes for warnings. Then summarise what changed, in the user's words, and ask what to
108
111
  model next.
112
+
113
+ ## The two evidence questions
114
+
115
+ Every strategic intent — a relationship, a consumable that leaves its context, a consumption —
116
+ gets exactly these two, and only when it is new:
117
+
118
+ - "Is that how you want it, or is it something you are living with?" → `disposition`. "How we
119
+ want it" is `by-design`, which is the default and is never written down. "Living with it, and
120
+ nobody is going to change it" is `tolerated`. "It should not stay like that" is `refactor`;
121
+ follow up with "what should it become?" and put the answer in the comment.
122
+ - "Where does that live — a file, a repo, an API doc, a decision record?" → the first `comment`,
123
+ with its `link`. Take a path or a URL, whichever they give; `kind` is `code`, `contract`,
124
+ `adr`, `runbook` or `dashboard`. If they have nothing to point at, still record what they
125
+ said as a comment with no link.
126
+
127
+ Rules for asking them:
128
+
129
+ - Once per intent, never per role. A relationship with an `open-host-service` upstream role and
130
+ an `anti-corruption-layer` downstream role is still one relationship and gets one pair of
131
+ questions, not two.
132
+ - Never for an internal consumable. It does not cross a boundary, so there is no strategic
133
+ claim to back up.
134
+ - Never for an intent that already carries comments. Read first, ask second, as everywhere else.
135
+ - Do not ask them before the intent itself is settled; they are the follow-up to "so I'd note a
136
+ ... right?", not a replacement for it.
137
+
138
+ If the codebase is at hand, offer to answer the second question yourself instead of asking:
139
+ that is reconciliation, and it is in `reconciliation.md`.
@@ -19,6 +19,7 @@ Represents a workspace in the Open Domain Specification (ODS).
19
19
  | `logoUrl` | string | no | |
20
20
  | `name` | string | yes | |
21
21
  | `odsVersion` | string | yes | |
22
+ | `options` | [WorkspaceOptions](#workspaceoptions) | no | Switches for behaviour that is not part of the model, such as opt-in rules. |
22
23
  | `primaryColor` | string | no | |
23
24
  | `relationships` | array of [ContextRelationship](#contextrelationship) | yes | |
24
25
  | `teams` | map of id to [Team](#team) | yes | |
@@ -75,13 +76,38 @@ Represents a bounded context in the Open Domain Specification (ODS).
75
76
 
76
77
  No other fields are allowed.
77
78
 
79
+ ## Comment
80
+
81
+ A short grounded statement about the real system behind a strategic intent, optionally backed by one link.
82
+
83
+ | Field | Type | Required | Notes |
84
+ |---|---|---|---|
85
+ | `link` | [CommentLink](#commentlink) | no | |
86
+ | `text` | string | yes | |
87
+
88
+ No other fields are allowed.
89
+
90
+ ## CommentLink
91
+
92
+ Where the evidence for a comment lives: the code, the contract, the decision record, the runbook or the dashboard.
93
+
94
+ | Field | Type | Required | Notes |
95
+ |---|---|---|---|
96
+ | `kind` | "adr" | "code" | "contract" | "dashboard" | "runbook" | yes | |
97
+ | `label` | string | no | What to show instead of the raw URL. |
98
+ | `url` | string | yes | |
99
+
100
+ No other fields are allowed.
101
+
78
102
  ## Consumable
79
103
 
80
104
  Represents a consumable in the Open Domain Specification (ODS).
81
105
 
82
106
  | Field | Type | Required | Notes |
83
107
  |---|---|---|---|
108
+ | `comments` | array of [Comment](#comment) | no | Grounded statements about the real system behind this consumable. |
84
109
  | `description` | string | yes | |
110
+ | `disposition` | "by-design" | "refactor" | "tolerated" | no | What the architecture thinks of this consumable. Absent means `by-design`. |
85
111
  | `internal` | boolean | no | True when the consumable stays inside its context: an event only local policies react to, or an operation only local callers issue. Internal consumables may not be consumed from another context. |
86
112
  | `name` | string | yes | |
87
113
  | `pattern` | "open-host-service" | "published-language" | no | The upstream role this consumable is offered under. Absent on internal consumables. |
@@ -97,7 +123,9 @@ Represents a consumption in the Open Domain Specification (ODS).
97
123
 
98
124
  | Field | Type | Required | Notes |
99
125
  |---|---|---|---|
126
+ | `comments` | array of [Comment](#comment) | no | Grounded statements about the real system behind this consumption. |
100
127
  | `consumable` | `{ "$ref": string }` | yes | |
128
+ | `disposition` | "by-design" | "refactor" | "tolerated" | no | What the architecture thinks of this consumption. Absent means `by-design`. |
101
129
  | `pattern` | "anti-corruption-layer" | "conformist" | no | The downstream role the consumer adopts for this consumable. |
102
130
 
103
131
  No other fields are allowed.
@@ -126,7 +154,9 @@ An upstream/downstream relationship between two bounded contexts.
126
154
 
127
155
  | Field | Type | Required | Notes |
128
156
  |---|---|---|---|
157
+ | `comments` | array of [Comment](#comment) | no | Grounded statements about the real system behind this relationship. |
129
158
  | `description` | string | no | |
159
+ | `disposition` | "by-design" | "refactor" | "tolerated" | no | What the architecture thinks of this relationship. Absent means `by-design`. |
130
160
  | `downstream` | `{ "$ref": string }` | yes | |
131
161
  | `downstreamRoles` | array of "anti-corruption-layer" | "conformist" | yes | |
132
162
  | `type` | "customer-supplier" | "upstream-downstream" | yes | |
@@ -210,6 +240,16 @@ A reaction: when these events happen, issue these commands.
210
240
 
211
241
  No other fields are allowed.
212
242
 
243
+ ## RuleOptions
244
+
245
+ Opt-in validation rules. A rule listed here is off unless the workspace turns it on.
246
+
247
+ | Field | Type | Required | Notes |
248
+ |---|---|---|---|
249
+ | `commentsRequired` | boolean | no | Warn on every context relationship that carries no comments. Off by default. |
250
+
251
+ No other fields are allowed.
252
+
213
253
  ## Service
214
254
 
215
255
  Represents a service in the Open Domain Specification (ODS).
@@ -242,7 +282,9 @@ A relationship between two bounded contexts with no upstream or downstream side.
242
282
 
243
283
  | Field | Type | Required | Notes |
244
284
  |---|---|---|---|
285
+ | `comments` | array of [Comment](#comment) | no | Grounded statements about the real system behind this relationship. |
245
286
  | `description` | string | no | |
287
+ | `disposition` | "by-design" | "refactor" | "tolerated" | no | What the architecture thinks of this relationship. Absent means `by-design`. |
246
288
  | `participants` | array of unknown | yes | |
247
289
  | `type` | "partnership" | "separate-ways" | "shared-kernel" | yes | |
248
290
 
@@ -273,6 +315,16 @@ Represents a value object in the Open Domain Specification (ODS).
273
315
 
274
316
  No other fields are allowed.
275
317
 
318
+ ## WorkspaceOptions
319
+
320
+ Per-workspace switches for behaviour that is not part of the model itself.
321
+
322
+ | Field | Type | Required | Notes |
323
+ |---|---|---|---|
324
+ | `rules` | [RuleOptions](#ruleoptions) | no | |
325
+
326
+ No other fields are allowed.
327
+
276
328
  ## Ref grammar
277
329
 
278
330
  Every cross-link is an object `{ "$ref": "<path>" }`. Paths are JSON pointers into the workspace, built from the ids that key each collection:
@@ -0,0 +1,184 @@
1
+ # Reconciliation
2
+
3
+ A model is a claim about a real system. Reconciliation is checking the claim: for each strategic
4
+ intent, go and look in the repository for the evidence behind it, then write down what is
5
+ actually there. The result is `comments` on the intent — grounded statements, each backed by a
6
+ link — and, where the code disagrees with the model, a proposed `disposition`.
7
+
8
+ Strategic intents are the three things evidence hangs off: a **relationship** between two
9
+ contexts, a **consumable** that crosses a boundary, and a **consumption** of one. Internal
10
+ consumables never cross a boundary, so they are not strategic and are never reconciled.
11
+
12
+ ## When to reconcile
13
+
14
+ - The user asks: "check the model against the code", "is the ACL actually there", "reconcile".
15
+ - After the interview, for the intents just created, if the codebase is at hand.
16
+ - On opening a workspace whose intents carry no comments: offer it, once, and let the user
17
+ choose. Do not start a reconciliation nobody asked for.
18
+
19
+ The worklist is every intent with no comments — the "No comments" list on the health report,
20
+ and `intentsWithoutComments(workspace)` in the DSL. Work it in the order the report shows and
21
+ stop when the user says enough.
22
+
23
+ ## What to look for, per pattern
24
+
25
+ The pattern names the thing that should exist. Search for that thing, in the code of the side
26
+ that owns it. Terms below are starting points, not a script: read the repository's own
27
+ vocabulary first and search in it.
28
+
29
+ | Pattern | Owned by | What should exist | Where to look |
30
+ |---|---|---|---|
31
+ | `anti-corruption-layer` | downstream | An adapter or translator on the downstream side: a mapper between the upstream payload and the downstream model, so no upstream type reaches the domain | The downstream context's code. Names like `adapter`, `translator`, `mapper`, `acl`, `client`, `gateway`; a function taking an upstream DTO and returning a domain type |
32
+ | `conformist` | downstream | Direct use of the upstream types: the upstream's own classes, DTOs or generated client, imported into the downstream domain, with no mapping | Imports in the downstream context from the upstream package, module or generated client |
33
+ | `open-host-service` | upstream | A published contract: an OpenAPI, GraphQL, gRPC or AsyncAPI document that others build against, versioned and documented | The upstream context's repository root and its `docs/`, `api/`, `contracts/`, `proto/` folders; a published API reference |
34
+ | `published-language` | upstream | A shared interchange format independent of either side: a JSON Schema, Protobuf, Avro or industry format the events are emitted in | A schema registry, a `schemas/` or `proto/` folder, a package holding only message definitions |
35
+ | `shared-kernel` | both | A shared package, library or schema both sides depend on, small and jointly owned | A workspace package both contexts declare as a dependency; a shared database schema or migration folder |
36
+ | `partnership` | both | Joint planning and release: one pipeline, one release cadence, or features that land in both at once | Release workflows, a shared changelog, commits that touch both sides together |
37
+ | `customer-supplier` | upstream | The downstream's requirements visible on the upstream side: contract tests, a consumer-driven test suite, downstream tickets in the upstream backlog | Contract test folders (`pact`, `contract`, `consumer`), the upstream's issue references |
38
+ | `upstream-downstream` | downstream | A directed dependency, and nothing on the upstream side that plans for the downstream | The downstream's dependency on the upstream, and the absence of the customer-supplier evidence above |
39
+ | `separate-ways` | neither | Nothing: no dependency, no shared package, no calls between the two | Search for the dependency and report that there is none. Finding one is the interesting result |
40
+
41
+ ## The three outcomes
42
+
43
+ Every intent you look at ends in exactly one of these. Write a comment in all three cases; a
44
+ reconciliation that says nothing is worse than one that says "I looked and found nothing".
45
+
46
+ **It is there.** Write one comment saying what you found and where, with a link to it. Leave the
47
+ disposition alone; an absent disposition already means `by-design`.
48
+
49
+ **It is not what the model says.** Write one comment saying what is there instead, with a link,
50
+ and propose a disposition:
51
+
52
+ - `tolerated` — the code disagrees with the pattern, the reason is understood, and nobody plans
53
+ to change it. A compromise someone is living with.
54
+ - `refactor` — the intent should be removed or replaced, and someone means to do it. Say in the
55
+ comment what it should become.
56
+
57
+ Propose, never apply. The disposition is the author's judgement about their own architecture:
58
+ show the comment and the disposition you would set, say why in one sentence, and wait.
59
+
60
+ **You cannot tell.** Write one comment naming what you searched for and where, and propose no
61
+ disposition. Then ask the user where it lives. "No adapter or translator found in the Sales
62
+ context; searched for a mapper between the Catalog pet payload and Sales' own types" is useful
63
+ evidence. A guess is not.
64
+
65
+ ## Writing the comment
66
+
67
+ A comment is `{ text, link? }`. The text is one or two sentences, in the present tense, saying
68
+ what is in the system — not what you did, and not what the pattern means in general. The link is
69
+ `{ kind, url, label? }` with `kind` one of `code`, `contract`, `adr`, `runbook`, `dashboard`.
70
+
71
+ - Cite what you actually opened. A path or a URL, never a guess at where a file probably is.
72
+ - One statement per comment. Two findings are two comments; the report lists them separately.
73
+ - `label` is what a reader should see instead of the URL: the repo-relative path, the endpoint,
74
+ the ADR's title.
75
+ - Use the repository's own words for its own parts, and the model's words for the model's.
76
+ - Never delete or rewrite a comment an author wrote. Add yours next to it.
77
+ - A comment with no link is still evidence when there is nothing to link to; a link you cannot
78
+ justify is not.
79
+
80
+ In the DSL, `comments` and `disposition` go in the options of `upstreamOf`, `downstreamOf`,
81
+ `partnerOf`, `sharesKernelWith`, `separateWaysFrom`, `provides` and `consumes`:
82
+
83
+ ```ts
84
+ salesBC.downstreamOf(catalogBC, {
85
+ type: "customer-supplier",
86
+ upstreamRoles: ["open-host-service"],
87
+ downstreamRoles: ["anti-corruption-layer"],
88
+ description: "Sales needs pet availability; Catalog commits to the summary contract",
89
+ disposition: "tolerated",
90
+ comments: [
91
+ {
92
+ text: "Sales calls the Catalog summary endpoint directly from its order service; there is no translator, so the Catalog pet payload reaches the Sales domain unchanged.",
93
+ link: {
94
+ kind: "code",
95
+ url: "https://github.com/example/petstore/blob/main/sales/src/orders/service.ts",
96
+ label: "sales/src/orders/service.ts",
97
+ },
98
+ },
99
+ ],
100
+ });
101
+ ```
102
+
103
+ In JSON, both are optional fields on the relationship, the consumable and the consumption:
104
+
105
+ ```json
106
+ {
107
+ "relationships": [
108
+ {
109
+ "upstream": { "$ref": "#/boundedcontexts/catalog" },
110
+ "downstream": { "$ref": "#/boundedcontexts/sales" },
111
+ "type": "customer-supplier",
112
+ "upstreamRoles": ["open-host-service"],
113
+ "downstreamRoles": ["anti-corruption-layer"],
114
+ "description": "Sales needs pet availability; Catalog commits to the summary contract",
115
+ "disposition": "tolerated",
116
+ "comments": [
117
+ {
118
+ "text": "Sales calls the Catalog summary endpoint directly from its order service; there is no translator, so the Catalog pet payload reaches the Sales domain unchanged.",
119
+ "link": {
120
+ "kind": "code",
121
+ "url": "https://github.com/example/petstore/blob/main/sales/src/orders/service.ts",
122
+ "label": "sales/src/orders/service.ts"
123
+ }
124
+ }
125
+ ]
126
+ }
127
+ ]
128
+ }
129
+ ```
130
+
131
+ A consumable carries the same two fields beside its `pattern`, and so does a consumption:
132
+
133
+ ```json
134
+ "provides": {
135
+ "get_pet_summary": {
136
+ "name": "GetPetSummary",
137
+ "description": "GET /pets/{id}/summary",
138
+ "type": "operation",
139
+ "pattern": "open-host-service",
140
+ "comments": [
141
+ {
142
+ "text": "The summary endpoint is in the published Catalog OpenAPI document and is versioned with the rest of the API.",
143
+ "link": {
144
+ "kind": "contract",
145
+ "url": "https://github.com/example/petstore/blob/main/catalog/openapi.yaml",
146
+ "label": "GET /pets/{id}/summary"
147
+ }
148
+ }
149
+ ]
150
+ }
151
+ }
152
+ ```
153
+
154
+ ```json
155
+ "consumes": [
156
+ {
157
+ "consumable": {
158
+ "$ref": "#/boundedcontexts/catalog/services/pet_app/provides/get_pet_summary"
159
+ },
160
+ "pattern": "anti-corruption-layer",
161
+ "disposition": "refactor",
162
+ "comments": [
163
+ {
164
+ "text": "No adapter or translator found in Sales; searched the order service and its client folder for a mapper between the Catalog pet payload and the Sales order model.",
165
+ "link": {
166
+ "kind": "code",
167
+ "url": "https://github.com/example/petstore/tree/main/sales/src/orders",
168
+ "label": "sales/src/orders"
169
+ }
170
+ }
171
+ ]
172
+ }
173
+ ]
174
+ ```
175
+
176
+ `by-design` is the meaning of an absent `disposition` and is never written to the file. Writing
177
+ `"disposition": "by-design"` is the same as leaving it out; leave it out.
178
+
179
+ ## Reporting back
180
+
181
+ When the pass is done, summarise in the user's words, not in DDD words: how many intents you
182
+ looked at, how many now have evidence, which ones you would mark `tolerated` or `refactor` and
183
+ why, and which ones you could not resolve and need them to answer. Then validate: the health
184
+ report and the opt-in `comments-required` rule both read what you just wrote.
@@ -0,0 +1,98 @@
1
+ <!-- Generated by packages/skill/scripts/generate.mts from @open-domain-specification/core (PATTERNS); do not edit. -->
2
+
3
+ # Strategic relationships
4
+
5
+ What each strategic pattern means. Explain one to the user in these words: the diagram legend, the hover summaries and the generated documentation all read the same table, so your explanation and what they are looking at agree. The value in the left column is what goes in the model.
6
+
7
+ ## Relationship types
8
+
9
+ The `type` of a context relationship. Exactly one per relationship.
10
+
11
+ ### `upstream-downstream` — Upstream/Downstream (U/D)
12
+
13
+ One context depends on another; the upstream does not plan around the downstream.
14
+
15
+ A directed dependency with no customer commitment: the upstream evolves on its own schedule and the downstream adapts through its roles.
16
+
17
+ - Upstream keeps full autonomy
18
+ - Downstream carries the integration risk
19
+
20
+ ### `customer-supplier` — Customer/Supplier (C/S)
21
+
22
+ Upstream plans for and prioritizes downstream requirements.
23
+
24
+ An asymmetric relationship where downstream needs act as customer requirements and upstream delivery commitments factor in downstream deadlines.
25
+
26
+ - Predictable alignment between collaborating teams
27
+ - Upstream velocity can be constrained by downstream dependencies
28
+
29
+ ### `shared-kernel` — Shared Kernel (SK)
30
+
31
+ A shared subset of domain model and code, co-owned by both teams.
32
+
33
+ A strictly bounded shared library, schema, or database subset. Neither team alters the kernel without joint consultation and continuous test verification.
34
+
35
+ - Prevents duplicate modeling and translation costs
36
+ - High coordination friction; degrades autonomy if it grows beyond a small subset
37
+
38
+ ### `partnership` — Partnership (P)
39
+
40
+ Mutual co-operation where teams coordinate development and releases.
41
+
42
+ Two contexts succeed or fail together. Features spanning both are planned, co-designed, and released in synchronized cycles.
43
+
44
+ - Tight strategic cohesion across organizational boundaries
45
+ - Requires close communication and joint release cadences
46
+
47
+ ### `separate-ways` — Separate Ways (SW)
48
+
49
+ A deliberate decision to forego integration and develop independently.
50
+
51
+ Both contexts solve their requirements without technical links, accepting possible domain overlap to keep complete operational independence.
52
+
53
+ - Maximum operational autonomy with no cross-team dependencies
54
+ - Possible duplication of data and business logic
55
+
56
+ ## Upstream roles
57
+
58
+ How the upstream side exposes what it provides: a relationship's `upstreamRoles`, and a consumable's `pattern`.
59
+
60
+ ### `open-host-service` — Open Host Service (OHS)
61
+
62
+ A public, stable protocol or API provided by an upstream context.
63
+
64
+ The upstream context commits to maintaining a standardized, backward-compatible interface so multiple downstream subsystems can integrate without bespoke integration logic.
65
+
66
+ - Reduces coupling across multiple consumers
67
+ - Increases upstream maintenance overhead and versioning obligations
68
+
69
+ ### `published-language` — Published Language (PL)
70
+
71
+ A well-documented shared interchange format.
72
+
73
+ An explicit schema standard (JSON Schema, Protobuf, an industry XML) that expresses domain operations and events independently of either context's internal representation.
74
+
75
+ - Enables polyglot integrations and widespread consumption
76
+ - Requires governance over schema evolution
77
+
78
+ ## Downstream roles
79
+
80
+ How the downstream side protects itself: a relationship's `downstreamRoles`, and a consumption's `pattern`.
81
+
82
+ ### `anti-corruption-layer` — Anti-Corruption Layer (ACL)
83
+
84
+ A translating boundary isolating a downstream model from external concepts.
85
+
86
+ A translating mechanism (adapters, facades, mappers) that keeps foreign domain concepts, schema changes, or vendor anomalies from leaking into the downstream model.
87
+
88
+ - Maximum isolation and autonomy for the downstream context
89
+ - Cost of maintaining translation logic and data mappings
90
+
91
+ ### `conformist` — Conformist (CF)
92
+
93
+ Downstream adopts the upstream domain model without translation.
94
+
95
+ The downstream team accepts the upstream model as-is, dropping translation layers when the upstream model fits well or translation overhead is unjustified.
96
+
97
+ - No translation and a simpler codebase
98
+ - Exposed to breaking upstream schema changes
@@ -17,9 +17,9 @@ element lives in a workspace file; `DSL` is the core call that creates it.
17
17
  | "the old system", "legacy", "nobody understands its schema" | big ball of mud | `"bigBallOfMud": true` | `bigBallOfMud: true` |
18
18
  | "A depends on B", "A calls B", "A reads B's data" | upstream-downstream (B upstream) | `relationships[]` `type: "upstream-downstream"` | `a.downstreamOf(b, {...})` |
19
19
  | "they ask us before changing", "we're their customer" | customer-supplier | `type: "customer-supplier"` | `a.downstreamOf(b, {type: "customer-supplier", ...})` |
20
- | "both teams change it together", "we release together" | partnership | `type: "partnership"`, `participants` | `a.partnerOf(b)` |
21
- | "we share the same tables / library / code" | shared kernel | `type: "shared-kernel"` | `a.sharesKernelWith(b)` |
22
- | "we deliberately don't integrate" | separate ways | `type: "separate-ways"` | `a.separateWaysFrom(b, why)` |
20
+ | "both teams change it together", "we release together" | partnership | `type: "partnership"`, `participants` | `a.partnerOf(b, {description})` |
21
+ | "we share the same tables / library / code" | shared kernel | `type: "shared-kernel"` | `a.sharesKernelWith(b, {description})` |
22
+ | "we deliberately don't integrate" | separate ways | `type: "separate-ways"` | `a.separateWaysFrom(b, {description: why})` |
23
23
  | "we use their API as-is", "we take whatever they send" | conformist | consumption `pattern: "conformist"`; relationship `downstreamRoles` | `agg.consumes(c, {pattern: "conformist"})` |
24
24
  | "we copy and reshape their data", "we wrap their API" | anti-corruption layer | `pattern: "anti-corruption-layer"` | `agg.consumes(c, {pattern: "anti-corruption-layer"})` |
25
25
  | "we expose a documented API", "there's a REST endpoint" | open host service | operation `pattern: "open-host-service"`; relationship `upstreamRoles` | `svc.provides(name, {type: "operation", pattern: "open-host-service"})` |
@@ -75,3 +75,11 @@
75
75
  **Why it matters:** A context that serves no subdomain has no place in the problem-space view, so nobody can see which part of the business it exists for.
76
76
 
77
77
  **Usual fix:** Add the subdomain the context serves to its subdomains list.
78
+
79
+ ## `comments-required` (warning)
80
+
81
+ **Requires:** Every context relationship carries at least one comment. Opt-in: set options.rules.commentsRequired on the workspace.
82
+
83
+ **Why it matters:** A relationship is a claim about how two teams meet; without a note saying where that shows up in the real system, nobody can tell whether the map is still true.
84
+
85
+ **Usual fix:** Add a comment to the relationship saying what backs it in the code, or turn options.rules.commentsRequired off while the evidence layer is still being written.