@alveolus/arch 0.3.0 → 0.4.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/README.md +5 -0
- package/dist/bin.mjs +1 -1
- package/dist/{docs-DsQHpTtV.mjs → docs-DcFgskuN.mjs} +26 -40
- package/dist/docs-DcFgskuN.mjs.map +1 -0
- package/dist/index.d.mts +10 -11
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/docs/guide/existing-project.md +6 -3
- package/docs/guide/getting-started.md +14 -3
- package/docs/rules/index.md +2 -0
- package/docs/rules/layers/no-impure-domain.md +2 -0
- package/docs/rules/layers/no-outward-import.md +2 -0
- package/docs/rules/strategic/no-unmapped-context.md +25 -22
- package/docs/rules/tactical/no-foreign-query-dependency.md +98 -3
- package/package.json +1 -1
- package/dist/docs-DsQHpTtV.mjs.map +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -231,16 +231,12 @@ type Upstreams = Readonly<Record<string, readonly string[]>>;
|
|
|
231
231
|
declare class ContextMap {
|
|
232
232
|
private readonly upstreams;
|
|
233
233
|
constructor(upstreams: Upstreams);
|
|
234
|
-
static ofEdges(edges: readonly {
|
|
235
|
-
readonly from: string;
|
|
236
|
-
readonly to: string;
|
|
237
|
-
}[]): ContextMap;
|
|
238
234
|
get contexts(): string[];
|
|
235
|
+
get consumed(): string[];
|
|
236
|
+
selfConsumers(): string[];
|
|
239
237
|
allows(from: string, to: string): boolean;
|
|
240
238
|
cycle(): string[] | undefined;
|
|
241
|
-
cycleThrough(from: string, to: string): boolean;
|
|
242
239
|
private cycleFrom;
|
|
243
|
-
private reaches;
|
|
244
240
|
}
|
|
245
241
|
//#endregion
|
|
246
242
|
//#region src/architecture/settings.d.ts
|
|
@@ -258,7 +254,7 @@ interface Settings {
|
|
|
258
254
|
readonly contextFolders: readonly ContextFolder[];
|
|
259
255
|
readonly compositionRoot: string;
|
|
260
256
|
readonly extraFolders: ExtraFolders;
|
|
261
|
-
readonly contextMap: ContextMap
|
|
257
|
+
readonly contextMap: ContextMap;
|
|
262
258
|
readonly domainDependencies: AllowedPackages;
|
|
263
259
|
readonly applicationDependencies: AllowedPackages;
|
|
264
260
|
}
|
|
@@ -357,7 +353,7 @@ export declare class Architecture {
|
|
|
357
353
|
get files(): readonly SourceFile[];
|
|
358
354
|
get domainDependencies(): AllowedPackages;
|
|
359
355
|
get applicationDependencies(): AllowedPackages;
|
|
360
|
-
get contextMap(): ContextMap
|
|
356
|
+
get contextMap(): ContextMap;
|
|
361
357
|
locationOf(file: SourceFile): Location;
|
|
362
358
|
locationOfTarget(target: FileTarget$1): Location;
|
|
363
359
|
shapeIssueOf(location: Location): ShapeIssue | undefined;
|
|
@@ -587,6 +583,9 @@ export declare class Cli {
|
|
|
587
583
|
//#endregion
|
|
588
584
|
//#region src/config/alveolus-config.d.ts
|
|
589
585
|
type RuleSetting = "error" | "warn" | "info" | "off";
|
|
586
|
+
type ContextMapConfig = Readonly<Record<string, {
|
|
587
|
+
readonly consumes: readonly string[];
|
|
588
|
+
}>>;
|
|
590
589
|
interface LayoutConfig {
|
|
591
590
|
readonly extraFolders?: Readonly<Partial<Record<"domain" | "application", readonly string[]>>>;
|
|
592
591
|
}
|
|
@@ -596,7 +595,7 @@ interface AlveolusConfig {
|
|
|
596
595
|
readonly boundedContexts: Readonly<Record<string, string>>;
|
|
597
596
|
readonly sharedKernel?: string;
|
|
598
597
|
readonly subdomains?: Subdomains;
|
|
599
|
-
readonly contextMap
|
|
598
|
+
readonly contextMap: ContextMapConfig;
|
|
600
599
|
readonly compositionRoot?: string;
|
|
601
600
|
readonly domainDependencies?: PackageDependencies;
|
|
602
601
|
readonly applicationDependencies?: PackageDependencies;
|
|
@@ -615,7 +614,7 @@ export declare class Config implements CheckSettings {
|
|
|
615
614
|
readonly applicationDependencies: AllowedPackages;
|
|
616
615
|
readonly contextFolders: readonly ContextFolder[];
|
|
617
616
|
readonly extraFolders: ExtraFolders;
|
|
618
|
-
readonly contextMap: ContextMap
|
|
617
|
+
readonly contextMap: ContextMap;
|
|
619
618
|
private readonly ignored;
|
|
620
619
|
private readonly rules;
|
|
621
620
|
constructor(config: AlveolusConfig, projectDir: string);
|
|
@@ -649,5 +648,5 @@ export declare class Init {
|
|
|
649
648
|
private write;
|
|
650
649
|
}
|
|
651
650
|
//#endregion
|
|
652
|
-
export type { AlveolusConfig, CheckSettings, Finding, FindingData, ImportScope, PackageDependencies, RuleId, RuleMeta, RuleSetting, Settings, Violation, Written };
|
|
651
|
+
export type { AlveolusConfig, CheckSettings, ContextMapConfig, Finding, FindingData, ImportScope, PackageDependencies, RuleId, RuleMeta, RuleSetting, Settings, Violation, Written };
|
|
653
652
|
//# sourceMappingURL=index.d.mts.map
|
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/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":";;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,qBAAqB;WACrB,yBAAyB;WACzB,SAAS;;cAGN;WACI;WACA;WACA,MAAM;WACN,YAAY;WACZ;WACA;WACA;WACA,qBAAqB;WACrB,yBAAyB;WACzB,SAAS;EAEzB,YAAmB,OAAO;MAaf;MAIA;;;;UC5CK;WACP;WACA;WACA;WACA,MAAM;WACN,UAAU;WACV,sBAAsB;WACtB,kBAAkB;;cAGf;WACI;WACA;WACA;WACA,MAAM;WACN,UAAU;WACV,sBAAsB;WACtB,kBAAkB;EAElC,YAAmB,OAAO;MAUf,eAAe;MAIf,iBAAiB;MAIjB,0BAA0B;MAI1B;MAIA;UAKH;;;;KCvDG;KAEP;KAEO;WAA8B;WAAuB;WAAuB,YAAY;;WAAgC;WAA0B;;cAEjJ;WAEX;WACA,MAAsB;WACtB;WACA;WACA,QAAwB;EALzB,YACC,cACA,MAAsB,gBACtB,mBACA,0BACA,QAAwB;MAGd;EAIX,YAAmB;;;;KCnBR;KAEA;cAEC;WAEX;WACA;WACA,QAAwB;WACxB,QAAwB;EAJzB,YACC,cACA,cACA,QAAwB,cACxB,QAAwB;;;;cCTb;WAEX;WACA;EAFD,YACC,cACA;MAGU;;;;KCNA;cAEC;WAEX;WACA,MAAsB;EAFvB,YACC,cACA,MAAsB;;;;KCLZ;cAEC;WAEX,MAAsB;WACtB;WACA;EAHD,YACC,MAAsB,eACtB,cACA;;;;UCCe;WACP;WACA;WACA,uBAAuB;WACvB,kBAAkB;WAClB,qBAAqB;WACrB,iBAAiB;WACjB,kBAAkB;WAClB,mBAAmB;;cAGhB;WACI;WACA,uBAAuB;WACvB,kBAAkB;WAClB,qBAAqB;WACrB,iBAAiB;WACjB,kBAAkB;WAClB,mBAAmB;mBAClB;EAEjB,YAAmB,OAAO;EAW1B,SAAgB;EAIhB,aAAoB,eAAe;EAInC,WAAkB,eAAe;;;;cCzCrBA;WAIX;WACA,gBAAgC;mBAJhB;EAEjB,YACC,mBACA,gBAAgC;EAKjC,KAAY,eAAe;EAI3B,QAAe,MAAM,YAAY;EAOjC,aAAoB;;;;cCvBR;MACD;EAIX,QAAe,MAAM,YAAY;EAUjC,UAAiB,sBAAsB,cAAc;EAUrD,SAAgB,OAAO;EAOvB,eAAsB;EAItB,0BAAiC;UAIzB;UAIA;UAIA;;;;KCpDG,YAAY,SAAS;cAEpB;mBACO;EAAnB,YAAmB,WAA4B;
|
|
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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"}
|
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-DcFgskuN.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.
|
|
23
|
-
|
|
24
|
-
|
|
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
|
|
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` |
|
|
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`. |
|
package/docs/rules/index.md
CHANGED
|
@@ -152,8 +152,10 @@ name:
|
|
|
152
152
|
```ts [alveolus.config.ts]
|
|
153
153
|
export default defineConfig({
|
|
154
154
|
boundedContexts: { ordering: "ordering" },
|
|
155
|
+
contextMap: { ordering: { consumes: [] } },
|
|
155
156
|
root: "src",
|
|
156
157
|
rules: { "tactical/no-misplaced-class": "off", "tactical/no-public-field": "warn" },
|
|
158
|
+
subdomains: { core: ["ordering"] },
|
|
157
159
|
});
|
|
158
160
|
```
|
|
159
161
|
|
|
@@ -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
|
|
|
@@ -10,7 +10,7 @@ never depend on each other.
|
|
|
10
10
|
<dl class="al-glance">
|
|
11
11
|
<dt>Rule</dt><dd><code>strategic/no-unmapped-context</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 of another context that <code>contextMap</code> does not allow
|
|
13
|
+
<dt>Reports</dt><dd>An import of another context that <code>contextMap</code> does not allow</dd>
|
|
14
14
|
<dt>Applies to</dt><dd>Every file of every bounded context</dd>
|
|
15
15
|
<dt>Turn off</dt><dd><a href="#turn-it-off"><code>"strategic/no-unmapped-context": "off"</code></a></dd>
|
|
16
16
|
</dl>
|
|
@@ -24,20 +24,22 @@ extracted or rewritten without the other. In a system that lives for years, that
|
|
|
24
24
|
turns "change this module" into "rewrite the application".
|
|
25
25
|
|
|
26
26
|
::: tip The fix
|
|
27
|
-
Write the context map, as DDD asks: which context is upstream of which.
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
Write the context map, as DDD asks: which context is upstream of which. `alveolus.config.ts`
|
|
28
|
+
requires it, so the code can no longer stray from it. A dependency that goes against the map is
|
|
29
|
+
reversed: `Ledger` publishes an event, `Payments` reacts. Adding a line to the map is the other
|
|
30
|
+
way out, and it is a strategic decision: take it in a review, not in a fix.
|
|
30
31
|
:::
|
|
31
32
|
|
|
32
33
|
## What it checks
|
|
33
34
|
|
|
34
35
|
Every import from a file of one bounded context to a file of another, open host service or
|
|
35
|
-
composition root alike:
|
|
36
|
+
composition root alike: the importing context lists the imported one under `consumes` in
|
|
37
|
+
`contextMap`.
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
The map itself is checked when the configuration loads, before any rule runs: a context left out
|
|
40
|
+
of the map, a context the map names that `boundedContexts` does not declare, a context that
|
|
41
|
+
consumes itself, or a cycle, is an error. Two contexts that depend on each other are therefore
|
|
42
|
+
never allowed, whichever way the code is written.
|
|
41
43
|
|
|
42
44
|
Imports of the shared kernel are not consumptions: every context may import it.
|
|
43
45
|
|
|
@@ -46,18 +48,17 @@ Imports of the shared kernel are not consumptions: every context may import it.
|
|
|
46
48
|
```
|
|
47
49
|
src/ledger/driven/payments/adapters/payment-status.adapter.ts
|
|
48
50
|
2 error strategic/no-unmapped-context: ledger consumes payments, which the
|
|
49
|
-
context map does not allow:
|
|
50
|
-
|
|
51
|
+
context map does not allow: reverse the dependency, or if ledger
|
|
52
|
+
really is downstream of payments, add payments to
|
|
53
|
+
contextMap.ledger.consumes.
|
|
51
54
|
```
|
|
52
55
|
|
|
53
|
-
|
|
56
|
+
A map that would allow it is refused before the check:
|
|
54
57
|
|
|
55
58
|
```
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
no longer change alone; declare a contextMap and reverse one
|
|
60
|
-
dependency.
|
|
59
|
+
Invalid configuration in alveolus.config.ts:
|
|
60
|
+
contextMap has a cycle: ledger → payments → ledger. Two contexts that depend
|
|
61
|
+
on each other can no longer change alone: reverse one dependency.
|
|
61
62
|
```
|
|
62
63
|
|
|
63
64
|
## Fix it
|
|
@@ -65,21 +66,23 @@ src/ledger/driven/payments/adapters/payment-status.adapter.ts
|
|
|
65
66
|
### Declare the context map
|
|
66
67
|
|
|
67
68
|
So that the direction of every dependency is a decision, not an accident, list for each context
|
|
68
|
-
the ones it consumes:
|
|
69
|
+
the ones it consumes. Each line reads as a sentence: `payments` consumes `ledger` and `customers`.
|
|
69
70
|
|
|
70
71
|
```ts [alveolus.config.ts]
|
|
71
72
|
export default defineConfig({
|
|
72
73
|
boundedContexts: { customers: "customers", ledger: "ledger", payments: "payments" },
|
|
73
74
|
contextMap: {
|
|
74
|
-
customers: [],
|
|
75
|
-
ledger: ["customers"],
|
|
76
|
-
payments: ["ledger", "customers"],
|
|
75
|
+
customers: { consumes: [] },
|
|
76
|
+
ledger: { consumes: ["customers"] },
|
|
77
|
+
payments: { consumes: ["ledger", "customers"] },
|
|
77
78
|
},
|
|
78
79
|
root: "src",
|
|
80
|
+
subdomains: { core: ["ledger", "payments"], generic: ["customers"] },
|
|
79
81
|
});
|
|
80
82
|
```
|
|
81
83
|
|
|
82
|
-
|
|
84
|
+
Every context is in the map. `consumes: []` is a decision too: that context goes its separate
|
|
85
|
+
way, and the day it needs another one, the import is reported and the map is updated on purpose.
|
|
83
86
|
|
|
84
87
|
### Reverse a dependency with an event
|
|
85
88
|
|
|
@@ -17,12 +17,20 @@ A query handler receives what reads: nothing that writes or changes state.
|
|
|
17
17
|
## Why
|
|
18
18
|
|
|
19
19
|
`GetOrderSummaryHandler` receives the unit of work: a read can now change state, and the caller who
|
|
20
|
-
asked a question gets a side effect too. A command handler
|
|
21
|
-
|
|
20
|
+
asked a question gets a side effect too. A command handler injected into it does the same, one
|
|
21
|
+
step removed.
|
|
22
|
+
|
|
23
|
+
A domain service is different: it is pure, so injecting it changes nothing. What it changes is
|
|
24
|
+
where the business rule runs. `GetSafeguardingReconciliationHandler` receives
|
|
25
|
+
`SafeguardingReconciliation` and computes the reconciliation at each read: two readers at two
|
|
26
|
+
moments see two different results, nothing records which one was reported, and the rule now runs
|
|
27
|
+
on two paths, the command's and the query's, that drift apart. The read side delivers data shaped
|
|
28
|
+
for the reader; the domain's behaviour runs on the write side, once, and leaves a fact.
|
|
22
29
|
|
|
23
30
|
::: tip The fix
|
|
24
31
|
A query reads a view through a query repository, and writes nothing. A query that seems to need a
|
|
25
|
-
write is a command, or a command followed by a query.
|
|
32
|
+
write is a command, or a command followed by a query. A query that seems to need a domain service
|
|
33
|
+
is reading a fact nobody recorded, or holding a calculation that belongs to a value object.
|
|
26
34
|
:::
|
|
27
35
|
|
|
28
36
|
## What it checks
|
|
@@ -78,6 +86,91 @@ constructor(private readonly summaries: OrderSummaries) {
|
|
|
78
86
|
|
|
79
87
|
</div>
|
|
80
88
|
|
|
89
|
+
### Record the fact, then read it
|
|
90
|
+
|
|
91
|
+
So that a result the reader relies on exists once, with its date, a domain service runs in a
|
|
92
|
+
command handler that records its outcome, and the query reads the record. A reconciliation, a
|
|
93
|
+
regulatory figure, a score: when the reader asks "what was it", the answer is a fact to keep, not
|
|
94
|
+
a calculation to redo.
|
|
95
|
+
|
|
96
|
+
<div class="al-compare">
|
|
97
|
+
|
|
98
|
+
```ts [❌ Avoid: src/safeguarding/application/queries/get-reconciliation.query.ts]
|
|
99
|
+
constructor(
|
|
100
|
+
private readonly balances: SafeguardingBalances,
|
|
101
|
+
private readonly reconciliation: SafeguardingReconciliation,
|
|
102
|
+
) {
|
|
103
|
+
super();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async handle(query: GetReconciliation): Promise<Result<ReconciliationView, NotFound>> {
|
|
107
|
+
const balances = await this.balances.on(query.date);
|
|
108
|
+
return ok(this.reconciliation.reconcile(balances));
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```ts [✅ Prefer: src/safeguarding/application/commands/reconcile-safeguarding.command.ts]
|
|
113
|
+
constructor(
|
|
114
|
+
private readonly accounts: SafeguardingAccounts,
|
|
115
|
+
private readonly reconciliation: SafeguardingReconciliation,
|
|
116
|
+
private readonly unitOfWork: UnitOfWork,
|
|
117
|
+
) {
|
|
118
|
+
super();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async handle(command: ReconcileSafeguarding): Promise<Result<void, NotFound>> {
|
|
122
|
+
const account = await this.accounts.of(command.accountId);
|
|
123
|
+
account.reconcile(this.reconciliation.reconcile(account.balances()), command.at);
|
|
124
|
+
await this.unitOfWork.commit();
|
|
125
|
+
return ok();
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
The query handler then receives `Reconciliations`, a query repository, and returns the
|
|
132
|
+
reconciliation of the date asked. The aggregate records the outcome, so the domain service keeps
|
|
133
|
+
one caller.
|
|
134
|
+
|
|
135
|
+
### Move a calculation into a value object
|
|
136
|
+
|
|
137
|
+
So that a figure derived from the values of a view is computed where values are computed, the
|
|
138
|
+
calculation becomes a static factory of a [value object](../../core/domain/value-objects.md),
|
|
139
|
+
which a query may use: a projection, a conversion, a total. Nothing is recorded because nothing
|
|
140
|
+
happened.
|
|
141
|
+
|
|
142
|
+
<div class="al-compare">
|
|
143
|
+
|
|
144
|
+
```ts [❌ Avoid: src/safeguarding/application/queries/get-own-funds-requirement.query.ts]
|
|
145
|
+
constructor(
|
|
146
|
+
private readonly figures: SafeguardingFigures,
|
|
147
|
+
private readonly calculator: OwnFundsRequirementCalculator,
|
|
148
|
+
) {
|
|
149
|
+
super();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async handle(query: GetOwnFundsRequirement): Promise<Result<OwnFundsRequirementView, NotFound>> {
|
|
153
|
+
const figures = await this.figures.of(query.firmId);
|
|
154
|
+
return ok({ amount: this.calculator.compute(figures) });
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
```ts [✅ Prefer: src/safeguarding/application/queries/get-own-funds-requirement.query.ts]
|
|
159
|
+
constructor(private readonly figures: SafeguardingFigures) {
|
|
160
|
+
super();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async handle(query: GetOwnFundsRequirement): Promise<Result<OwnFundsRequirementView, NotFound>> {
|
|
164
|
+
const figures = await this.figures.of(query.firmId);
|
|
165
|
+
return ok({ amount: OwnFundsRequirement.of(figures).amount });
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
</div>
|
|
170
|
+
|
|
171
|
+
Which of the two? If the reader asks for the figure as it was declared or decided, record it. If
|
|
172
|
+
the reader asks what the figure would be from the values on the screen, calculate it.
|
|
173
|
+
|
|
81
174
|
## Limits
|
|
82
175
|
|
|
83
176
|
::: warning What the rule cannot see
|
|
@@ -101,6 +194,8 @@ new queries keep to reading while you split the old ones.
|
|
|
101
194
|
- [Query handlers](../../core/application/query-handlers.md), what is checked
|
|
102
195
|
- [Repositories](../../core/domain/repositories.md) and [Views](../../core/domain/views.md), what
|
|
103
196
|
a query reads
|
|
197
|
+
- [Domain services](../../core/domain/domain-services.md), called by the command handler, and
|
|
198
|
+
[value objects](../../core/domain/value-objects.md), the home of a calculation
|
|
104
199
|
- [`tactical/no-foreign-command-dependency`](./no-foreign-command-dependency.md), the same list for
|
|
105
200
|
command handlers
|
|
106
201
|
- [Rules](../index.md), every rule by category
|