@alveolus/arch 0.5.0 → 0.6.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.
- package/dist/bin.mjs +1 -1
- package/dist/{docs-B1Tzkjrn.mjs → docs-B6M9DxTM.mjs} +449 -56
- package/dist/docs-B6M9DxTM.mjs.map +1 -0
- package/dist/index.d.mts +29 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/docs/core/strategic/open-host-services.md +1 -1
- package/docs/rules/index.md +7 -3
- package/docs/rules/strategic/no-cross-context-import.md +34 -5
- package/docs/rules/strategic/no-leaky-host-service.md +59 -6
- package/docs/rules/strategic/no-shared-state.md +37 -11
- package/docs/rules/strategic/no-unmapped-context.md +17 -11
- package/package.json +1 -1
- package/dist/docs-B1Tzkjrn.mjs.map +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -76,6 +76,9 @@ interface MemberProps {
|
|
|
76
76
|
readonly parameterTypes: readonly ClassType[];
|
|
77
77
|
readonly returns: ReturnShape | undefined;
|
|
78
78
|
readonly holdsCollection: boolean;
|
|
79
|
+
readonly receivesFunction: boolean;
|
|
80
|
+
readonly erasedType: string | undefined;
|
|
81
|
+
readonly opaqueValue: string | undefined;
|
|
79
82
|
}
|
|
80
83
|
declare class Member {
|
|
81
84
|
readonly name: string;
|
|
@@ -90,6 +93,9 @@ declare class Member {
|
|
|
90
93
|
readonly parameterTypes: readonly ClassType[];
|
|
91
94
|
readonly returns: ReturnShape | undefined;
|
|
92
95
|
readonly holdsCollection: boolean;
|
|
96
|
+
readonly receivesFunction: boolean;
|
|
97
|
+
readonly erasedType: string | undefined;
|
|
98
|
+
readonly opaqueValue: string | undefined;
|
|
93
99
|
constructor(props: MemberProps);
|
|
94
100
|
get isPublicInstance(): boolean;
|
|
95
101
|
get isField(): boolean;
|
|
@@ -144,6 +150,15 @@ declare class Dependency {
|
|
|
144
150
|
isOnPackage(name: string): boolean;
|
|
145
151
|
}
|
|
146
152
|
//#endregion
|
|
153
|
+
//#region src/model/dependencies/global-channel.d.ts
|
|
154
|
+
type GlobalAccess = "reads" | "writes";
|
|
155
|
+
declare class GlobalChannel {
|
|
156
|
+
readonly line: number;
|
|
157
|
+
readonly name: string;
|
|
158
|
+
readonly access: GlobalAccess;
|
|
159
|
+
constructor(line: number, name: string, access: GlobalAccess);
|
|
160
|
+
}
|
|
161
|
+
//#endregion
|
|
147
162
|
//#region src/model/dependencies/global-use.d.ts
|
|
148
163
|
type GlobalOrigin = "ecmascript" | "host";
|
|
149
164
|
type GlobalEffect = "clock" | "randomness";
|
|
@@ -155,6 +170,15 @@ declare class GlobalUse {
|
|
|
155
170
|
constructor(line: number, name: string, origin: GlobalOrigin, effect: GlobalEffect | undefined);
|
|
156
171
|
}
|
|
157
172
|
//#endregion
|
|
173
|
+
//#region src/model/dependencies/module-reach.d.ts
|
|
174
|
+
declare class ModuleReach {
|
|
175
|
+
readonly line: number;
|
|
176
|
+
readonly expression: string;
|
|
177
|
+
readonly means: string;
|
|
178
|
+
readonly module: ClassType;
|
|
179
|
+
constructor(line: number, expression: string, means: string, module: ClassType);
|
|
180
|
+
}
|
|
181
|
+
//#endregion
|
|
158
182
|
//#region src/model/dependencies/wiring.d.ts
|
|
159
183
|
interface WiringLink {
|
|
160
184
|
readonly text: string;
|
|
@@ -202,7 +226,9 @@ interface SourceFileProps {
|
|
|
202
226
|
readonly statements: readonly TopLevelStatement[];
|
|
203
227
|
readonly throws: readonly Throw[];
|
|
204
228
|
readonly globals: readonly GlobalUse[];
|
|
229
|
+
readonly globalChannels: readonly GlobalChannel[];
|
|
205
230
|
readonly wirings: readonly Wiring[];
|
|
231
|
+
readonly moduleReaches: readonly ModuleReach[];
|
|
206
232
|
readonly disables: readonly DisableComment[];
|
|
207
233
|
}
|
|
208
234
|
declare class SourceFile {
|
|
@@ -212,7 +238,9 @@ declare class SourceFile {
|
|
|
212
238
|
readonly statements: readonly TopLevelStatement[];
|
|
213
239
|
readonly throws: readonly Throw[];
|
|
214
240
|
readonly globals: readonly GlobalUse[];
|
|
241
|
+
readonly globalChannels: readonly GlobalChannel[];
|
|
215
242
|
readonly wirings: readonly Wiring[];
|
|
243
|
+
readonly moduleReaches: readonly ModuleReach[];
|
|
216
244
|
readonly disables: readonly DisableComment[];
|
|
217
245
|
private readonly lines;
|
|
218
246
|
constructor(props: SourceFileProps);
|
|
@@ -505,6 +533,7 @@ export declare class TsMorphImporter extends Importer {
|
|
|
505
533
|
private readonly statements;
|
|
506
534
|
private readonly disables;
|
|
507
535
|
private readonly throws;
|
|
536
|
+
private readonly globalChannels;
|
|
508
537
|
constructor(sources: Project);
|
|
509
538
|
static fromTsConfig(tsConfigFilePath: string): TsMorphImporter;
|
|
510
539
|
resolves(packageName: string, scope: ImportScope): boolean;
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":["Project","FileTarget","Project","Project","MorphProject","Project"],"sources":["../src/architecture/allowed-packages.ts","../src/conventions/core-api.ts","../src/conventions/layers.ts","../src/conventions/building-blocks.ts","../src/model/classes/named-type.ts","../src/model/classes/class-type.ts","../src/model/classes/heritage.ts","../src/model/classes/return-shape.ts","../src/model/classes/member.ts","../src/model/classes/class-declaration.ts","../src/model/dependencies/dependency.ts","../src/model/dependencies/global-use.ts","../src/model/dependencies/wiring.ts","../src/model/statements/disable-comment.ts","../src/model/statements/throw.ts","../src/model/statements/top-level-statement.ts","../src/model/source-file.ts","../src/model/project.ts","../src/architecture/core-api.ts","../src/architecture/context-map.ts","../src/architecture/settings.ts","../src/architecture/paths/location.ts","../src/architecture/paths/place.ts","../src/architecture/building-blocks.ts","../src/architecture/crossing.ts","../src/architecture/layers.ts","../src/architecture/paths/layer-shape.ts","../src/architecture/architecture.ts","../src/rules/framework/finding.ts","../src/rules/framework/wording.ts","../src/rules/framework/rule.ts","../src/rules/tooling/no-loose-disable.rule.ts","../src/rules/registry.ts","../src/check/violation.ts","../src/check/baseline.ts","../src/importer/importer.ts","../src/importer/ts-morph/ts-morph-importer.ts","../src/check/checker.ts","../src/check/report.ts","../src/docs/page.ts","../src/docs/docs.ts","../src/cli/cli.ts","../src/config/alveolus-config.ts","../src/config/config.ts","../src/config/config-loader.ts","../src/config/define-config.ts","../src/init/init.ts"],"mappings":";;KAAY,sBAAsB,SAAS;qBAE9B;mBACO;EAAnB,YAAmB,WAA2B;EAE9C,IAAW;EAIX,eAAsB,qBAAqB;EAW3C,aAAoB;EAQpB,KAAY,OAAO,kBAAkB;;;;cC1BzB;KAmBD,mBAAmB;cAElB;KAED,qBAAqB;;;cCzBpB;KAED,gBAAgB;;;UCCX;WACP,OAAO;WACP;WACA;;;;UCNO;WACP;WACA;;;;cCAG;WAEX;WACA;WACA,kBAAkC;EAHnC,YACC,cACA,gCACA,kBAAkC;MAGxB;MAIA;;;;UCXK;WACP;WACA;WACA,gBAAgB;;UAGT;WACP;WACA,wBAAwB;;;;UCRjB;WACP,OAAO;WACP,gBAAgB;;;;KCDd;KAEA;UAEK;WACP;WACA;WACA,MAAM;WACN,YAAY;WACZ;WACA;WACA;WACA;WACA,qBAAqB;WACrB,yBAAyB;WACzB,SAAS;WACT;;cAGG;WACI;WACA;WACA,MAAM;WACN,YAAY;WACZ;WACA;WACA;WACA;WACA,qBAAqB;WACrB,yBAAyB;WACzB,SAAS;WACT;EAEhB,YAAmB,OAAO;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":["Project","FileTarget","Project","Project","MorphProject","Project"],"sources":["../src/architecture/allowed-packages.ts","../src/conventions/core-api.ts","../src/conventions/layers.ts","../src/conventions/building-blocks.ts","../src/model/classes/named-type.ts","../src/model/classes/class-type.ts","../src/model/classes/heritage.ts","../src/model/classes/return-shape.ts","../src/model/classes/member.ts","../src/model/classes/class-declaration.ts","../src/model/dependencies/dependency.ts","../src/model/dependencies/global-channel.ts","../src/model/dependencies/global-use.ts","../src/model/dependencies/module-reach.ts","../src/model/dependencies/wiring.ts","../src/model/statements/disable-comment.ts","../src/model/statements/throw.ts","../src/model/statements/top-level-statement.ts","../src/model/source-file.ts","../src/model/project.ts","../src/architecture/core-api.ts","../src/architecture/context-map.ts","../src/architecture/settings.ts","../src/architecture/paths/location.ts","../src/architecture/paths/place.ts","../src/architecture/building-blocks.ts","../src/architecture/crossing.ts","../src/architecture/layers.ts","../src/architecture/paths/layer-shape.ts","../src/architecture/architecture.ts","../src/rules/framework/finding.ts","../src/rules/framework/wording.ts","../src/rules/framework/rule.ts","../src/rules/tooling/no-loose-disable.rule.ts","../src/rules/registry.ts","../src/check/violation.ts","../src/check/baseline.ts","../src/importer/importer.ts","../src/importer/ts-morph/ts-morph-importer.ts","../src/check/checker.ts","../src/check/report.ts","../src/docs/page.ts","../src/docs/docs.ts","../src/cli/cli.ts","../src/config/alveolus-config.ts","../src/config/config.ts","../src/config/config-loader.ts","../src/config/define-config.ts","../src/init/init.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as TsMorphImporter, c as Config, d as RuleRegistry, f as Rule, h as Baseline, i as Init, l as Report, m as AllowedPackages, n as Page, o as Importer, p as Architecture, r as Cli, s as ConfigLoader, t as Docs, u as Checker } from "./docs-
|
|
1
|
+
import { a as TsMorphImporter, c as Config, d as RuleRegistry, f as Rule, h as Baseline, i as Init, l as Report, m as AllowedPackages, n as Page, o as Importer, p as Architecture, r as Cli, s as ConfigLoader, t as Docs, u as Checker } from "./docs-B6M9DxTM.mjs";
|
|
2
2
|
//#region src/config/define-config.ts
|
|
3
3
|
function defineConfig(config) {
|
|
4
4
|
return config;
|
|
@@ -265,7 +265,7 @@ npx alveolus arch check
|
|
|
265
265
|
|
|
266
266
|
<div class="al-cards">
|
|
267
267
|
<div class="al-card"><span class="al-card-title"><a href="../../rules/strategic/no-cross-context-import"><code>strategic/no-cross-context-import</code></a></span>Another context imports this class, and nothing else of the catalog.</div>
|
|
268
|
-
<div class="al-card"><span class="al-card-title"><a href="../../rules/strategic/no-leaky-host-service"><code>strategic/no-leaky-host-service</code></a></span>It answers in the published language, never with a class of the catalog.</div>
|
|
268
|
+
<div class="al-card"><span class="al-card-title"><a href="../../rules/strategic/no-leaky-host-service"><code>strategic/no-leaky-host-service</code></a></span>It answers in the published language, never with a class of the catalog or an untyped result, and it receives data, never a callback.</div>
|
|
269
269
|
<div class="al-card"><span class="al-card-title"><a href="../../rules/tactical/no-misplaced-class"><code>tactical/no-misplaced-class</code></a></span>It stays under <code>driving/</code>.</div>
|
|
270
270
|
</div>
|
|
271
271
|
|
package/docs/rules/index.md
CHANGED
|
@@ -54,7 +54,7 @@ its own business. The "Applies to" line of each rule page says which it is.
|
|
|
54
54
|
| [`strategic/no-cross-context-import`](./strategic/no-cross-context-import.md) | An import from another bounded context that is not its open host service, a file the analysis does not see, a composition root that re-exports. |
|
|
55
55
|
| [`strategic/no-fat-shared-kernel`](./strategic/no-fat-shared-kernel.md) | An aggregate, a repository or a handler in the shared kernel. |
|
|
56
56
|
| [`strategic/no-shared-state`](./strategic/no-shared-state.md) | A static field of the shared kernel that holds state. |
|
|
57
|
-
| [`strategic/no-leaky-host-service`](./strategic/no-leaky-host-service.md) | An open host service that exposes a class of its context instead of the published language. |
|
|
57
|
+
| [`strategic/no-leaky-host-service`](./strategic/no-leaky-host-service.md) | An open host service that exposes a class of its context, receives a function or returns an erased type, instead of speaking the published language. |
|
|
58
58
|
| [`strategic/no-unmapped-context`](./strategic/no-unmapped-context.md) | A context consuming one the context map does not allow, or two contexts that depend on each other. |
|
|
59
59
|
|
|
60
60
|
## Layers
|
|
@@ -142,8 +142,12 @@ root of `src/`: a test file that re-exports another context would otherwise carr
|
|
|
142
142
|
every rule.
|
|
143
143
|
|
|
144
144
|
Code loaded at runtime counts the same way, wherever it is: `node:module` (`createRequire`),
|
|
145
|
-
`node:vm`, `module.require`, `process.getBuiltinModule`, `eval
|
|
146
|
-
|
|
145
|
+
`node:vm`, `module.require`, `process.getBuiltinModule`, `eval` and the `Function` constructor.
|
|
146
|
+
They are recognised by their type as well as by their name: an alias of `Function` or `eval`, the
|
|
147
|
+
`constructor` of a function (`(() => 0).constructor`, the `AsyncFunction` constructor),
|
|
148
|
+
`Reflect.construct(Function, …)`, and a lookup on `globalThis` whose key is computed, such as
|
|
149
|
+
`Reflect.get(globalThis, name)`. To read a JSON file, import it:
|
|
150
|
+
`import pkg from "../package.json" with { type: "json" }`.
|
|
147
151
|
|
|
148
152
|
```
|
|
149
153
|
src/ordering/domain/services/pricing.service.ts
|
|
@@ -44,18 +44,33 @@ The shared kernel imports no bounded context at all. Every context may import th
|
|
|
44
44
|
No file of a context or of the shared kernel imports a file the analysis does not see: ignored,
|
|
45
45
|
unresolved, computed at runtime, or outside the declared contexts and the shared kernel. Such a
|
|
46
46
|
file could re-export another context, and the rule could not tell which one. Code loaded at
|
|
47
|
-
runtime, with `createRequire`, `eval`, `
|
|
47
|
+
runtime, with `createRequire`, `eval`, the `Function` constructor or `node:vm`, is reported the
|
|
48
|
+
same way, whether it is named or reached through its type, see
|
|
49
|
+
[Every import counts](../index.md#every-import-counts).
|
|
48
50
|
|
|
49
51
|
Every form of import counts, see [Every import counts](../index.md#every-import-counts).
|
|
50
52
|
|
|
53
|
+
The global object is no channel either. Writing or reading a name of `globalThis`, `global`,
|
|
54
|
+
`window` or `self` that no file declares is reported, wherever it is written: an assignment,
|
|
55
|
+
`Reflect.set`, `Reflect.get`, `Object.assign`, `Object.defineProperty`, or `(globalThis as any).x`.
|
|
56
|
+
A name the library declares, such as a polyfill of `globalThis.crypto` or a read of `fetch`, is
|
|
57
|
+
fine, and a global the project declares with `declare global` counts as an import of the file that
|
|
58
|
+
declares it.
|
|
59
|
+
|
|
51
60
|
The wiring follows the same contract. In the composition roots and the files at the root of
|
|
52
61
|
`src/`, a value of one context given to another one, such as
|
|
53
|
-
`new
|
|
62
|
+
`new OrderingModule({ prices: () => this.catalog.commands.changePrice })`, is an
|
|
54
63
|
`OpenHostService` of the giving context, or one of its methods. A handler, a repository or any
|
|
55
64
|
other class of its model is reported, even when the receiving side declares a type of the same
|
|
56
65
|
shape. Whether the receiving context may consume the giving one at all is checked by
|
|
57
66
|
[`strategic/no-unmapped-context`](./no-unmapped-context.md).
|
|
58
67
|
|
|
68
|
+
So that what crosses can be seen, a module of another context is read by its properties
|
|
69
|
+
(`this.catalog.api`), or handed whole to the constructor of another module
|
|
70
|
+
(`new OrderingModule(this.catalog)`). Any other way into it is reported: brackets
|
|
71
|
+
(`this.catalog["commands"]`), `Reflect.get`, a spread, destructuring, or passing it to a function,
|
|
72
|
+
such as a helper of `src/` or `get` from lodash, that could hand back anything.
|
|
73
|
+
|
|
59
74
|
## What it reports
|
|
60
75
|
|
|
61
76
|
```
|
|
@@ -94,9 +109,18 @@ src/ordering/driven/memory/adapters/memory-prices.adapter.ts
|
|
|
94
109
|
|
|
95
110
|
src/app.module.ts
|
|
96
111
|
14 error strategic/no-cross-context-import: Gives
|
|
97
|
-
this.
|
|
98
|
-
|
|
99
|
-
|
|
112
|
+
this.catalog.commands.changePrice, from catalog, to ordering: only an
|
|
113
|
+
OpenHostService of another bounded context may cross, in an import or
|
|
114
|
+
in the wiring.
|
|
115
|
+
21 error strategic/no-cross-context-import: Reaches into this.catalog
|
|
116
|
+
with Reflect.get: in the wiring, a module of another context is read
|
|
117
|
+
by its properties, so that what crosses can be seen.
|
|
118
|
+
|
|
119
|
+
src/catalog/catalog.module.ts
|
|
120
|
+
18 error strategic/no-cross-context-import: Writes
|
|
121
|
+
globalThis.catalog:prices, which no file declares: two contexts can
|
|
122
|
+
meet there without the context map showing it. Integrate through an
|
|
123
|
+
open host service, or publish an integration event.
|
|
100
124
|
```
|
|
101
125
|
|
|
102
126
|
## Fix it
|
|
@@ -151,6 +175,11 @@ bounded context. Move what it needs into the shared kernel, or keep it in the co
|
|
|
151
175
|
should build values of its own context.
|
|
152
176
|
- A file that matches `ignore` in `alveolus.config.ts` is not analysed at all, and no file of a
|
|
153
177
|
context may import it. Review a change to `ignore` as you would review a rule turned off.
|
|
178
|
+
- A loader reached through a value typed `any` that is neither a `constructor` nor a lookup on
|
|
179
|
+
`globalThis`, such as `(loaders as any).run(code)`, is not recognised. In review, `any` around a
|
|
180
|
+
call is a question to ask.
|
|
181
|
+
- In the wiring, a module chosen by a condition (`flag ? this.catalog : this.ordering`) and then
|
|
182
|
+
reached into is not recognised as a module: its type is a union.
|
|
154
183
|
- In the wiring, a value whose type is erased on the way, by a cast or a container token written
|
|
155
184
|
as a string, is not seen. In review, the composition root holds no cast.
|
|
156
185
|
:::
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: "Architecture rule: an open host service speaks the published language
|
|
2
|
+
description: "Architecture rule: an open host service speaks the published language: it never exposes a class of its bounded context, never receives a function and never returns an erased type."
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# no-leaky-host-service
|
|
6
6
|
|
|
7
|
-
An open host service speaks the published language:
|
|
8
|
-
offers.
|
|
7
|
+
An open host service speaks the published language: data in, data out. No class of its context
|
|
8
|
+
appears in what it offers, it receives no function, and what it returns has a type.
|
|
9
9
|
|
|
10
10
|
<dl class="al-glance">
|
|
11
11
|
<dt>Rule</dt><dd><code>strategic/no-leaky-host-service</code></dd>
|
|
12
12
|
<dt>Category</dt><dd><a href="/rules/#strategic">Strategic</a>: what crosses a bounded context</dd>
|
|
13
|
-
<dt>Reports</dt><dd>A class of the project in the parameters, results, properties or getters of an open host service</dd>
|
|
13
|
+
<dt>Reports</dt><dd>A class of the project in the parameters, results, properties or getters of an open host service; a function among its parameters; <code>unknown</code>, <code>any</code> or <code>object</code> as a result</dd>
|
|
14
14
|
<dt>Applies to</dt><dd>Every class that implements <code>OpenHostService</code></dd>
|
|
15
15
|
<dt>Turn off</dt><dd><a href="#turn-it-off"><code>"strategic/no-leaky-host-service": "off"</code></a></dd>
|
|
16
16
|
</dl>
|
|
@@ -39,6 +39,13 @@ parameters and its result, followed into generics, `Promise`, arrays and object
|
|
|
39
39
|
| A class of an installed package | ✅ |
|
|
40
40
|
| Any other class of the project: an aggregate, an entity, a value object, an identifier, a domain event | ❌ |
|
|
41
41
|
|
|
42
|
+
Its parameters carry data, never behaviour: a function is reported wherever it sits, as a
|
|
43
|
+
parameter, a property of an object parameter (`{ onSettled: () => void }`), an item of an array or
|
|
44
|
+
a method of an interface it receives. A class, such as `Date` or `AbortSignal`, is not a function.
|
|
45
|
+
|
|
46
|
+
Its results have a type: `unknown`, `any` and `object`, alone or in a `Promise`, are reported. A
|
|
47
|
+
parameter may be `unknown`, for a payload the service validates.
|
|
48
|
+
|
|
42
49
|
The constructor and private members are left out: they wire the service, other contexts never see
|
|
43
50
|
them.
|
|
44
51
|
|
|
@@ -49,6 +56,13 @@ src/catalog/driving/in-process/catalog-api.ts
|
|
|
49
56
|
6 error strategic/no-leaky-host-service: CatalogApi.product exposes
|
|
50
57
|
Product, an AggregateRoot of catalog: an open host service speaks
|
|
51
58
|
the published language.
|
|
59
|
+
9 error strategic/no-leaky-host-service: CatalogApi.onRestockNeeded
|
|
60
|
+
receives a function: an open host service receives data, never
|
|
61
|
+
behaviour, or the upstream context ends up running code of another
|
|
62
|
+
one. To let another context react, publish an integration event.
|
|
63
|
+
12 error strategic/no-leaky-host-service: CatalogApi.inspect returns
|
|
64
|
+
unknown: an open host service returns a type of its published
|
|
65
|
+
language, so that what leaves the context can be seen.
|
|
52
66
|
```
|
|
53
67
|
|
|
54
68
|
## Fix it
|
|
@@ -79,12 +93,51 @@ export class CatalogApi implements OpenHostService {
|
|
|
79
93
|
|
|
80
94
|
</div>
|
|
81
95
|
|
|
96
|
+
### Publish an event instead of taking a callback
|
|
97
|
+
|
|
98
|
+
So that the upstream context never runs code of another one, it does not accept a callback to call
|
|
99
|
+
when something happens: it publishes an integration event, and the downstream context subscribes
|
|
100
|
+
to it. The context map then says the truth: the downstream context consumes the upstream one, not
|
|
101
|
+
the other way round.
|
|
102
|
+
|
|
103
|
+
<div class="al-compare">
|
|
104
|
+
|
|
105
|
+
```ts [❌ Avoid: src/catalog/driving/in-process/catalog-api.ts]
|
|
106
|
+
export class CatalogApi implements OpenHostService {
|
|
107
|
+
onRestockNeeded(callback: RestockCallback): void {
|
|
108
|
+
this.handlers.connect(callback);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
```ts [✅ Prefer: src/catalog/application/translators/catalog-events.translator.ts]
|
|
114
|
+
export class CatalogEventsTranslator extends EventTranslator<
|
|
115
|
+
RestockNeeded,
|
|
116
|
+
RestockNeededRepresentation
|
|
117
|
+
> {
|
|
118
|
+
protected readonly source = "catalog";
|
|
119
|
+
|
|
120
|
+
translate(event: RestockNeeded, context: IntegrationEventContext) {
|
|
121
|
+
return this.wrap(event, context, {
|
|
122
|
+
payload: { productId: event.productId.value, quantity: event.quantity },
|
|
123
|
+
type: "catalog.restock-needed",
|
|
124
|
+
version: 1,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
</div>
|
|
131
|
+
|
|
82
132
|
## Limits
|
|
83
133
|
|
|
84
134
|
::: warning What the rule cannot see
|
|
85
|
-
-
|
|
86
|
-
|
|
135
|
+
- A plain object typed by hand with the fields of the aggregate: the rule follows classes and
|
|
136
|
+
erased types, not shapes. In review, a method of an open host service returns a type of
|
|
87
137
|
`published-language/`.
|
|
138
|
+
- A function smuggled through a parameter typed `unknown`: parameters may be `unknown`, for a
|
|
139
|
+
payload to validate, so the rule cannot tell. In review, an `unknown` parameter is parsed, never
|
|
140
|
+
called.
|
|
88
141
|
- A DTO class of the context is a leak too, on purpose: the published language is made of types,
|
|
89
142
|
not classes.
|
|
90
143
|
:::
|
|
@@ -17,10 +17,10 @@ by every context at once: a channel between them that no import and no context m
|
|
|
17
17
|
|
|
18
18
|
## Why
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
Someone adds a `ServiceRegistry` to the shared kernel:
|
|
22
|
-
|
|
23
|
-
depends on
|
|
20
|
+
The catalog needs to cancel the orders of a withdrawn product, and the context map says the
|
|
21
|
+
catalog consumes nothing. Someone adds a `ServiceRegistry` to the shared kernel: ordering registers
|
|
22
|
+
its handler under a name, the catalog resolves it. No file of the catalog imports ordering, every
|
|
23
|
+
import rule passes, and the catalog now depends on ordering in a way nobody decided.
|
|
24
24
|
|
|
25
25
|
::: tip The fix
|
|
26
26
|
Contexts talk through an [open host service](../../core/strategic/open-host-services.md), consumed
|
|
@@ -39,14 +39,24 @@ Every static field of a class in the shared kernel:
|
|
|
39
39
|
| `static readonly ZERO = new Money({ amount: 0 })` | ✅ |
|
|
40
40
|
| `static readonly PRECISION = 2` | ✅ |
|
|
41
41
|
| `static readonly CURRENCIES: readonly string[] = ["EUR"]`, a `ReadonlyMap`, a `ReadonlySet` | ✅ |
|
|
42
|
+
| `static readonly NONE = new CustomerId("")`: an identifier | ✅ |
|
|
43
|
+
| `static readonly RATE = new Decimal("1.1")`: a class of a package listed in `domainDependencies` | ✅ |
|
|
42
44
|
| `static count = 0`: without `readonly` | ❌ |
|
|
43
45
|
| `static readonly services = new Map()`: a `Map`, a `Set`, a `WeakMap`, a `WeakSet` | ❌ |
|
|
44
46
|
| `static readonly names: string[] = []`: an array that is not `readonly` | ❌ |
|
|
45
47
|
| `static readonly byId: Record<string, Handler> = {}`: an index signature | ❌ |
|
|
46
48
|
| `static readonly options = { strict: true }`: an object literal | ❌ |
|
|
49
|
+
| `static readonly shared = new ServiceDirectory()`: a class that is not a value, such as a singleton | ❌ |
|
|
50
|
+
| `static readonly bus = new EventEmitter()`: a class of a package outside `domainDependencies` | ❌ |
|
|
51
|
+
| `static readonly resolve = makeResolver()`: a function, which can close over any state | ❌ |
|
|
47
52
|
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
A static field holds a value: a primitive, a value object, an identifier, a class of a package
|
|
54
|
+
listed in `domainDependencies`, or a `readonly` collection of them. Static methods are not fields:
|
|
55
|
+
`static of(amount: number)` is fine.
|
|
56
|
+
|
|
57
|
+
Instance fields are not checked: an adapter of the shared kernel may hold its own state. When each
|
|
58
|
+
context builds its own instance, nobody else reaches it; when the composition root at the root of
|
|
59
|
+
`src/` hands the same instance to two contexts, it is a channel, see [Limits](#limits).
|
|
50
60
|
|
|
51
61
|
Module-level state, such as `let current` or `const services = new Map()` at the top of a file, is
|
|
52
62
|
reported by [`tactical/no-loose-code`](../tactical/no-loose-code.md).
|
|
@@ -59,6 +69,12 @@ src/shared-kernel/driven/memory/registry/service-registry.ts
|
|
|
59
69
|
collection in a static field: every context reaches the same one, a
|
|
60
70
|
channel the context map does not show. The shared kernel shares a
|
|
61
71
|
model, not state: integrate through an open host service.
|
|
72
|
+
3 error strategic/no-shared-state: ServiceRegistry.shared holds a
|
|
73
|
+
ServiceRegistry in a static field, which can keep state every context
|
|
74
|
+
reaches, a channel the context map does not show. A static field of
|
|
75
|
+
the shared kernel holds a value: a primitive, a value object, an
|
|
76
|
+
identifier, a class of domainDependencies, or a readonly collection
|
|
77
|
+
of them.
|
|
62
78
|
```
|
|
63
79
|
|
|
64
80
|
## Fix it
|
|
@@ -74,17 +90,27 @@ anti-corruption layer. The composition root passes the service; no registry is n
|
|
|
74
90
|
So that a constant cannot become a channel, make it `readonly` and give it an immutable type: a
|
|
75
91
|
value object, a primitive, a `readonly` array, a `ReadonlyMap`.
|
|
76
92
|
|
|
93
|
+
### Build services in the composition root
|
|
94
|
+
|
|
95
|
+
So that every context gets the instance the composition root decides, a clock, a bus or a
|
|
96
|
+
directory is built there and passed to the constructors that need it, not kept in a static
|
|
97
|
+
`instance` field.
|
|
98
|
+
|
|
77
99
|
## Limits
|
|
78
100
|
|
|
79
101
|
::: warning What the rule cannot see
|
|
80
|
-
- A
|
|
81
|
-
`
|
|
82
|
-
|
|
83
|
-
service.
|
|
102
|
+
- A value object or a class of `domainDependencies` is trusted to be immutable: a value object
|
|
103
|
+
that keeps a `Map` in a private field passes. In review, a value object changes by returning a
|
|
104
|
+
new one.
|
|
84
105
|
- A `Readonly<Record<…>>` counts as a collection, because it has an index signature: use a
|
|
85
106
|
`ReadonlyMap` for a constant dictionary.
|
|
107
|
+
- An instance handed to two contexts by the composition root at the root of `src/` is not checked:
|
|
108
|
+
the database and the outbox are shared that way on purpose, and a registry passed the same way
|
|
109
|
+
goes unseen. In review, the root passes the same instance to several contexts only for the
|
|
110
|
+
infrastructure every context needs: the database, the outbox, the clock.
|
|
86
111
|
- State held outside the shared kernel is not checked: a package with a global container, such as
|
|
87
|
-
the default container of a dependency injection library
|
|
112
|
+
the default container of a dependency injection library. A name of `globalThis` that no file
|
|
113
|
+
declares is reported by [`strategic/no-cross-context-import`](./no-cross-context-import.md).
|
|
88
114
|
:::
|
|
89
115
|
|
|
90
116
|
## Turn it off
|
|
@@ -47,15 +47,15 @@ Imports of the shared kernel are not consumptions: every context may import it.
|
|
|
47
47
|
|
|
48
48
|
A context can consume another one without importing it: `app.module.ts` sees every module, and
|
|
49
49
|
can hand a value of one context to another. Ledger declares a port, its adapter calls whatever it
|
|
50
|
-
is given, and the root composition gives it a handler of
|
|
50
|
+
is given, and the root composition gives it a handler of payments:
|
|
51
51
|
|
|
52
52
|
```ts [src/app.module.ts]
|
|
53
53
|
this.ledger = new LedgerModule({
|
|
54
|
-
|
|
54
|
+
transfers: () => this.payments.commands.settleTransfer,
|
|
55
55
|
});
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
No file of ledger imports
|
|
58
|
+
No file of ledger imports payments, yet ledger now consumes it. In the composition roots and the
|
|
59
59
|
files at the root of `src/`, every value given to a class, a function or a field of one context
|
|
60
60
|
(an argument, a property of an object, the body of an arrow function, an assignment, a variable
|
|
61
61
|
typed by that context) is read: when it comes from another context, by where it is declared or by
|
|
@@ -68,16 +68,18 @@ from it is.
|
|
|
68
68
|
```
|
|
69
69
|
src/ledger/driven/payments/adapters/payment-status.adapter.ts
|
|
70
70
|
2 error strategic/no-unmapped-context: ledger consumes payments, which the
|
|
71
|
-
context map does not allow: reverse the dependency
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
context map does not allow: reverse the dependency with an
|
|
72
|
+
integration event that ledger publishes and payments subscribes to,
|
|
73
|
+
not with a callback; or if ledger really is downstream of payments,
|
|
74
|
+
add payments to contextMap.ledger.consumes.
|
|
74
75
|
|
|
75
76
|
src/app.module.ts
|
|
76
77
|
14 error strategic/no-unmapped-context: ledger receives
|
|
77
|
-
this.
|
|
78
|
-
context map does not allow: reverse the dependency
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
this.payments.commands.settleTransfer from payments here, which the
|
|
79
|
+
context map does not allow: reverse the dependency with an
|
|
80
|
+
integration event that ledger publishes and payments subscribes to,
|
|
81
|
+
not with a callback; or if ledger really is downstream of payments,
|
|
82
|
+
add payments to contextMap.ledger.consumes.
|
|
81
83
|
```
|
|
82
84
|
|
|
83
85
|
A map that would allow it is refused before the check:
|
|
@@ -117,6 +119,10 @@ So that `Ledger` stays upstream, it does not ask `Payments` anything: it publish
|
|
|
117
119
|
`TransferSettled` in its [published language](../../core/strategic/published-language.md), and
|
|
118
120
|
`Payments` reacts to it.
|
|
119
121
|
|
|
122
|
+
A callback is not a reversal. `Ledger` calling a function that `Payments` registered on its open
|
|
123
|
+
host service still runs code of `Payments` when `Ledger` decides: the dependency is the same, only
|
|
124
|
+
hidden from the map. An event carries data, and `Ledger` does not know who reacts.
|
|
125
|
+
|
|
120
126
|
## Limits
|
|
121
127
|
|
|
122
128
|
::: warning What the rule cannot see
|
|
@@ -125,7 +131,7 @@ So that `Ledger` stays upstream, it does not ask `Payments` anything: it publish
|
|
|
125
131
|
another context is an import of its open host service.
|
|
126
132
|
- A value whose type is erased on the way: a cast (`as unknown as Handler`, `any`) in the
|
|
127
133
|
composition root, or a container token written as a string, such as NestJS
|
|
128
|
-
`{ provide: "
|
|
134
|
+
`{ provide: "transfers", useFactory: … }`. In review, the composition root holds no cast, and
|
|
129
135
|
a token is the abstract class of a port.
|
|
130
136
|
- With the rule off, the wiring is no longer checked against the map, even though
|
|
131
137
|
[`strategic/no-cross-context-import`](./no-cross-context-import.md) still checks what crosses.
|