@ddd-ts-kit/eslint-plugin 0.10.0 → 0.11.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 +2 -0
- package/dist/rules/lifecycle-status-through-machine.d.ts +3 -0
- package/dist/rules/lifecycle-status-through-machine.d.ts.map +1 -0
- package/dist/rules/lifecycle-status-through-machine.js +89 -0
- package/dist/rules/lifecycle-status-through-machine.js.map +1 -0
- package/dist/rules/process-sends-commands-only.d.ts +2 -0
- package/dist/rules/process-sends-commands-only.d.ts.map +1 -0
- package/dist/rules/process-sends-commands-only.js +46 -0
- package/dist/rules/process-sends-commands-only.js.map +1 -0
- package/dist/rules/rules.d.ts.map +1 -1
- package/dist/rules/rules.js +4 -0
- package/dist/rules/rules.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/rules/lifecycle-status-through-machine.md +69 -0
- package/docs/rules/process-sends-commands-only.md +63 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -142,6 +142,8 @@ is an error whatever the configured limit). `strict` sets every rule to `error`.
|
|
|
142
142
|
| [`ports/value-import`](docs/rules/ports-value-import.md) | `ddd-ts-kit/ports-value-import` | a port named in a dependency list or given to `bind` is not a type-only import; fixable | `error` | custom |
|
|
143
143
|
| [`scheduling/job-triggers-use-case`](docs/rules/scheduling-job-triggers-use-case.md) | `ddd-ts-kit/scheduling-job-triggers-use-case` | a job lives in `application/scheduling/*.job.ts`, implements `ScheduledJob`, sends a command, and touches neither a repository nor the model | `error` | custom |
|
|
144
144
|
| [`scheduling/job-name`](docs/rules/scheduling-job-name.md) | `ddd-ts-kit/scheduling-job-name` | a job is named `<context>.<job-name>` by a string literal, and its `systemActor` carries that name | `error` | custom |
|
|
145
|
+
| [`lifecycle/status-through-machine`](docs/rules/lifecycle-status-through-machine.md) | `ddd-ts-kit/lifecycle-status-through-machine` | in an aggregate that imports its state machine, the status is written only from the machine (`next`, `start`) or copied from a status already read | `error` | custom |
|
|
146
|
+
| [`process/sends-commands-only`](docs/rules/process-sends-commands-only.md) | `ddd-ts-kit/process-sends-commands-only` | a process manager of `application/processes/` imports no aggregate, entity, repository, domain service, port or infrastructure: it changes the world through the commands it decides | `error` | custom |
|
|
145
147
|
| [`imports/alias-only`](docs/rules/imports-alias-only.md) | `ddd-ts-kit/imports-alias-only` | a file of the project is imported by its alias (`@/...`, `$test/...`, `$scripts/...`), never by a relative path, the same directory included; no `.` or `..` segment in an alias path; fixable | `error` | custom |
|
|
146
148
|
| [`imports/extension`](docs/rules/imports-extension.md) | `ddd-ts-kit/imports-extension` | an import of a file of the project ends as `importExtension` of the config says: no extension under `none`, `.js` under `js`; fixable | `error` | custom |
|
|
147
149
|
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type TSESLint } from '@typescript-eslint/utils';
|
|
2
|
+
export declare const lifecycleStatusThroughMachine: TSESLint.RuleModule<"notThroughMachine", [], import("../index.js").RuleDocs, TSESLint.RuleListener>;
|
|
3
|
+
//# sourceMappingURL=lifecycle-status-through-machine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle-status-through-machine.d.ts","sourceRoot":"","sources":["../../src/rules/lifecycle-status-through-machine.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,QAAQ,EAAiB,MAAM,0BAA0B,CAAC;AAYlG,eAAO,MAAM,6BAA6B,qGAgDxC,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { AST_NODE_TYPES, ASTUtils } from '@typescript-eslint/utils';
|
|
2
|
+
import { className } from '../support/classes/class-name.js';
|
|
3
|
+
import { enclosingMember } from '../support/classes/enclosing-member.js';
|
|
4
|
+
import { defineRule } from '../support/rule/define-rule.js';
|
|
5
|
+
const STATUS = 'status';
|
|
6
|
+
const MACHINE_FILE = /(?:^|\/)state-machines\/[^/]+\.state-machine(?:\.[cm]?[jt]s)?$/;
|
|
7
|
+
const THROUGH = new Set(['next', 'start']);
|
|
8
|
+
export const lifecycleStatusThroughMachine = defineRule({
|
|
9
|
+
id: 'lifecycle/status-through-machine',
|
|
10
|
+
type: 'problem',
|
|
11
|
+
recommended: 'error',
|
|
12
|
+
description: 'In an aggregate that imports its state machine, the status is written only from the machine (`machine.next(...)`, `machine.start(...)`) or copied from a status already read.',
|
|
13
|
+
messages: {
|
|
14
|
+
notThroughMachine: "{{className}} writes its status without its state machine ({{value}}). Ask the machine: `status: {{machine}}.next(this.state.status, '<transition>')`, so that a forbidden transition is refused in one place.",
|
|
15
|
+
},
|
|
16
|
+
create({ file, sourceCode, report }) {
|
|
17
|
+
if (!isAggregateFile(file)) {
|
|
18
|
+
return {};
|
|
19
|
+
}
|
|
20
|
+
const machines = new Set();
|
|
21
|
+
const judge = (node, value) => {
|
|
22
|
+
const site = enclosingMember(node);
|
|
23
|
+
const machine = [...machines][0];
|
|
24
|
+
if (site === undefined || machine === undefined || isThroughMachine(value, { machines, sourceCode })) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const data = { className: className(site.body.parent), value: sourceCode.getText(value), machine };
|
|
28
|
+
report({ node: value, messageId: 'notThroughMachine', data });
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
ImportDeclaration(node) {
|
|
32
|
+
if (MACHINE_FILE.test(node.source.value)) {
|
|
33
|
+
node.specifiers.forEach((specifier) => machines.add(specifier.local.name));
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
Property(node) {
|
|
37
|
+
if (node.parent.type === AST_NODE_TYPES.ObjectExpression && keyIsStatus(node.key)) {
|
|
38
|
+
judge(node, node.value);
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
AssignmentExpression(node) {
|
|
42
|
+
if (node.left.type === AST_NODE_TYPES.MemberExpression && keyIsStatus(node.left.property)) {
|
|
43
|
+
judge(node, node.right);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
function isAggregateFile(file) {
|
|
50
|
+
const { location } = file;
|
|
51
|
+
return (location.area === 'context' && location.layer === 'domain' && !file.isTest && location.inside[0] === 'aggregates');
|
|
52
|
+
}
|
|
53
|
+
function keyIsStatus(key) {
|
|
54
|
+
return ((key.type === AST_NODE_TYPES.Identifier && key.name === STATUS) ||
|
|
55
|
+
(key.type === AST_NODE_TYPES.Literal && key.value === STATUS));
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* A call of the machine, or a status copied from one already read (`this.state.status`, a
|
|
59
|
+
* parameter named `status`, `const { status } = ...`). A local binding is followed back to its value:
|
|
60
|
+
* `const status = InvoiceStatus.Paid; this.state = { ...this.state, status }` is a write by hand.
|
|
61
|
+
*/
|
|
62
|
+
function isThroughMachine(value, judged) {
|
|
63
|
+
if (value.type === AST_NODE_TYPES.Identifier) {
|
|
64
|
+
return isBoundThroughMachine(value, judged);
|
|
65
|
+
}
|
|
66
|
+
if (value.type === AST_NODE_TYPES.MemberExpression) {
|
|
67
|
+
return keyIsStatus(value.property);
|
|
68
|
+
}
|
|
69
|
+
return isMachineCall(value, judged.machines);
|
|
70
|
+
}
|
|
71
|
+
function isBoundThroughMachine(identifier, judged) {
|
|
72
|
+
const definition = ASTUtils.findVariable(judged.sourceCode.getScope(identifier), identifier.name)?.defs[0];
|
|
73
|
+
if (definition?.node.type !== AST_NODE_TYPES.VariableDeclarator) {
|
|
74
|
+
return definition !== undefined && identifier.name === STATUS;
|
|
75
|
+
}
|
|
76
|
+
const { id, init } = definition.node;
|
|
77
|
+
return id.type !== AST_NODE_TYPES.Identifier || (init !== null && isThroughMachine(init, judged));
|
|
78
|
+
}
|
|
79
|
+
function isMachineCall(value, machines) {
|
|
80
|
+
if (value.type !== AST_NODE_TYPES.CallExpression || value.callee.type !== AST_NODE_TYPES.MemberExpression) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
const { object, property } = value.callee;
|
|
84
|
+
return (object.type === AST_NODE_TYPES.Identifier &&
|
|
85
|
+
machines.has(object.name) &&
|
|
86
|
+
property.type === AST_NODE_TYPES.Identifier &&
|
|
87
|
+
THROUGH.has(property.name));
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=lifecycle-status-through-machine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle-status-through-machine.js","sourceRoot":"","sources":["../../src/rules/lifecycle-status-through-machine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-sends-commands-only.d.ts","sourceRoot":"","sources":["../../src/rules/process-sends-commands-only.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,wBAAwB,kKAuBnC,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { defineRule } from '../support/rule/define-rule.js';
|
|
2
|
+
import { importListener } from '../support/imports/import-listener.js';
|
|
3
|
+
const WRITERS = {
|
|
4
|
+
domain: ['aggregates', 'entities', 'repositories', 'services'],
|
|
5
|
+
application: ['ports'],
|
|
6
|
+
};
|
|
7
|
+
export const processSendsCommandsOnly = defineRule({
|
|
8
|
+
id: 'process/sends-commands-only',
|
|
9
|
+
type: 'problem',
|
|
10
|
+
recommended: 'error',
|
|
11
|
+
description: 'A process manager of application/processes decides from its state and what happened, and changes the world only through the commands it sends: it imports no aggregate, entity, repository, domain service, port or infrastructure.',
|
|
12
|
+
messages: {
|
|
13
|
+
writes: 'This process imports {{target}}. A process never writes an aggregate, of its context or another, nor calls an outside system: it answers a decision whose commands do it, each authorized and transactional in its own handler. Remove the import and send a command instead.',
|
|
14
|
+
},
|
|
15
|
+
create({ file, report }) {
|
|
16
|
+
if (!isProcessFile(file)) {
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
return importListener(file, ({ node, target }) => {
|
|
20
|
+
const reached = reachedWriter(target);
|
|
21
|
+
if (reached !== undefined) {
|
|
22
|
+
report({ node, messageId: 'writes', data: { target: reached } });
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
function isProcessFile(file) {
|
|
28
|
+
const { location } = file;
|
|
29
|
+
return (!file.isTest &&
|
|
30
|
+
location.area === 'context' &&
|
|
31
|
+
location.layer === 'application' &&
|
|
32
|
+
location.inside[0] === 'processes' &&
|
|
33
|
+
file.path.endsWith('.process.ts'));
|
|
34
|
+
}
|
|
35
|
+
function reachedWriter(target) {
|
|
36
|
+
if (target.kind !== 'file' || target.location.area !== 'context') {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
const { layer, inside } = target.location;
|
|
40
|
+
if (layer === 'infrastructure') {
|
|
41
|
+
return 'infrastructure/';
|
|
42
|
+
}
|
|
43
|
+
const [folder = ''] = inside;
|
|
44
|
+
return WRITERS[layer]?.includes(folder) === true ? `${layer}/${folder}/` : undefined;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=process-sends-commands-only.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-sends-commands-only.js","sourceRoot":"","sources":["../../src/rules/process-sends-commands-only.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAIvE,MAAM,OAAO,GAAgD;IAC3D,MAAM,EAAE,CAAC,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,UAAU,CAAC;IAC9D,WAAW,EAAE,CAAC,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,CAAC,MAAM,wBAAwB,GAAG,UAAU,CAAY;IAC5D,EAAE,EAAE,6BAA6B;IACjC,IAAI,EAAE,SAAS;IACf,WAAW,EAAE,OAAO;IACpB,WAAW,EACT,qOAAqO;IACvO,QAAQ,EAAE;QACR,MAAM,EACJ,+QAA+Q;KAClR;IACD,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE;QACrB,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,OAAO,cAAc,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE;YAC/C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;YAEtC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC1B,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;YACnE,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAC,CAAC;AAEH,SAAS,aAAa,CAAC,IAAgB;IACrC,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IAE1B,OAAO,CACL,CAAC,IAAI,CAAC,MAAM;QACZ,QAAQ,CAAC,IAAI,KAAK,SAAS;QAC3B,QAAQ,CAAC,KAAK,KAAK,aAAa;QAChC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,WAAW;QAClC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,CAClC,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,MAAoB;IACzC,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QACjE,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC;IAE1C,IAAI,KAAK,KAAK,gBAAgB,EAAE,CAAC;QAC/B,OAAO,iBAAiB,CAAC;IAC3B,CAAC;IAED,MAAM,CAAC,MAAM,GAAG,EAAE,CAAC,GAAG,MAAM,CAAC;IAE7B,OAAO,OAAO,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,MAAM,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AACvF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/rules/rules.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,OAAO,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/rules/rules.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAsIlE,+HAA+H;AAC/H,eAAO,MAAM,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEnD,CAAC"}
|
package/dist/rules/rules.js
CHANGED
|
@@ -50,6 +50,8 @@ import { presentationControllerDelegates } from './presentation-controller-deleg
|
|
|
50
50
|
import { presentationControllerShape } from './presentation-controller-shape.js';
|
|
51
51
|
import { presentationGuardAtTheEdge } from './presentation-guard-at-the-edge.js';
|
|
52
52
|
import { purityNoAmbientRandomness, purityNoEnvironment, purityNoSystemClock } from './purity.js';
|
|
53
|
+
import { lifecycleStatusThroughMachine } from './lifecycle-status-through-machine.js';
|
|
54
|
+
import { processSendsCommandsOnly } from './process-sends-commands-only.js';
|
|
53
55
|
import { schedulingJobName } from './scheduling-job-name.js';
|
|
54
56
|
import { schedulingJobTriggersUseCase } from './scheduling-job-triggers-use-case.js';
|
|
55
57
|
import { standardNoParallelInterface } from './standard-no-parallel-interface.js';
|
|
@@ -115,6 +117,8 @@ const ALL = [
|
|
|
115
117
|
portsNoConcreteDependency,
|
|
116
118
|
portsValueImport,
|
|
117
119
|
schedulingJobTriggersUseCase,
|
|
120
|
+
lifecycleStatusThroughMachine,
|
|
121
|
+
processSendsCommandsOnly,
|
|
118
122
|
schedulingJobName,
|
|
119
123
|
importsAliasOnly,
|
|
120
124
|
importsExtension,
|
package/dist/rules/rules.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/rules/rules.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/rules/rules.ts"],"names":[],"mappings":"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"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.11.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const VERSION = '0.
|
|
1
|
+
export const VERSION = '0.11.0';
|
|
2
2
|
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# ddd-ts-kit/lifecycle-status-through-machine
|
|
2
|
+
|
|
3
|
+
Architecture id: `lifecycle/status-through-machine`. Default severity: `error` in `recommended`, `error` in `strict`.
|
|
4
|
+
|
|
5
|
+
Applies to: `domain/aggregates/*` of a context, specs aside, when the file imports a state machine
|
|
6
|
+
of `domain/state-machines/`.
|
|
7
|
+
|
|
8
|
+
## Why
|
|
9
|
+
|
|
10
|
+
A state machine says once which status follows which, and what refuses a forbidden transition.
|
|
11
|
+
An aggregate that also writes its status by hand keeps a second, silent copy of the lifecycle:
|
|
12
|
+
the diagram generated from the machine then lies, and a transition the machine forbids happens
|
|
13
|
+
anyway. The status is therefore only ever written from the machine, or copied from a status
|
|
14
|
+
already read.
|
|
15
|
+
|
|
16
|
+
## Reported
|
|
17
|
+
|
|
18
|
+
In an aggregate file that imports a `*.state-machine` module, inside a member of a class:
|
|
19
|
+
|
|
20
|
+
- an object property `status: <value>` whose value is not a call `<machine>.next(..)` or
|
|
21
|
+
`<machine>.start(..)` of an imported machine, a status read from somewhere (`this.state.status`,
|
|
22
|
+
`state.status`) or the variable `status`
|
|
23
|
+
- an assignment `<target>.status = <value>`, by the same test
|
|
24
|
+
|
|
25
|
+
Incorrect:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
confirm(now: Date): void {
|
|
29
|
+
if (this.state.status !== 'requested') {
|
|
30
|
+
throw AppointmentError.alreadyConfirmed(this.id);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
this.state = { ...this.state, changedAt: now, status: 'booked' };
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Correct:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
confirm(now: Date): void {
|
|
41
|
+
this.state = { ...this.state, changedAt: now, status: appointmentLifecycle.next(this.state.status, 'confirm') };
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Details
|
|
46
|
+
|
|
47
|
+
The machine is any binding imported from a module whose path ends with
|
|
48
|
+
`state-machines/<name>.state-machine`. What the machine guarantees: `defineStateMachine` of
|
|
49
|
+
`@ddd-ts-kit/kernel`.
|
|
50
|
+
|
|
51
|
+
## Options
|
|
52
|
+
|
|
53
|
+
None.
|
|
54
|
+
|
|
55
|
+
`rules."lifecycle/status-through-machine"` in `ddd-ts-kit.config.json` set to `off` silences the
|
|
56
|
+
rule, and `// ddd-ts-kit-disable-next-line lifecycle/status-through-machine -- <reason>` silences
|
|
57
|
+
one line, exactly as for `ddd-ts-kit check`.
|
|
58
|
+
|
|
59
|
+
## Limits
|
|
60
|
+
|
|
61
|
+
- One file at a time: an aggregate whose state machine exists and is not imported is reported
|
|
62
|
+
by `ddd-ts-kit check` alone.
|
|
63
|
+
- A status written through a helper function, or held in a field initializer of the class, is
|
|
64
|
+
not followed.
|
|
65
|
+
|
|
66
|
+
## With the CLI checker
|
|
67
|
+
|
|
68
|
+
`ddd-ts-kit check` reports the same id from the text of the files, and also an aggregate whose
|
|
69
|
+
state machine exists (`domain/state-machines/<aggregate>.state-machine.ts`) and is not imported.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# ddd-ts-kit/process-sends-commands-only
|
|
2
|
+
|
|
3
|
+
Architecture id: `process/sends-commands-only`. Default severity: `error` in `recommended`, `error` in `strict`.
|
|
4
|
+
|
|
5
|
+
Applies to: `application/processes/*.process.ts` of a context, specs aside.
|
|
6
|
+
|
|
7
|
+
## Why
|
|
8
|
+
|
|
9
|
+
A process manager coordinates a long-running process: it reacts to events, keeps a state of its
|
|
10
|
+
own and decides what happens next. When it also loads and saves aggregates, it becomes a use case
|
|
11
|
+
nobody can send, with no permission, no audit and a transaction that spans what it should only
|
|
12
|
+
coordinate; when it writes the aggregate of another context, the boundary is gone. Its steps
|
|
13
|
+
answer a decision, and the commands of that decision do the work, each in its own handler.
|
|
14
|
+
|
|
15
|
+
## Reported
|
|
16
|
+
|
|
17
|
+
An import, in a process file, of:
|
|
18
|
+
|
|
19
|
+
- `domain/aggregates/`, `domain/entities/`, `domain/repositories/` or `domain/services/` of any
|
|
20
|
+
context
|
|
21
|
+
- `application/ports/` of any context
|
|
22
|
+
- `infrastructure/` of any context
|
|
23
|
+
|
|
24
|
+
Incorrect:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { ReminderRepository } from '@/contexts/notification/domain/repositories/reminder.repository';
|
|
28
|
+
|
|
29
|
+
export class AppointmentReminderProcess { /* saves reminders itself */ }
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Correct:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { PlanReminderCommand } from '@/contexts/notification/application/commands/plan-reminder/plan-reminder.command';
|
|
36
|
+
|
|
37
|
+
export class AppointmentReminderProcess implements ProcessManager<ReminderProcessState> {
|
|
38
|
+
booked(event: AppointmentBookedV1, current: ReminderProcessState | undefined): ProcessDecision<ReminderProcessState> | undefined {
|
|
39
|
+
return { state: { appointmentId: event.payload.appointmentId, stage: 'waiting' }, commands: [new PlanReminderCommand(/* ... */)] };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Details
|
|
45
|
+
|
|
46
|
+
Commands, the published language of upstream contexts, value objects, vocabularies and the
|
|
47
|
+
kernel stay allowed: a process builds commands and reads events.
|
|
48
|
+
|
|
49
|
+
## Options
|
|
50
|
+
|
|
51
|
+
None.
|
|
52
|
+
|
|
53
|
+
`rules."process/sends-commands-only"` in `ddd-ts-kit.config.json` set to `off` silences the rule,
|
|
54
|
+
and `// ddd-ts-kit-disable-next-line process/sends-commands-only -- <reason>` silences one line,
|
|
55
|
+
exactly as for `ddd-ts-kit check`.
|
|
56
|
+
|
|
57
|
+
## Limits
|
|
58
|
+
|
|
59
|
+
- An import read by a dynamic expression is not followed.
|
|
60
|
+
|
|
61
|
+
## With the CLI checker
|
|
62
|
+
|
|
63
|
+
`ddd-ts-kit check` reports the same id from the text of the files.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ddd-ts-kit/eslint-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "AST-accurate ESLint rules for DDD and Clean Architecture in TypeScript: layer and context isolation, domain purity, clean code ordering.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|