@alveolus/arch 0.3.0 → 0.5.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/index.d.mts CHANGED
@@ -69,11 +69,13 @@ interface MemberProps {
69
69
  readonly kind: MemberKind;
70
70
  readonly visibility: Visibility;
71
71
  readonly isStatic: boolean;
72
+ readonly isReadonly: boolean;
72
73
  readonly isParameterProperty: boolean;
73
74
  readonly isCallable: boolean;
74
75
  readonly valueTypes: readonly ClassType[];
75
76
  readonly parameterTypes: readonly ClassType[];
76
77
  readonly returns: ReturnShape | undefined;
78
+ readonly holdsCollection: boolean;
77
79
  }
78
80
  declare class Member {
79
81
  readonly name: string;
@@ -81,11 +83,13 @@ declare class Member {
81
83
  readonly kind: MemberKind;
82
84
  readonly visibility: Visibility;
83
85
  readonly isStatic: boolean;
86
+ readonly isReadonly: boolean;
84
87
  readonly isParameterProperty: boolean;
85
88
  readonly isCallable: boolean;
86
89
  readonly valueTypes: readonly ClassType[];
87
90
  readonly parameterTypes: readonly ClassType[];
88
91
  readonly returns: ReturnShape | undefined;
92
+ readonly holdsCollection: boolean;
89
93
  constructor(props: MemberProps);
90
94
  get isPublicInstance(): boolean;
91
95
  get isField(): boolean;
@@ -119,8 +123,8 @@ declare class ClassDeclaration {
119
123
  }
120
124
  //#endregion
121
125
  //#region src/model/dependencies/dependency.d.ts
122
- type DependencyForm = "import" | "re-export" | "inline type" | "dynamic import" | "require" | "global";
123
- type TargetVisibility = "analysed" | "ignored" | "unresolved";
126
+ type DependencyForm = "import" | "re-export" | "inline type" | "dynamic import" | "require" | "reference" | "augmentation" | "dynamic load" | "global";
127
+ type TargetVisibility = "analysed" | "ignored" | "unresolved" | "dynamic";
124
128
  type DependencyTarget = {
125
129
  readonly kind: "file";
126
130
  readonly path: string;
@@ -151,6 +155,19 @@ declare class GlobalUse {
151
155
  constructor(line: number, name: string, origin: GlobalOrigin, effect: GlobalEffect | undefined);
152
156
  }
153
157
  //#endregion
158
+ //#region src/model/dependencies/wiring.d.ts
159
+ interface WiringLink {
160
+ readonly text: string;
161
+ readonly declaredIn: string | undefined;
162
+ readonly types: readonly ClassType[];
163
+ }
164
+ declare class Wiring {
165
+ readonly line: number;
166
+ readonly receiver: string;
167
+ readonly links: readonly WiringLink[];
168
+ constructor(line: number, receiver: string, links: readonly WiringLink[]);
169
+ }
170
+ //#endregion
154
171
  //#region src/model/statements/disable-comment.d.ts
155
172
  declare class DisableComment {
156
173
  readonly line: number;
@@ -185,6 +202,7 @@ interface SourceFileProps {
185
202
  readonly statements: readonly TopLevelStatement[];
186
203
  readonly throws: readonly Throw[];
187
204
  readonly globals: readonly GlobalUse[];
205
+ readonly wirings: readonly Wiring[];
188
206
  readonly disables: readonly DisableComment[];
189
207
  }
190
208
  declare class SourceFile {
@@ -194,6 +212,7 @@ declare class SourceFile {
194
212
  readonly statements: readonly TopLevelStatement[];
195
213
  readonly throws: readonly Throw[];
196
214
  readonly globals: readonly GlobalUse[];
215
+ readonly wirings: readonly Wiring[];
197
216
  readonly disables: readonly DisableComment[];
198
217
  private readonly lines;
199
218
  constructor(props: SourceFileProps);
@@ -231,16 +250,12 @@ type Upstreams = Readonly<Record<string, readonly string[]>>;
231
250
  declare class ContextMap {
232
251
  private readonly upstreams;
233
252
  constructor(upstreams: Upstreams);
234
- static ofEdges(edges: readonly {
235
- readonly from: string;
236
- readonly to: string;
237
- }[]): ContextMap;
238
253
  get contexts(): string[];
254
+ get consumed(): string[];
255
+ selfConsumers(): string[];
239
256
  allows(from: string, to: string): boolean;
240
257
  cycle(): string[] | undefined;
241
- cycleThrough(from: string, to: string): boolean;
242
258
  private cycleFrom;
243
- private reaches;
244
259
  }
245
260
  //#endregion
246
261
  //#region src/architecture/settings.d.ts
@@ -258,14 +273,14 @@ interface Settings {
258
273
  readonly contextFolders: readonly ContextFolder[];
259
274
  readonly compositionRoot: string;
260
275
  readonly extraFolders: ExtraFolders;
261
- readonly contextMap: ContextMap | undefined;
276
+ readonly contextMap: ContextMap;
262
277
  readonly domainDependencies: AllowedPackages;
263
278
  readonly applicationDependencies: AllowedPackages;
264
279
  }
265
280
  //#endregion
266
281
  //#region src/architecture/paths/location.d.ts
267
282
  type Area = "context" | "shared-kernel" | "root" | "outside";
268
- type Unseen = "ignored" | "unresolved";
283
+ type Unseen = "ignored" | "unresolved" | "dynamic";
269
284
  interface LocationProps {
270
285
  readonly area: Area;
271
286
  readonly context?: string;
@@ -320,6 +335,15 @@ declare class BuildingBlocks {
320
335
  foldersOf(layer: "domain" | "application"): readonly string[];
321
336
  }
322
337
  //#endregion
338
+ //#region src/architecture/crossing.d.ts
339
+ interface Crossing {
340
+ readonly line: number;
341
+ readonly from: string;
342
+ readonly to: string;
343
+ readonly expression: string;
344
+ readonly isOpenHostService: boolean;
345
+ }
346
+ //#endregion
323
347
  //#region src/architecture/layers.d.ts
324
348
  declare class Layers {
325
349
  isLayer(name: string): name is Layer;
@@ -353,12 +377,15 @@ export declare class Architecture {
353
377
  private readonly shape;
354
378
  private readonly layout;
355
379
  private readonly locations;
380
+ private readonly crossings;
356
381
  constructor(project: Project$1, settings: Settings);
357
382
  get files(): readonly SourceFile[];
358
383
  get domainDependencies(): AllowedPackages;
359
384
  get applicationDependencies(): AllowedPackages;
360
- get contextMap(): ContextMap | undefined;
385
+ get contextMap(): ContextMap;
361
386
  locationOf(file: SourceFile): Location;
387
+ locationOfPath(path: string): Location;
388
+ crossingsIn(file: SourceFile): Crossing[];
362
389
  locationOfTarget(target: FileTarget$1): Location;
363
390
  shapeIssueOf(location: Location): ShapeIssue | undefined;
364
391
  relativePath(path: string): string;
@@ -422,7 +449,7 @@ declare class NoLooseDisableRule extends Rule<"tooling/no-loose-disable", Messag
422
449
  }
423
450
  //#endregion
424
451
  //#region src/rules/registry.d.ts
425
- declare const ruleIds: readonly ["strategic/no-cross-context-import", "strategic/no-leaky-host-service", "strategic/no-unmapped-context", "strategic/no-fat-shared-kernel", "layers/no-impure-domain", "layers/no-outward-import", "layers/no-portless-adapter", "layers/no-driving-shortcut", "tactical/no-aggregate-reference", "tactical/no-public-field", "tactical/no-foreign-command-dependency", "tactical/no-foreign-query-dependency", "tactical/no-stateful-service", "tactical/no-thrown-failure", "tactical/no-misplaced-class", "tactical/no-loose-code", "tooling/no-loose-disable"];
452
+ declare const ruleIds: readonly ["strategic/no-cross-context-import", "strategic/no-leaky-host-service", "strategic/no-unmapped-context", "strategic/no-fat-shared-kernel", "strategic/no-shared-state", "layers/no-impure-domain", "layers/no-outward-import", "layers/no-portless-adapter", "layers/no-driving-shortcut", "tactical/no-aggregate-reference", "tactical/no-public-field", "tactical/no-foreign-command-dependency", "tactical/no-foreign-query-dependency", "tactical/no-stateful-service", "tactical/no-thrown-failure", "tactical/no-misplaced-class", "tactical/no-loose-code", "tooling/no-loose-disable"];
426
453
  type RuleId = (typeof ruleIds)[number];
427
454
  export declare class RuleRegistry {
428
455
  readonly rules: readonly Rule<RuleId>[];
@@ -463,6 +490,7 @@ interface ImportScope {
463
490
  readonly projectDir: string;
464
491
  readonly rootDir: string;
465
492
  isIgnored(path: string): boolean;
493
+ readsWiring(path: string): boolean;
466
494
  }
467
495
  export declare abstract class Importer {
468
496
  abstract read(scope: ImportScope): Project$1;
@@ -473,6 +501,7 @@ export declare abstract class Importer {
473
501
  export declare class TsMorphImporter extends Importer {
474
502
  private readonly sources;
475
503
  private readonly classes;
504
+ private readonly types;
476
505
  private readonly statements;
477
506
  private readonly disables;
478
507
  private readonly throws;
@@ -587,6 +616,9 @@ export declare class Cli {
587
616
  //#endregion
588
617
  //#region src/config/alveolus-config.d.ts
589
618
  type RuleSetting = "error" | "warn" | "info" | "off";
619
+ type ContextMapConfig = Readonly<Record<string, {
620
+ readonly consumes: readonly string[];
621
+ }>>;
590
622
  interface LayoutConfig {
591
623
  readonly extraFolders?: Readonly<Partial<Record<"domain" | "application", readonly string[]>>>;
592
624
  }
@@ -596,7 +628,7 @@ interface AlveolusConfig {
596
628
  readonly boundedContexts: Readonly<Record<string, string>>;
597
629
  readonly sharedKernel?: string;
598
630
  readonly subdomains?: Subdomains;
599
- readonly contextMap?: Upstreams;
631
+ readonly contextMap: ContextMapConfig;
600
632
  readonly compositionRoot?: string;
601
633
  readonly domainDependencies?: PackageDependencies;
602
634
  readonly applicationDependencies?: PackageDependencies;
@@ -615,13 +647,15 @@ export declare class Config implements CheckSettings {
615
647
  readonly applicationDependencies: AllowedPackages;
616
648
  readonly contextFolders: readonly ContextFolder[];
617
649
  readonly extraFolders: ExtraFolders;
618
- readonly contextMap: ContextMap | undefined;
650
+ readonly contextMap: ContextMap;
619
651
  private readonly ignored;
620
652
  private readonly rules;
653
+ private readonly layout;
621
654
  constructor(config: AlveolusConfig, projectDir: string);
622
655
  private classifiedContexts;
623
656
  private validContextMap;
624
657
  isIgnored(path: string): boolean;
658
+ readsWiring(path: string): boolean;
625
659
  severityOf(rule: RuleId): Severity | "off";
626
660
  }
627
661
  //#endregion
@@ -649,5 +683,5 @@ export declare class Init {
649
683
  private write;
650
684
  }
651
685
  //#endregion
652
- export type { AlveolusConfig, CheckSettings, Finding, FindingData, ImportScope, PackageDependencies, RuleId, RuleMeta, RuleSetting, Settings, Violation, Written };
686
+ export type { AlveolusConfig, CheckSettings, ContextMapConfig, Finding, FindingData, ImportScope, PackageDependencies, RuleId, RuleMeta, RuleSetting, Settings, Violation, Written };
653
687
  //# sourceMappingURL=index.d.mts.map
@@ -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/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/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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"}
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":"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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-DsQHpTtV.mjs";
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-B1Tzkjrn.mjs";
2
2
  //#region src/config/define-config.ts
3
3
  function defineConfig(config) {
4
4
  return config;
@@ -19,13 +19,16 @@ Nothing here requires a rewrite.
19
19
  Name the bounded contexts as the code has them today, even when they are folders named
20
20
  `modules/orders` or `features/billing`: `boundedContexts` takes any folder under `root`. Say under
21
21
  `subdomains` which ones are the [core domain](./project-layout.md#core-supporting-generic): only
22
- those are checked inside; a supporting or generic context is checked at its boundary only. Leave
23
- the context map for later. Put under `ignore` what has nothing to do with the architecture:
24
- scripts, generated code, migrations.
22
+ those are checked inside; a supporting or generic context is checked at its boundary only. Under
23
+ `contextMap`, start with `consumes: []` for every context: each import between two contexts is
24
+ then a violation, the baseline of the next step keeps them, and the map fills in as each one is
25
+ decided. Put under `ignore` what has nothing to do with the architecture: scripts, generated code,
26
+ migrations.
25
27
 
26
28
  ```ts [alveolus.config.ts]
27
29
  export default defineConfig({
28
30
  boundedContexts: { billing: "features/billing", orders: "modules/orders" },
31
+ contextMap: { billing: { consumes: [] }, orders: { consumes: [] } },
29
32
  ignore: ["src/migrations/**", "src/generated/**"],
30
33
  root: "src",
31
34
  subdomains: { core: ["orders"], supporting: ["billing"] },
@@ -106,8 +106,8 @@ Start from a use case: the [aggregate](../core/domain/aggregates.md) that keeps
106
106
  ## Configure the checks
107
107
 
108
108
  Create `alveolus.config.ts` at the root of the project. It says where the source code is, which
109
- folders are bounded contexts and which subdomain each one implements; everything else has a
110
- default.
109
+ folders are bounded contexts, which subdomain each one implements and which contexts each one
110
+ consumes; everything else has a default.
111
111
 
112
112
  ```sh
113
113
  npx alveolus init
@@ -121,6 +121,11 @@ import { defineConfig } from "@alveolus/arch";
121
121
 
122
122
  export default defineConfig({
123
123
  boundedContexts: { catalog: "catalog", notifications: "notifications", ordering: "ordering" },
124
+ contextMap: {
125
+ catalog: { consumes: [] },
126
+ notifications: { consumes: ["ordering"] },
127
+ ordering: { consumes: ["catalog"] },
128
+ },
124
129
  root: "src",
125
130
  subdomains: { core: ["catalog", "ordering"], generic: ["notifications"] },
126
131
  });
@@ -130,6 +135,12 @@ A [core](./project-layout.md#core-supporting-generic) context is checked by ever
130
135
  supporting or generic one is checked only at its boundary: it may be written any way you like, as
131
136
  long as it reaches the other contexts through their open host services.
132
137
 
138
+ The context map is the strategic design of the system, written down: each line reads as a
139
+ sentence, `ordering` consumes `catalog`, and every context has one, `consumes: []` when it
140
+ consumes nothing. An import that goes against the map is reported by
141
+ [`strategic/no-unmapped-context`](../rules/strategic/no-unmapped-context.md); a cycle in the map
142
+ is refused when the configuration loads.
143
+
133
144
  ### Options
134
145
 
135
146
  | Option | Default | What it does |
@@ -139,7 +150,7 @@ long as it reaches the other contexts through their open host services.
139
150
  | `boundedContexts` | required | Each bounded context and its folder, relative to `root`. `"modules/ordering"` works. |
140
151
  | `sharedKernel` | `"shared-kernel"` | The folder shared by every bounded context, relative to `root`. |
141
152
  | `subdomains` | required | The [subdomain](./project-layout.md#core-supporting-generic) each bounded context implements: `{ core: ["ordering"], supporting: ["billing"], generic: ["notifications"] }`. Every context is listed once. |
142
- | `contextMap` | none | For each bounded context, the ones it consumes: `{ payments: ["ledger"] }`. Checked for cycles; without it, only cycles are reported. |
153
+ | `contextMap` | required | For each bounded context, the ones it consumes: `{ ledger: { consumes: [] }, payments: { consumes: ["ledger"] } }`. Every context is listed, and the map has no cycle. |
143
154
  | `compositionRoot` | `"*.module.ts"` | The file, at the root of a bounded context, that wires it. |
144
155
  | `domainDependencies` | `{}` | npm packages the domain may import, besides `@alveolus/core`. |
145
156
  | `applicationDependencies` | `{}` | npm packages the application may import, besides `@alveolus/core` and `domainDependencies`. |
@@ -313,7 +313,9 @@ may import it; it imports none of them.
313
313
  Each change to the shared kernel reaches every context. `Clock` and `IdGenerator` already come
314
314
  with `@alveolus/core`; only their adapters live here. An aggregate, a repository or a handler in
315
315
  the shared kernel is reported by
316
- [`strategic/no-fat-shared-kernel`](../rules/strategic/no-fat-shared-kernel.md).
316
+ [`strategic/no-fat-shared-kernel`](../rules/strategic/no-fat-shared-kernel.md), and a static field
317
+ that holds state, such as a registry of services, by
318
+ [`strategic/no-shared-state`](../rules/strategic/no-shared-state.md).
317
319
  :::
318
320
 
319
321
  ## See also
@@ -51,8 +51,9 @@ its own business. The "Applies to" line of each rule page says which it is.
51
51
 
52
52
  | Rule | Reports |
53
53
  | --- | --- |
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 composition root that re-exports. |
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
+ | [`strategic/no-shared-state`](./strategic/no-shared-state.md) | A static field of the shared kernel that holds state. |
56
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
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. |
58
59
 
@@ -120,12 +121,15 @@ The rules that check imports read every way a file can depend on another one, no
120
121
  `import … from`:
121
122
 
122
123
  ```ts
124
+ /// <reference path="../legacy/pool.ts" />
125
+ /// <reference types="pg" />
123
126
  import { Pool } from "pg";
124
127
  export { Pool } from "pg";
125
128
  type Pool = import("pg").Pool;
126
129
  const pg = await import("pg");
127
130
  const pg = require("pg");
128
131
  import pg = require("pg");
132
+ declare module "pg" { interface Pool { tenant: string } }
129
133
  ```
130
134
 
131
135
  A global declared by the project, in a `declare global` block, counts as an import of the file that
@@ -133,8 +137,13 @@ declares it.
133
137
 
134
138
  An import the analysis cannot see through counts as a file outside the project: one that does not
135
139
  resolve, such as a `.js` file without types, one whose path is computed at runtime, or one that is
136
- ignored, such as a test file. No layer imports it: only the composition root and the files at the
137
- root of `src/` may.
140
+ ignored, such as a test file. No file imports it, not even a composition root or a file at the
141
+ root of `src/`: a test file that re-exports another context would otherwise carry the import past
142
+ every rule.
143
+
144
+ Code loaded at runtime counts the same way, wherever it is: `node:module` (`createRequire`),
145
+ `node:vm`, `module.require`, `process.getBuiltinModule`, `eval`, `Function` and `new Function`.
146
+ To read a JSON file, import it: `import pkg from "../package.json" with { type: "json" }`.
138
147
 
139
148
  ```
140
149
  src/ordering/domain/services/pricing.service.ts
@@ -152,8 +161,10 @@ name:
152
161
  ```ts [alveolus.config.ts]
153
162
  export default defineConfig({
154
163
  boundedContexts: { ordering: "ordering" },
164
+ contextMap: { ordering: { consumes: [] } },
155
165
  root: "src",
156
166
  rules: { "tactical/no-misplaced-class": "off", "tactical/no-public-field": "warn" },
167
+ subdomains: { core: ["ordering"] },
157
168
  });
158
169
  ```
159
170
 
@@ -153,11 +153,13 @@ to allow everything they export, or with the names you allow:
153
153
  ```ts [alveolus.config.ts]
154
154
  export default defineConfig({
155
155
  boundedContexts: { ordering: "ordering" },
156
+ contextMap: { ordering: { consumes: [] } },
156
157
  domainDependencies: {
157
158
  "date-fns": ["addDays", "isBefore"],
158
159
  "decimal.js": true,
159
160
  },
160
161
  root: "src",
162
+ subdomains: { core: ["ordering"] },
161
163
  });
162
164
  ```
163
165
 
@@ -40,8 +40,11 @@ Within the same context, or towards the shared kernel:
40
40
  | `published-language/` | Its own `published-language/`. From `@alveolus/core`, only `PublishedLanguage`, `IntegrationEvent` and `JsonValue`; other packages, such as a schema library, are fine. |
41
41
  | `driven/` | `domain/`, `application/`, `published-language/`, `driven/`, any package. |
42
42
  | `driving/` | `domain/`, `application/`, `published-language/`, `driving/`, any package. |
43
- | the composition root | Anything in its context and in the shared kernel. |
44
- | files at the root of `src/` | Composition roots, and each other. |
43
+ | the composition root | Anything in its context and in the shared kernel, any package. |
44
+ | files at the root of `src/` | Composition roots, each other, any package. |
45
+
46
+ No file imports a file the analysis does not see: ignored, unresolved, computed at runtime, or
47
+ outside the declared contexts and the shared kernel.
45
48
 
46
49
  No layer imports a composition root. The domain has its own rule,
47
50
  [`layers/no-impure-domain`](./no-impure-domain.md); imports from another context are checked by
@@ -100,7 +103,8 @@ The rule also reports:
100
103
  | Case | Message |
101
104
  | --- | --- |
102
105
  | A layer imports a composition root | `Imports … : only the composition root wires the layers.` |
103
- | A file at the root imports inside a context | `Files at the root import composition roots only, not ….` |
106
+ | A file at the root imports inside a context, or a file the analysis does not see | `Files at the root import composition roots only, not ….` |
107
+ | A composition root imports a file the analysis does not see | `The composition root imports src/ordering/wiring.fixture.ts (ignored by the analysis): it wires its own context and the shared kernel only.` |
104
108
  | Two composition roots in a context | `ordering has 2 composition roots (ordering.module.ts, pricing.module.ts): keep one, and move the rest into the layers.` |
105
109
  | The published language imports another name from core | `The published language imports … from @alveolus/core: only published-language types are allowed.` |
106
110
  | A name not allowed from a restricted package | `The application imports Controller from @nestjs/common: applicationDependencies only allows Injectable.` |
@@ -151,7 +155,9 @@ export default defineConfig({
151
155
  zod: true,
152
156
  },
153
157
  boundedContexts: { ordering: "ordering" },
158
+ contextMap: { ordering: { consumes: [] } },
154
159
  root: "src",
160
+ subdomains: { core: ["ordering"] },
155
161
  });
156
162
  ```
157
163
 
@@ -10,7 +10,7 @@ an anti-corruption layer or its composition root.
10
10
  <dl class="al-glance">
11
11
  <dt>Rule</dt><dd><code>strategic/no-cross-context-import</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>An import from another bounded context that is not its open host service, used where it may be</dd>
13
+ <dt>Reports</dt><dd>An import from another bounded context that is not its open host service, used where it may be; a value of another context given in the wiring that is not its open host service; an import of a file the analysis does not see</dd>
14
14
  <dt>Applies to</dt><dd>Every file of every bounded context, whatever its subdomain, and of the shared kernel</dd>
15
15
  <dt>Turn off</dt><dd><a href="#turn-it-off"><code>"strategic/no-cross-context-import": "off"</code></a></dd>
16
16
  </dl>
@@ -41,8 +41,21 @@ When a file of one bounded context imports a file of another one:
41
41
 
42
42
  The shared kernel imports no bounded context at all. Every context may import the shared kernel.
43
43
 
44
+ No file of a context or of the shared kernel imports a file the analysis does not see: ignored,
45
+ unresolved, computed at runtime, or outside the declared contexts and the shared kernel. Such a
46
+ file could re-export another context, and the rule could not tell which one. Code loaded at
47
+ runtime, with `createRequire`, `eval`, `new Function` or `node:vm`, is reported the same way.
48
+
44
49
  Every form of import counts, see [Every import counts](../index.md#every-import-counts).
45
50
 
51
+ The wiring follows the same contract. In the composition roots and the files at the root of
52
+ `src/`, a value of one context given to another one, such as
53
+ `new LedgerModule({ redemptions: () => this.emoney.commands.requestRedemption })`, is an
54
+ `OpenHostService` of the giving context, or one of its methods. A handler, a repository or any
55
+ other class of its model is reported, even when the receiving side declares a type of the same
56
+ shape. Whether the receiving context may consume the giving one at all is checked by
57
+ [`strategic/no-unmapped-context`](./no-unmapped-context.md).
58
+
46
59
  ## What it reports
47
60
 
48
61
  ```
@@ -67,6 +80,23 @@ src/shared-kernel/domain/value-objects/money.value-object.ts
67
80
  bounded context, but imports
68
81
  src/catalog/domain/value-objects/currency.value-object.ts
69
82
  (catalog domain).
83
+
84
+ src/ordering/domain/services/pricing.service.ts
85
+ 1 error strategic/no-cross-context-import: Imports
86
+ src/ordering/domain/value-objects/product-id.fixture.ts (ignored by
87
+ the analysis): the analysis cannot tell which bounded context it
88
+ reaches; move the file into a bounded context or the shared kernel.
89
+
90
+ src/ordering/driven/memory/adapters/memory-prices.adapter.ts
91
+ 1 error strategic/no-cross-context-import: Loads code at runtime with
92
+ node:module: the analysis cannot tell which bounded context it
93
+ reaches; use a static import.
94
+
95
+ src/app.module.ts
96
+ 14 error strategic/no-cross-context-import: Gives
97
+ this.emoney.commands.requestRedemption, from emoney, to ledger: only
98
+ an OpenHostService of another bounded context may cross, in an import
99
+ or in the wiring.
70
100
  ```
71
101
 
72
102
  ## Fix it
@@ -119,7 +149,10 @@ bounded context. Move what it needs into the shared kernel, or keep it in the co
119
149
  - The rule checks what an anti-corruption layer imports, not what it does with it: an adapter that
120
150
  returns the open host service's answer as is, untranslated, is accepted. In review, the ACL
121
151
  should build values of its own context.
122
- - A file that matches `ignore` in `alveolus.config.ts` is not analysed at all.
152
+ - A file that matches `ignore` in `alveolus.config.ts` is not analysed at all, and no file of a
153
+ context may import it. Review a change to `ignore` as you would review a rule turned off.
154
+ - In the wiring, a value whose type is erased on the way, by a cast or a container token written
155
+ as a string, is not seen. In review, the composition root holds no cast.
123
156
  :::
124
157
 
125
158
  ## Turn it off
@@ -77,5 +77,6 @@ rules: { "strategic/no-fat-shared-kernel": "off" },
77
77
  ## See also
78
78
 
79
79
  - [Project layout: shared kernel](../../guide/project-layout.md#shared-kernel)
80
+ - [`strategic/no-shared-state`](./no-shared-state.md), no state in the shared kernel either
80
81
  - [`strategic/no-unmapped-context`](./no-unmapped-context.md), the other way contexts get tied
81
82
  - [Rules](../index.md), every rule by category
@@ -0,0 +1,100 @@
1
+ ---
2
+ description: "Architecture rule: the shared kernel shares a model, not state; a static field that holds state would let two contexts talk where the context map does not show it."
3
+ ---
4
+
5
+ # no-shared-state
6
+
7
+ The shared kernel shares a model, never state. A static field that the code can change is reached
8
+ by every context at once: a channel between them that no import and no context map shows.
9
+
10
+ <dl class="al-glance">
11
+ <dt>Rule</dt><dd><code>strategic/no-shared-state</code></dd>
12
+ <dt>Category</dt><dd><a href="/rules/#strategic">Strategic</a>: what crosses a bounded context</dd>
13
+ <dt>Reports</dt><dd>A static field of the shared kernel without <code>readonly</code>, or holding a collection</dd>
14
+ <dt>Applies to</dt><dd>Every class of the shared kernel</dd>
15
+ <dt>Turn off</dt><dd><a href="#turn-it-off"><code>"strategic/no-shared-state": "off"</code></a></dd>
16
+ </dl>
17
+
18
+ ## Why
19
+
20
+ Ledger needs to create a redemption in e-money, and the context map says ledger consumes nothing.
21
+ Someone adds a `ServiceRegistry` to the shared kernel: e-money registers its handler under a name,
22
+ ledger resolves it. No file of ledger imports e-money, every import rule passes, and ledger now
23
+ depends on e-money in a way nobody decided.
24
+
25
+ ::: tip The fix
26
+ Contexts talk through an [open host service](../../core/strategic/open-host-services.md), consumed
27
+ in an [anti-corruption layer](../../core/strategic/anti-corruption-layers.md), and the
28
+ [context map](./no-unmapped-context.md) says who consumes whom. The shared kernel holds what both
29
+ contexts mean the same way: value objects, identifiers, ports. Constants are fine; a place to put
30
+ things is not.
31
+ :::
32
+
33
+ ## What it checks
34
+
35
+ Every static field of a class in the shared kernel:
36
+
37
+ | Static field | Allowed |
38
+ | --- | --- |
39
+ | `static readonly ZERO = new Money({ amount: 0 })` | ✅ |
40
+ | `static readonly PRECISION = 2` | ✅ |
41
+ | `static readonly CURRENCIES: readonly string[] = ["EUR"]`, a `ReadonlyMap`, a `ReadonlySet` | ✅ |
42
+ | `static count = 0`: without `readonly` | ❌ |
43
+ | `static readonly services = new Map()`: a `Map`, a `Set`, a `WeakMap`, a `WeakSet` | ❌ |
44
+ | `static readonly names: string[] = []`: an array that is not `readonly` | ❌ |
45
+ | `static readonly byId: Record<string, Handler> = {}`: an index signature | ❌ |
46
+ | `static readonly options = { strict: true }`: an object literal | ❌ |
47
+
48
+ Instance fields are not checked: an adapter of the shared kernel may hold its own state, each
49
+ context builds its own instance.
50
+
51
+ Module-level state, such as `let current` or `const services = new Map()` at the top of a file, is
52
+ reported by [`tactical/no-loose-code`](../tactical/no-loose-code.md).
53
+
54
+ ## What it reports
55
+
56
+ ```
57
+ src/shared-kernel/driven/memory/registry/service-registry.ts
58
+ 2 error strategic/no-shared-state: ServiceRegistry.services holds a
59
+ collection in a static field: every context reaches the same one, a
60
+ channel the context map does not show. The shared kernel shares a
61
+ model, not state: integrate through an open host service.
62
+ ```
63
+
64
+ ## Fix it
65
+
66
+ ### Integrate through an open host service
67
+
68
+ So that the dependency is decided and visible, the upstream context exposes an open host service,
69
+ the downstream context adds it to `contextMap.<downstream>.consumes` and calls it from an
70
+ anti-corruption layer. The composition root passes the service; no registry is needed.
71
+
72
+ ### Keep a constant constant
73
+
74
+ So that a constant cannot become a channel, make it `readonly` and give it an immutable type: a
75
+ value object, a primitive, a `readonly` array, a `ReadonlyMap`.
76
+
77
+ ## Limits
78
+
79
+ ::: warning What the rule cannot see
80
+ - A `static readonly` field typed by a class whose instances can change, such as
81
+ `static readonly bus = new EventEmitter()`, passes: the rule reads the type of the field, not
82
+ what its class does. In review, a static field of the shared kernel holds a value, never a
83
+ service.
84
+ - A `Readonly<Record<…>>` counts as a collection, because it has an index signature: use a
85
+ `ReadonlyMap` for a constant dictionary.
86
+ - 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, or a write to `globalThis`.
88
+ :::
89
+
90
+ ## Turn it off
91
+
92
+ ```ts [alveolus.config.ts]
93
+ rules: { "strategic/no-shared-state": "off" },
94
+ ```
95
+
96
+ ## See also
97
+
98
+ - [`strategic/no-fat-shared-kernel`](./no-fat-shared-kernel.md), what the shared kernel holds
99
+ - [`strategic/no-unmapped-context`](./no-unmapped-context.md), the relations between contexts
100
+ - [Rules](../index.md), every rule by category