ddd-ts-kit 0.15.0 → 0.16.1
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.fr.md +1594 -0
- package/README.md +426 -6
- package/dist/check/catalogue/architecture-rules.d.ts.map +1 -1
- package/dist/check/catalogue/architecture-rules.js +2 -0
- package/dist/check/catalogue/architecture-rules.js.map +1 -1
- package/dist/check/explain/domain-docs.d.ts +3 -0
- package/dist/check/explain/domain-docs.d.ts.map +1 -0
- package/dist/check/explain/domain-docs.js +30 -0
- package/dist/check/explain/domain-docs.js.map +1 -0
- package/dist/check/explain/rule-docs.d.ts.map +1 -1
- package/dist/check/explain/rule-docs.js +2 -0
- package/dist/check/explain/rule-docs.js.map +1 -1
- package/dist/check/fixtures/fixtures.d.ts.map +1 -1
- package/dist/check/fixtures/fixtures.js +4 -0
- package/dist/check/fixtures/fixtures.js.map +1 -1
- package/dist/check/fixtures/model-function-fixtures.d.ts +4 -0
- package/dist/check/fixtures/model-function-fixtures.d.ts.map +1 -0
- package/dist/check/fixtures/model-function-fixtures.js +52 -0
- package/dist/check/fixtures/model-function-fixtures.js.map +1 -0
- package/dist/check/fixtures/value-object-file-fixtures.d.ts +4 -0
- package/dist/check/fixtures/value-object-file-fixtures.d.ts.map +1 -0
- package/dist/check/fixtures/value-object-file-fixtures.js +54 -0
- package/dist/check/fixtures/value-object-file-fixtures.js.map +1 -0
- package/dist/check/fixtures/value-object-files.d.ts +34 -0
- package/dist/check/fixtures/value-object-files.d.ts.map +1 -0
- package/dist/check/fixtures/value-object-files.js +46 -0
- package/dist/check/fixtures/value-object-files.js.map +1 -0
- package/dist/check/rules/domain-value-object-class.d.ts +3 -0
- package/dist/check/rules/domain-value-object-class.d.ts.map +1 -0
- package/dist/check/rules/domain-value-object-class.js +21 -0
- package/dist/check/rules/domain-value-object-class.js.map +1 -0
- package/dist/check/structure/class-body-brace.d.ts +6 -0
- package/dist/check/structure/class-body-brace.d.ts.map +1 -0
- package/dist/check/structure/class-body-brace.js +22 -0
- package/dist/check/structure/class-body-brace.js.map +1 -0
- package/dist/check/structure/read-structure.d.ts.map +1 -1
- package/dist/check/structure/read-structure.js +3 -1
- package/dist/check/structure/read-structure.js.map +1 -1
- package/dist/check/values/answered-literals.d.ts +8 -0
- package/dist/check/values/answered-literals.d.ts.map +1 -0
- package/dist/check/values/answered-literals.js +20 -0
- package/dist/check/values/answered-literals.js.map +1 -0
- package/dist/check/values/arrow-body.d.ts +4 -0
- package/dist/check/values/arrow-body.d.ts.map +1 -0
- package/dist/check/values/arrow-body.js +52 -0
- package/dist/check/values/arrow-body.js.map +1 -0
- package/dist/check/values/entry-role.d.ts +10 -0
- package/dist/check/values/entry-role.d.ts.map +1 -0
- package/dist/check/values/entry-role.js +40 -0
- package/dist/check/values/entry-role.js.map +1 -0
- package/dist/check/values/exported-function-body.d.ts +9 -0
- package/dist/check/values/exported-function-body.d.ts.map +1 -0
- package/dist/check/values/exported-function-body.js +29 -0
- package/dist/check/values/exported-function-body.js.map +1 -0
- package/dist/check/values/exported-values.d.ts +9 -0
- package/dist/check/values/exported-values.d.ts.map +1 -0
- package/dist/check/values/exported-values.js +48 -0
- package/dist/check/values/exported-values.js.map +1 -0
- package/dist/check/values/extends-value-object.d.ts +9 -0
- package/dist/check/values/extends-value-object.d.ts.map +1 -0
- package/dist/check/values/extends-value-object.js +25 -0
- package/dist/check/values/extends-value-object.js.map +1 -0
- package/dist/check/values/function-body.d.ts +10 -0
- package/dist/check/values/function-body.d.ts.map +1 -0
- package/dist/check/values/function-body.js +2 -0
- package/dist/check/values/function-body.js.map +1 -0
- package/dist/check/values/generate-hint.d.ts +4 -0
- package/dist/check/values/generate-hint.d.ts.map +1 -0
- package/dist/check/values/generate-hint.js +8 -0
- package/dist/check/values/generate-hint.js.map +1 -0
- package/dist/check/values/initializer-of.d.ts +3 -0
- package/dist/check/values/initializer-of.d.ts.map +1 -0
- package/dist/check/values/initializer-of.js +15 -0
- package/dist/check/values/initializer-of.js.map +1 -0
- package/dist/check/values/is-model-file.d.ts +8 -0
- package/dist/check/values/is-model-file.d.ts.map +1 -0
- package/dist/check/values/is-model-file.js +15 -0
- package/dist/check/values/is-model-file.js.map +1 -0
- package/dist/check/values/is-value-object-file.d.ts +7 -0
- package/dist/check/values/is-value-object-file.d.ts.map +1 -0
- package/dist/check/values/is-value-object-file.js +14 -0
- package/dist/check/values/is-value-object-file.js.map +1 -0
- package/dist/check/values/javascript-globals.d.ts +3 -0
- package/dist/check/values/javascript-globals.d.ts.map +1 -0
- package/dist/check/values/javascript-globals.js +22 -0
- package/dist/check/values/javascript-globals.js.map +1 -0
- package/dist/check/values/literal-at.d.ts +3 -0
- package/dist/check/values/literal-at.d.ts.map +1 -0
- package/dist/check/values/literal-at.js +7 -0
- package/dist/check/values/literal-at.js.map +1 -0
- package/dist/check/values/local-declaration.d.ts +8 -0
- package/dist/check/values/local-declaration.d.ts.map +1 -0
- package/dist/check/values/local-declaration.js +31 -0
- package/dist/check/values/local-declaration.js.map +1 -0
- package/dist/check/values/nested-bodies.d.ts +8 -0
- package/dist/check/values/nested-bodies.d.ts.map +1 -0
- package/dist/check/values/nested-bodies.js +28 -0
- package/dist/check/values/nested-bodies.js.map +1 -0
- package/dist/check/values/plain-value-functions.d.ts +10 -0
- package/dist/check/values/plain-value-functions.d.ts.map +1 -0
- package/dist/check/values/plain-value-functions.js +33 -0
- package/dist/check/values/plain-value-functions.js.map +1 -0
- package/dist/check/values/returns-package-type.d.ts +9 -0
- package/dist/check/values/returns-package-type.d.ts.map +1 -0
- package/dist/check/values/returns-package-type.js +12 -0
- package/dist/check/values/returns-package-type.js.map +1 -0
- package/dist/check/values/stands-for-a-value.d.ts +9 -0
- package/dist/check/values/stands-for-a-value.d.ts.map +1 -0
- package/dist/check/values/stands-for-a-value.js +28 -0
- package/dist/check/values/stands-for-a-value.js.map +1 -0
- package/dist/check/values/value-object-file-findings.d.ts +5 -0
- package/dist/check/values/value-object-file-findings.d.ts.map +1 -0
- package/dist/check/values/value-object-file-findings.js +96 -0
- package/dist/check/values/value-object-file-findings.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +1 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/parse-stack.d.ts.map +1 -1
- package/dist/config/parse-stack.js +2 -1
- package/dist/config/parse-stack.js.map +1 -1
- package/dist/config/stack.d.ts +8 -0
- package/dist/config/stack.d.ts.map +1 -1
- package/dist/config/stack.js +1 -0
- package/dist/config/stack.js.map +1 -1
- package/dist/help/usage.d.ts +1 -1
- package/dist/help/usage.d.ts.map +1 -1
- package/dist/help/usage.js +1 -1
- package/dist/init/manifest/versions.js +24 -24
- package/dist/init/stack/resolve-stack.d.ts.map +1 -1
- package/dist/init/stack/resolve-stack.js +3 -1
- package/dist/init/stack/resolve-stack.js.map +1 -1
- package/dist/init/stack/stack-options.d.ts +2 -0
- package/dist/init/stack/stack-options.d.ts.map +1 -1
- package/dist/stack/stack-vars.js +2 -1
- package/dist/stack/stack-vars.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/templates/init/rules/domain.md.tpl +1 -1
- package/templates/pipeline/package-scripts.json +1 -1
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
**English** · [Français](README.fr.md)
|
|
2
|
+
|
|
1
3
|
# ddd-ts-kit
|
|
2
4
|
|
|
3
5
|
Bootstrap, scaffold, document and verify Domain-Driven Design and Clean Architecture projects
|
|
@@ -11,6 +13,15 @@ dependency-cruiser, knip, coverage thresholds, git hooks and the CI of GitHub or
|
|
|
11
13
|
The generator and the checker share one definition of the canonical tree, and a test generates
|
|
12
14
|
every kind for every stack and requires zero violation: they cannot disagree.
|
|
13
15
|
|
|
16
|
+
The main commands fall into two families. The first writes the project: `init` once, `generate` for
|
|
17
|
+
each piece, `docs`, `sdk` and `depcruise-config` for what is derived from the code and the
|
|
18
|
+
config, and three migrations that move a project from an older form to the current one. The
|
|
19
|
+
second reads the project back without changing it: `check` judges it, `list` and `describe` state
|
|
20
|
+
what it holds, `status` and `todo` say what is left to do, `affected` says what a pull request has
|
|
21
|
+
to run.
|
|
22
|
+
|
|
23
|
+

|
|
24
|
+
|
|
14
25
|
No runtime dependency. Node 22.12 or later.
|
|
15
26
|
|
|
16
27
|
```
|
|
@@ -52,7 +63,8 @@ ddd-ts-kit init [--http express|fastify|hono|nest|none]
|
|
|
52
63
|
[--style declared|explicit] [--assembly barrel|discovery]
|
|
53
64
|
[--dependencies signature|listed] [--refusals factories|classes]
|
|
54
65
|
[--routes controllers|functions] [--value-objects throwing|result]
|
|
55
|
-
[--literals named|free] [--
|
|
66
|
+
[--literals named|free] [--values classes|free] [--tenancy none|shared-table]
|
|
67
|
+
[--queue database|redis|memory]
|
|
56
68
|
[--schemas shared|per-route]
|
|
57
69
|
[--package-manager npm|pnpm]
|
|
58
70
|
[--allow-no-outbox "<reason>"] [--import-alias '$<name>/']
|
|
@@ -97,7 +109,7 @@ existed keeps its spelling. See [docs/CONFIG.md](../../docs/CONFIG.md).
|
|
|
97
109
|
|
|
98
110
|
Defaults: `express`, `zod`, `tsyringe` (`nest` with `--http nest`, and only then), `vitest`,
|
|
99
111
|
`drizzle` on `postgres`, `outbox` when there is an ORM, `swagger`, `github`, `jwt`, `rbac`, no broker,
|
|
100
|
-
the cache in the process, the audit trail `on` when there is an ORM, unexpected failures reported
|
|
112
|
+
the cache in the process, files in a local directory (`--storage local`), the audit trail `on` when there is an ORM, unexpected failures reported
|
|
101
113
|
nowhere (`--errors none`), JSON lines on the standard output (`--logger stdout`), and the
|
|
102
114
|
`declared` style with a `barrel` wherever the declarations are read: with `--di tsyringe`.
|
|
103
115
|
|
|
@@ -109,7 +121,7 @@ proven by a real install of a generated project, its build and its `verify`. The
|
|
|
109
121
|
from the registry either way.
|
|
110
122
|
|
|
111
123
|
**Pipelines.** A pull or merge request runs the unit specs, the integration specs and the lint of
|
|
112
|
-
the contexts its change reaches through the context map (`ddd-ts-kit affected`); types,
|
|
124
|
+
the contexts its change reaches through the context map ([`ddd-ts-kit affected`](#affected)); types,
|
|
113
125
|
architecture, import graph, dead code, end to end, build and documents always run on the whole
|
|
114
126
|
project, and the default branch and the nightly schedule run everything.
|
|
115
127
|
|
|
@@ -131,6 +143,104 @@ files it imports itself are compiled by the runner. Discovery is accepted behind
|
|
|
131
143
|
context listens on the subscriber `EventSubscribers.forContext(name)` hands it, in either assembly.
|
|
132
144
|
A config written before `stack.style` existed is explicit.
|
|
133
145
|
|
|
146
|
+
The same use case in both styles. In the declared style, the command and its handler carry their
|
|
147
|
+
decorator, and the handler's constructor says what it needs. The body of the handler is the one
|
|
148
|
+
`generate command billing IssueInvoice --aggregate Invoice --permission invoice.issue` writes,
|
|
149
|
+
before its `TODO(domain)` is answered; the input of the command is filled in:
|
|
150
|
+
|
|
151
|
+
<!-- checked sample -->
|
|
152
|
+
```ts
|
|
153
|
+
import { definePermissions } from '@ddd-ts-kit/auth';
|
|
154
|
+
import { type Actor, AggregateRoot, AuthorizedCommand, type Clock, type IdGenerator, Repository } from '@ddd-ts-kit/kernel';
|
|
155
|
+
import { CommandHandler, Requires } from '@ddd-ts-kit/kernel/declarations';
|
|
156
|
+
|
|
157
|
+
declare class Invoice extends AggregateRoot {
|
|
158
|
+
static create(id: string, now: Date): Invoice;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// domain/repositories/invoice.repository.ts
|
|
162
|
+
export abstract class InvoiceRepository extends Repository<Invoice> {}
|
|
163
|
+
|
|
164
|
+
// application/authorization/billing.permissions.ts
|
|
165
|
+
export const billingPermissions = definePermissions('billing', {
|
|
166
|
+
'invoice.issue': { description: 'Issue invoice' },
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// application/commands/issue-invoice/issue-invoice.command.ts
|
|
170
|
+
@Requires(billingPermissions['invoice.issue'])
|
|
171
|
+
export class IssueInvoiceCommand extends AuthorizedCommand<string> {
|
|
172
|
+
constructor(
|
|
173
|
+
actor: Actor,
|
|
174
|
+
readonly customerId: string,
|
|
175
|
+
) {
|
|
176
|
+
super(actor);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// application/commands/issue-invoice/issue-invoice.handler.ts
|
|
181
|
+
@CommandHandler(IssueInvoiceCommand)
|
|
182
|
+
export class IssueInvoiceHandler implements CommandHandler<IssueInvoiceCommand> {
|
|
183
|
+
// each parameter is typed by one class, which is also its injection token
|
|
184
|
+
constructor(
|
|
185
|
+
private readonly invoiceRepository: InvoiceRepository,
|
|
186
|
+
private readonly ids: IdGenerator,
|
|
187
|
+
private readonly clock: Clock,
|
|
188
|
+
) {}
|
|
189
|
+
|
|
190
|
+
async execute(command: IssueInvoiceCommand): Promise<string> {
|
|
191
|
+
const target = Invoice.create(this.ids.next(), this.clock.now());
|
|
192
|
+
|
|
193
|
+
await this.invoiceRepository.save(target);
|
|
194
|
+
|
|
195
|
+
return target.id;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
In the explicit style, the same classes carry no decorator, and the module file of the context
|
|
201
|
+
binds each of them, with its permission and the ports it is given:
|
|
202
|
+
|
|
203
|
+
<!-- checked sample -->
|
|
204
|
+
```ts
|
|
205
|
+
import { definePermissions } from '@ddd-ts-kit/auth';
|
|
206
|
+
import { defineContextModule } from '@ddd-ts-kit/di';
|
|
207
|
+
import { AggregateRoot, AuthorizedCommand, Clock, type CommandHandler, IdGenerator, Repository } from '@ddd-ts-kit/kernel';
|
|
208
|
+
|
|
209
|
+
// the classes of the declared sample, without their decorators
|
|
210
|
+
declare class Invoice extends AggregateRoot {}
|
|
211
|
+
declare class IssueInvoiceCommand extends AuthorizedCommand<string> {}
|
|
212
|
+
declare class IssueInvoiceHandler implements CommandHandler<IssueInvoiceCommand> {
|
|
213
|
+
constructor(invoiceRepository: InvoiceRepository, ids: IdGenerator, clock: Clock);
|
|
214
|
+
execute(command: IssueInvoiceCommand): Promise<string>;
|
|
215
|
+
}
|
|
216
|
+
declare class InMemoryInvoiceRepository implements InvoiceRepository {
|
|
217
|
+
findById(id: string): Promise<Invoice | undefined>;
|
|
218
|
+
save(invoice: Invoice): Promise<void>;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export abstract class InvoiceRepository extends Repository<Invoice> {}
|
|
222
|
+
|
|
223
|
+
export const billingPermissions = definePermissions('billing', {
|
|
224
|
+
'invoice.issue': { description: 'Issue invoice' },
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// billing.module.ts: every binding of the context, one line each
|
|
228
|
+
export const billingModule = defineContextModule({
|
|
229
|
+
name: 'billing',
|
|
230
|
+
register(builder) {
|
|
231
|
+
builder.bind(InvoiceRepository).toClass(InMemoryInvoiceRepository).as('repository');
|
|
232
|
+
builder
|
|
233
|
+
.command(IssueInvoiceCommand)
|
|
234
|
+
.requires(billingPermissions['invoice.issue'])
|
|
235
|
+
.handledBy(IssueInvoiceHandler, [InvoiceRepository, IdGenerator, Clock]);
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
`check` holds both forms: `authz/declared` reports a command that says neither who may send it
|
|
241
|
+
nor that it is public, `declarations/role-declared` a role file of the declared style without its
|
|
242
|
+
decorator.
|
|
243
|
+
|
|
134
244
|
**A port is an abstract class and its own injection token.** `application/ports/<name>.port.ts`
|
|
135
245
|
and `domain/repositories/<name>.repository.ts` hold one abstract class each. Generated code holds
|
|
136
246
|
no `token()`, no UPPER_SNAKE injection constant, no `<context>.ports.ts` and no
|
|
@@ -139,6 +249,50 @@ no `token()`, no UPPER_SNAKE injection constant, no `<context>.ports.ts` and no
|
|
|
139
249
|
list or a binding and type imports where they only type a parameter, an adapter `implements`
|
|
140
250
|
its port.
|
|
141
251
|
|
|
252
|
+
Instead of an interface, a token constant and an injection by name:
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
export interface IPaymentGateway {
|
|
256
|
+
charge(invoiceId: string, amountInCents: number): Promise<void>;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export const PAYMENT_GATEWAY = Symbol('PaymentGateway'); // ports/no-token-constant
|
|
260
|
+
|
|
261
|
+
constructor(@inject(PAYMENT_GATEWAY) private readonly payments: IPaymentGateway) {}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
write one abstract class, which is both the contract and the token:
|
|
265
|
+
|
|
266
|
+
<!-- checked sample -->
|
|
267
|
+
```ts
|
|
268
|
+
import { Adapter } from '@ddd-ts-kit/kernel/declarations';
|
|
269
|
+
|
|
270
|
+
// application/ports/payment-gateway.port.ts
|
|
271
|
+
export abstract class PaymentGateway {
|
|
272
|
+
abstract charge(invoiceId: string, amountInCents: number): Promise<void>;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// infrastructure/external/remote-payment-gateway.adapter.ts: implements the port, never extends it
|
|
276
|
+
@Adapter(PaymentGateway, { kind: 'external' })
|
|
277
|
+
export class RemotePaymentGateway implements PaymentGateway {
|
|
278
|
+
async charge(invoiceId: string, amountInCents: number): Promise<void> {
|
|
279
|
+
// TODO(platform): call the payment provider with invoiceId and amountInCents
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// a handler names the port by the type of its parameter: nothing else says what is injected
|
|
284
|
+
export class SettleInvoiceHandler {
|
|
285
|
+
constructor(private readonly payments: PaymentGateway) {}
|
|
286
|
+
|
|
287
|
+
async settle(invoiceId: string, amountInCents: number): Promise<void> {
|
|
288
|
+
await this.payments.charge(invoiceId, amountInCents);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
`ports/abstract-class`, `ports/implements-never-extends`, `ports/no-token-constant` and
|
|
294
|
+
`ports/value-import` hold this form, in `check` and in ESLint.
|
|
295
|
+
|
|
142
296
|
**The constructor states the dependencies.** In the declared style a generated class lists
|
|
143
297
|
nothing in its decorator (`@CommandHandler(IssueInvoiceCommand)`): each constructor parameter
|
|
144
298
|
is typed by one class, its token (`declarations/constructor-injection`). `generate`, `docs` and
|
|
@@ -153,6 +307,31 @@ is absent with standard decorators and under `tsx`. `init` writes
|
|
|
153
307
|
what each class is given. Every generated file exports one class, interface or function and is named
|
|
154
308
|
after it.
|
|
155
309
|
|
|
310
|
+

|
|
311
|
+
|
|
312
|
+
An excerpt of the barrel of the `notification` context of the example application, as the CLI
|
|
313
|
+
writes it (never by hand):
|
|
314
|
+
|
|
315
|
+
```ts
|
|
316
|
+
// src/contexts/notification/notification.declarations.ts
|
|
317
|
+
import { injected } from '@ddd-ts-kit/kernel/declarations';
|
|
318
|
+
import { WithdrawReminderHandler } from '@/contexts/notification/application/commands/withdraw-reminder/withdraw-reminder.handler';
|
|
319
|
+
import { ReminderRepository } from '@/contexts/notification/domain/repositories/reminder.repository';
|
|
320
|
+
import { KnexReminderRepository } from '@/contexts/notification/infrastructure/persistence/knex-reminder.repository';
|
|
321
|
+
import { Clock, ExecutionScope } from '@ddd-ts-kit/kernel';
|
|
322
|
+
import { KnexContext } from '@ddd-ts-kit/knex';
|
|
323
|
+
|
|
324
|
+
export * from '@/contexts/notification/application/commands/withdraw-reminder/withdraw-reminder.command';
|
|
325
|
+
export * from '@/contexts/notification/application/commands/withdraw-reminder/withdraw-reminder.handler';
|
|
326
|
+
export * from '@/contexts/notification/infrastructure/persistence/knex-reminder.repository';
|
|
327
|
+
|
|
328
|
+
injected(WithdrawReminderHandler, [ReminderRepository]);
|
|
329
|
+
injected(KnexReminderRepository, [KnexContext, ExecutionScope, Clock]);
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
and the one line of the composition root that adds the whole context:
|
|
333
|
+
`contextModule('notification', notificationDeclarations)` in `src/platform/compose-application.ts`.
|
|
334
|
+
|
|
156
335
|
**Domain errors are built by factories.** `init` writes `"stack.refusals": "factories"`: a
|
|
157
336
|
context has one error class per concept, extending `ConceptError` of the kernel, and
|
|
158
337
|
`generate error <context> <Concept> <refusal>` adds a static factory to it. The `errors/*`
|
|
@@ -160,6 +339,46 @@ rules hold that form. `--refusals classes`, or a config without the key, keeps o
|
|
|
160
339
|
error built with `new`, as 0.1 and 0.2 wrote them; `error-factories` moves a project from one
|
|
161
340
|
to the other.
|
|
162
341
|
|
|
342
|
+
The older form, under `"stack.refusals": "classes"`: one class per error.
|
|
343
|
+
|
|
344
|
+
<!-- checked sample -->
|
|
345
|
+
```ts
|
|
346
|
+
import { DomainError } from '@ddd-ts-kit/kernel';
|
|
347
|
+
|
|
348
|
+
// domain/errors/slot-taken.error.ts
|
|
349
|
+
export class SlotTakenError extends DomainError {
|
|
350
|
+
readonly code = 'SLOT_TAKEN';
|
|
351
|
+
|
|
352
|
+
constructor(readonly calendarId: string) {
|
|
353
|
+
super(`The slot of calendar ${calendarId} is already held.`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const refusal = new SlotTakenError('calendar-1');
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
The form `init` writes: one class per concept of the glossary, one factory per refusal, which
|
|
361
|
+
`generate error scheduling Appointment slotTaken` adds.
|
|
362
|
+
|
|
363
|
+
<!-- checked sample -->
|
|
364
|
+
```ts
|
|
365
|
+
import { ConceptError } from '@ddd-ts-kit/kernel';
|
|
366
|
+
|
|
367
|
+
// domain/errors/appointment.error.ts
|
|
368
|
+
export class AppointmentError extends ConceptError {
|
|
369
|
+
static slotTaken(calendarId: string) {
|
|
370
|
+
return AppointmentError.refusal('SLOT_TAKEN', `The slot of calendar ${calendarId} is already held.`, { calendarId });
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const refusal = AppointmentError.slotTaken('calendar-1');
|
|
375
|
+
|
|
376
|
+
// what catches it tells the refusal by its class and its code, and reads its facts
|
|
377
|
+
if (AppointmentError.is(refusal, 'SLOT_TAKEN')) {
|
|
378
|
+
console.log(refusal.facts.calendarId);
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
163
382
|
**A route is a controller class.** `init` writes `"stack.routes": "controllers"`: each route is
|
|
164
383
|
one class with one method, in a folder of its own, behind the guards of its route group, and a
|
|
165
384
|
declared context lists no route: its controllers reach the application through its generated
|
|
@@ -187,6 +406,65 @@ the aggregate recorded are dispatched once the save resolves, inside the unit of
|
|
|
187
406
|
command: by the kernel module on tsyringe, by `dispatchingOnSave` around each repository in
|
|
188
407
|
`platform/create-platform.ts` with hand wiring (`domain-events/dispatched-on-save`).
|
|
189
408
|
|
|
409
|
+
Instead of pulling the events and dispatching them in the handler:
|
|
410
|
+
|
|
411
|
+
```ts
|
|
412
|
+
async execute(command: IssueInvoiceCommand): Promise<void> {
|
|
413
|
+
const invoice = await this.invoiceRepository.findById(command.invoiceId);
|
|
414
|
+
|
|
415
|
+
invoice.issue(this.clock.now());
|
|
416
|
+
await this.invoiceRepository.save(invoice);
|
|
417
|
+
await this.dispatcher.dispatch(invoice.pullEvents()); // domain-events/dispatched-on-save
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
write the save, and nothing after it:
|
|
422
|
+
|
|
423
|
+
<!-- checked sample -->
|
|
424
|
+
```ts
|
|
425
|
+
import { type Actor, AggregateRoot, AuthorizedCommand, type Clock, type CommandHandler, ConceptError, Repository } from '@ddd-ts-kit/kernel';
|
|
426
|
+
|
|
427
|
+
declare class Invoice extends AggregateRoot {
|
|
428
|
+
/** Makes the invoice final and records InvoiceIssued, which waits in the aggregate. */
|
|
429
|
+
issue(now: Date): void;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
declare class InvoiceError extends ConceptError {
|
|
433
|
+
static notFound(invoiceId: string): InvoiceError;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
export abstract class InvoiceRepository extends Repository<Invoice> {}
|
|
437
|
+
|
|
438
|
+
export class IssueInvoiceCommand extends AuthorizedCommand<void> {
|
|
439
|
+
constructor(
|
|
440
|
+
actor: Actor,
|
|
441
|
+
readonly invoiceId: string,
|
|
442
|
+
) {
|
|
443
|
+
super(actor);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
export class IssueInvoiceHandler implements CommandHandler<IssueInvoiceCommand> {
|
|
448
|
+
constructor(
|
|
449
|
+
private readonly invoiceRepository: InvoiceRepository,
|
|
450
|
+
private readonly clock: Clock,
|
|
451
|
+
) {}
|
|
452
|
+
|
|
453
|
+
async execute(command: IssueInvoiceCommand): Promise<void> {
|
|
454
|
+
const invoice = await this.invoiceRepository.findById(command.invoiceId);
|
|
455
|
+
|
|
456
|
+
if (invoice === undefined) {
|
|
457
|
+
throw InvoiceError.notFound(command.invoiceId);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
invoice.issue(this.clock.now());
|
|
461
|
+
|
|
462
|
+
// InvoiceIssued is dispatched once this save resolves, inside the unit of work of the command
|
|
463
|
+
await this.invoiceRepository.save(invoice);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
```
|
|
467
|
+
|
|
190
468
|
`--events in-memory` together with an ORM is refused unless `--allow-no-outbox "<reason>"` is given:
|
|
191
469
|
it records a `deviations` entry (`stack.events`) and writes the decision record it points to. The generated
|
|
192
470
|
persistence of the four ORMs is written for PostgreSQL; `custom` starts on in-memory adapters
|
|
@@ -211,6 +489,7 @@ config, `docsUi`, `ci` and `logger` included.
|
|
|
211
489
|
| `--http-cache varnish` | a compose service `varnish` (Varnish 9.1.0, reaching the API on `host.docker.internal`) with its VCL in `docker/varnish/default.vcl` (only answers whose `Cache-Control` allows them by `max-age` or `s-maxage` are kept, filed under their `xkey` tags, purged by `PURGE` from the machine of Varnish or with `VARNISH_PURGE_KEY`, `x-cache: HIT\|MISS`), `VARNISH_URL` and `VARNISH_PURGE_KEY` validated, `cacheTags: { header: CacheTagHeader.xkey }` in the HTTP boundary, and the outbox relay of `main.ts` and of every `generate service` entry point publishing through `PurgingPublisher` with `VarnishCachePurger` (the end-to-end application records the purges in `purges`); on `--di manual` and `nest` the platform gains `cacheInvalidation`; with `--tenancy`, the tags of the answers and the purges are filed under the tenant (`perTenant`) and the tenant of a request is read from `x-tenant-id`. Needs an HTTP framework and `--events outbox`: a purge is a row of the outbox |
|
|
212
490
|
| `--errors sentry` | unexpected failures reported to Sentry or GlitchTip (one protocol, only the DSN differs) through the kernel `ErrorReporter`, bound to `@ddd-ts-kit/sentry`. `src/platform/errors.ts` (`initSentry` on the validated configuration, then `reportProcessFailures`), the second import of `main.ts`, of `apps/<context>/main.ts` and of `apps/worker/main.ts`, right after `tracing.ts`; `errors` handed to `composeApplication`, which gives it to the kernel module on tsyringe (`errorReporter`, flushed by `container.stop()` before the logger, `errorReportingMiddleware` first on both buses, the default scheduler observing with `reportFailedRuns`) or to `createPlatform` by hand (the same middleware, `onRun: reportFailedRuns(errors)`, `platform.errors`, flushed two seconds at most when the application stops); `reportHttpErrors` in `auth/http-boundary.ts` for every request answered 5xx; `DeadLetterReports` on the `onFailure` hook of a provided broker (`createEventProvider(config, errors)`, with `observing` around each consumer) and of the outbox relay. `SENTRY_DSN`, `SENTRY_ENVIRONMENT` (default: `NODE_ENV`), `SENTRY_RELEASE` and `SENTRY_SCRUB_MESSAGES` validated in `config/load-config.ts` as `errorReporting`: without a DSN the application starts and reports nothing; a DSN that is not a URL refuses the start. Dependencies `@ddd-ts-kit/sentry` and `@sentry/node`. A compose profile `errors` running GlitchTip 6.2.6 with its own PostgreSQL and Valkey (`GLITCHTIP_PORT`, loopback only, development bootstrap), and a section of the generated README saying how to read its DSN. The tools and the tests compose the application without a reporter. It goes with every other choice of the stack; with `--errors none` nothing of this is written |
|
|
213
491
|
| `--authn keycloak` | a compose service `keycloak` (Keycloak 26.8.0, `start-dev --import-realm`, profile `identity`) and `docker/keycloak/<project>-realm.json`: the API client, a public client `<project>-local` with the direct grant, its audience and `tenant_id` mappers, the realm role `admin`, and the demo users `demo-admin` and `demo-user`, for local development only |
|
|
492
|
+
| `--storage local\|s3` | recorded as `stack.storage`: where the content of files lives, never in the database. `src/platform/config/read-files.ts` and `src/platform/files/open-files.ts`, one per choice: `local`, a directory in development (`FILES_DIRECTORY`, `.data/files`) and the signed links answered by the application under `/files` (`FILES_PUBLIC_URL` and `FILES_SIGNING_SECRET` required in production); `s3`, a bucket of any provider of the S3 protocol through `@ddd-ts-kit/s3` (the `S3_*` variables, MinIO in the compose stack); the memory of the process in the tests and the tools, either way. With `--di tsyringe`: `files/bind-files.ts`, `files/files.module.ts` and `files/staging-uploads.ts`; with hand wiring `files/create-files.ts`; with an HTTP stack `files/file-routes.ts`. They bind `FileStorage`, `FileKeys`, `FileScanner` and `FileStaging`, register the hourly purge of staged files and stage the file a command carries before its transaction; `generate file-upload` needs them. `--storage none` writes none of this ([File storage](../../docs/concepts/file-storage.md)) |
|
|
214
493
|
| `src/platform/events/` | `event-provider.ts` (`bus`, `forConsumer(name)`, `start()`, `close()`), `in-process-events.ts`, and for `--broker` `create-event-provider.ts`: `RabbitMqEventBus` (`AMQP_URL`), `KafkaEventBus` (`KAFKA_BROKERS`, with `aggregate-key-of.ts` for per-aggregate order), `NatsEventBus` (`NATS_URL`) or `RedisStreamsEventBus` of `@ddd-ts-kit/redis/event-bus` (`REDIS_URL`: Redis, Dragonfly or Valkey), each with one subscriber per consuming context (`forConsumer`), `assertTopology` at start, its address validated and a compose service with a healthcheck; or `custom-event-bus.ts` with its `eventBusContract` spec. `start-outbox-relay.ts` with the outbox. `--broker redis` with `--cache redis` is one server and one compose service, `cache`; alone, the service is `streams` |
|
|
215
494
|
| `src/platform/database/` | `persistence.ts` (`interface Persistence`: unit of work, outbox and processed-event stores of the ORM package, the storage handle), `open-persistence.ts`, `persistence-over.ts`, `inert-persistence.ts`, the connection (`connect-to-database.ts`, `create-client.ts` or `create-data-source.ts`), `schema.ts`, `open-test-database.ts` (PostgreSQL in process, or the server named by a URL). The storage class a repository adapter names in its dependency list: `database.ts` with drizzle and prisma (`abstract class Database extends` the context type of the ORM package), `KnexContext` of `@ddd-ts-kit/knex`, `TypeOrmContext` of `@ddd-ts-kit/typeorm` |
|
|
216
495
|
| migration tooling | `drizzle.config.ts`, `knexfile.js` and `migrations/00000000000000_initial.js`, `prisma/schema.prisma` and `prisma.config.ts`, or `data-source.ts` |
|
|
@@ -296,10 +575,42 @@ ddd-ts-kit generate <kind> ... [--term "<Term>=<meaning>"]... [--allow-name] [--
|
|
|
296
575
|
ddd-ts-kit generate --help --json
|
|
297
576
|
```
|
|
298
577
|
|
|
578
|
+
One run of `generate` does five things, in this order, and writes nothing when the first one
|
|
579
|
+
refuses:
|
|
580
|
+
|
|
581
|
+

|
|
582
|
+
|
|
583
|
+
In the example application, a use case named with a word its glossary does not define is refused,
|
|
584
|
+
and the message says how to go on:
|
|
585
|
+
|
|
586
|
+
```
|
|
587
|
+
$ npx ddd-ts-kit generate command notification ArchiveNotification --permission notification.archive --aggregate Notification
|
|
588
|
+
error: "ArchiveNotification" uses "archive", which the glossary of src/contexts/notification/CONTEXT.md does not define.
|
|
589
|
+
fix: Either use an existing term (Notification, Send, Recipient, Contact, Confirmation, Reminder, Plan, Due, Withdraw, Mailer, Kind, Request, Rescheduling, Cancellation, Read, Search), or add the term in the same command: --term "Archive=<what it means for the business>". The glossary is written before the code (rule language/glossary-term).
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
With the term given in the same command (and `--dry-run`, which says what it would write):
|
|
593
|
+
|
|
594
|
+
```
|
|
595
|
+
$ npx ddd-ts-kit generate command notification ArchiveNotification --permission notification.archive --aggregate Notification \
|
|
596
|
+
--term "Archive=To set a sent notification aside, out of the default search" --dry-run
|
|
597
|
+
would create src/contexts/notification/application/commands/archive-notification/archive-notification.command.ts
|
|
598
|
+
would create src/contexts/notification/application/commands/archive-notification/archive-notification.handler.ts
|
|
599
|
+
would create src/contexts/notification/test/unit/archive-notification.handler.spec.ts
|
|
600
|
+
would update src/contexts/notification/CONTEXT.md
|
|
601
|
+
would update src/contexts/notification/application/authorization/notification.permissions.ts
|
|
602
|
+
would update src/contexts/notification/notification.declarations.ts
|
|
603
|
+
|
|
604
|
+
Next, in this order: the language of the context first, then the business decisions only you can make
|
|
605
|
+
src/contexts/notification/application/commands/archive-notification/archive-notification.command.ts:9 declare the input of "archive notification" as a constructor with the actor first, ...
|
|
606
|
+
src/contexts/notification/application/commands/archive-notification/archive-notification.handler.ts:16 decide what "archive notification" does to a Notification: ...
|
|
607
|
+
src/contexts/notification/test/unit/archive-notification.handler.spec.ts:38 replace each entry below by a test named after the business behaviour of "archive notification": ...
|
|
608
|
+
```
|
|
609
|
+
|
|
299
610
|
| Kind | Arguments | Writes |
|
|
300
611
|
|---|---|---|
|
|
301
612
|
| `context` | `<name> --classification <c> [--purpose "<two sentences>"] [--term ...]... [--upstream <ctx>[:relationship]]... [--preset identity]` | `CONTEXT.md` with its glossary, `published-language/index.ts`, config entry, registration in `platform/compose-application.ts` and in `test/architecture/extractable.spec.ts`; on tsyringe, with HTTP, `presentation/http/<name>.routes.ts` (a function of the two buses, in both styles); declared: the barrel `<name>.declarations.ts`; explicit and hand wiring: `<name>.module.ts`. `application/authorization/<name>.permissions.ts` is born with the first permission, or at once when `platform/auth/permissions.ts` lists the permission sets (rebac, custom). With `--preset`, the context is then written whole from that preset, its classification and purpose those of the preset unless the flags say otherwise ([Presets](#presets)) |
|
|
302
|
-
| `aggregate` | `<context> <Name
|
|
613
|
+
| `aggregate` | `<context> <Name> [--event-sourced]` | aggregate, builder, spec in given / when / then; with `--event-sourced`, an aggregate on `EventSourcedAggregate` whose state is the replay of its events, born with its `Created` domain event, the codec of its stored events in `infrastructure/persistence/<name>-events.mapper.ts`, a builder that replays a history and a spec given events, and a note naming the `EventSourcedRepository` its repository adapter delegates to |
|
|
303
614
|
| `builder` | `<context> <Aggregate>` | `test/builders/<name>.builder.ts`, a class extending `Builder` |
|
|
304
615
|
| `entity` | `<context> <Name>` | entity, spec |
|
|
305
616
|
| `value-object` (`vo`) | `<context> <Name>` | value object, spec |
|
|
@@ -315,6 +626,7 @@ ddd-ts-kit generate --help --json
|
|
|
315
626
|
| `port` | `<context> <Name>` | `application/ports/<name>.port.ts`, one abstract class, and its fake |
|
|
316
627
|
| `route` | `<context> <UseCaseName> --method <GET\|POST\|PUT\|PATCH\|DELETE> --path <p> [--command\|--query] [--group <Name>] [--mapper] [--serializer] [--cache public\|private\|no-store]` | under `"stack.routes": "controllers"`: a controller class in `presentation/http/<resource>/<use-case>/` (the resource is the first segment of the path), depending on `CommandBus` or `QueryBus`, behind the route group of its resource, written with it when it is new (`<resource>.routes.ts`: prefix, tag, `Authenticated`); a public use case stands in no group and says `guards: [Public]`; with class-validator one class per file (`<use-case>.params.ts`, `.body.ts`, `.answer.ts`, `.accepted.ts`, `.line.ts`); `--mapper` and `--serializer` add a request mapper and a serializer the controller is given; a spec; no registration in a declared project, a controller built by hand in `<context>.routes.ts` (the module file with hand wiring) in an explicit one. Under `"functions"`: a `defineRoute` function, its spec, its registration in `<context>.routes.ts`, and `<use-case>-<role>.dto.ts` classes |
|
|
317
628
|
| `controller` | the same arguments as `route` | the controller class, whatever `stack.routes` says: for a project of function routes that writes its next route as a class. `--cache` writes its cache policy: `private` (a `GET` of a caller, `max-age=0`, `must-revalidate`, strong `ETag`), `public` (a `GET` of a public use case, `max-age` 1m, `s-maxage` 5m, tagged with the resources of its path), `no-store`; refused on a write, and `public` behind a route group |
|
|
629
|
+
| `file-upload` | `<context> <UseCaseName> --path <p> (--permission <aggregate>.<action> \| --public) [--method POST\|PUT] [--group <Name>]` | an upload: a controller that declares the file it receives (its size and media types in the contract, checked as it streams), the command that carries it as a `FileUpload`, staged by the command bus before the transaction, the handler that records it and promotes it with the commit, and their specs. Refused without `stack.storage` or without an HTTP stack, with the `init --force --storage local` to run |
|
|
318
630
|
| `route-group` (`group`) | `<context> <Name> [--prefix <path>] [--group <ParentName>] [--public \| --role <role>]` | `presentation/http/<name>.routes.ts`: an empty class carrying `@RouteGroup`, behind `Authenticated` unless `--public` or `--role` says otherwise (`HasRole(Role.Admin)`, the role read from the vocabulary of the platform, `<platformDir>/auth/role.ts`, which gets it when it does not hold it); with `--group` it refines that group and inherits from it |
|
|
319
631
|
| `guard` | `<context> <Name>` | `presentation/http/guards/<name>.guard.ts`: a declared guard class to fill, and its spec |
|
|
320
632
|
| `representation` | `<context> <UseCase> <xml\|csv\|media/type>[,...]` | adds `produces` to the `@Controller` of the use case, and `consumes: ['application/xml']` to one that receives a body when XML is asked; says when CSV needs a page or a streamed list, and when `stack.representations` is not `negotiated` |
|
|
@@ -322,7 +634,7 @@ ddd-ts-kit generate --help --json
|
|
|
322
634
|
| `schema` | `<context\|shared-kernel> <Name>` | `presentation/http/shared/<name>.schema.ts` of the context, or `presentation/<name>.schema.ts` of the shared kernel: one shape of the contract declared once (a zod schema with `.meta({ id })`, a class-validator class, or a schema wrapped by `sharedSchema`), to fill; the routes use it whole, nested or in part |
|
|
323
635
|
| `integration-event` | `<context> <Aggregate> <Fact> [--version n]` | `defineIntegrationEvent` declaration in the published language (its payload in `<event>.v<n>.payload.ts` with class-validator or a schema of your own), index export, the publisher `application/integration/publishers/<event>.publisher.ts`, spec, registration, a line of `platform/audit/audited-events.ts` |
|
|
324
636
|
| `integration-handler` (`consumer`) | `<context> <upstreamContext> <eventType>` | `application/integration/handlers/<name>.integration-handler.ts`, a class `<Name>IntegrationHandler` depending on `CommandBus`, its spec, its idempotent subscription under the consumer name `<context>.<name>`; refused unless the upstream is declared |
|
|
325
|
-
| `event-handler` | `<context> <Aggregate> <Fact> [--phase after-commit]` | reaction to a domain event of the context, spec, subscription; before the commit of the unit of work by default, after it with `--phase after-commit` (an effect no transaction covers, at most once), and the spec runs the reaction in that phase |
|
|
637
|
+
| `event-handler` | `<context> <Aggregate> <Fact> [--phase before-commit\|after-commit]` | reaction to a domain event of the context, spec, subscription; before the commit of the unit of work by default, after it with `--phase after-commit` (an effect no transaction covers, at most once), and the spec runs the reaction in that phase |
|
|
326
638
|
| `projection` (`projector`) | `<context> <Name> <event-type>` | a projector in `application/projections/<name>.projection.ts`, declared with `@Projection('<context>.<name>')` and one `@On(definition)` method for the integration event, which the context itself or a declared upstream publishes, and its spec; declared style only. It keeps one read model: its port is received in the constructor |
|
|
327
639
|
| `job` | `<context> <Name> (--every <duration> \| --cron "<expression>" [--time-zone <zone>]) --command <UseCaseName>` | `application/scheduling/<name>.job.ts`, a class `<Name>Job implements ScheduledJob` that sends the command, as `systemActor('<context>.<name>')` when it is protected, with `execution.scheduledAt` when the command takes it; `test/unit/<name>.job.spec.ts`, which runs the job (by name on tsyringe, `application.jobs.runNow`; `job.run(occurrence)` with hand wiring) and asserts the command it sent; its registration; its switch in `platform/job-settings.ts`; the grant of its system actor (below) |
|
|
328
640
|
| `background-job` | `<context> <Name> --command <UseCaseName>` | `application/background/<name>.background-job.ts`, a class `<Name>BackgroundJob implements BackgroundJob<<Name>Payload>` named by its static `jobName` `<context>.<name>`, that sends the command as `execution.actor` when it is protected, followed by one field of `<Name>Payload` per other parameter of the constructor of the command (strings, numbers, booleans, literals and lists of them as written, a `Date` as its ISO text; anything else is refused); `test/unit/<name>.background-job.spec.ts`, which enqueues it with a payload of every field as a user holding the permission and runs what is due; its registration (`@Background()`, or `builder.backgroundJob(..)` in the module file). `--di tsyringe` only |
|
|
@@ -621,6 +933,15 @@ this command rewrites, in every `.js`, `.mjs`, `.cjs`, `.d.ts`, `.d.mts` and `.d
|
|
|
621
933
|
output directory of the tsconfig (`tsconfig.build.json` by default), each alias specifier into
|
|
622
934
|
the relative path of the emitted file it names, with its `.js` extension
|
|
623
935
|
(`../../platform/errors.js`), so that `node dist/src/main.js` runs with no loader and no flag.
|
|
936
|
+
|
|
937
|
+
```js
|
|
938
|
+
// dist/src/contexts/billing/domain/aggregates/invoice.aggregate.js, as the compiler emits it
|
|
939
|
+
import { InvoiceError } from '@/contexts/billing/domain/errors/invoice.error';
|
|
940
|
+
|
|
941
|
+
// the same line after ddd-ts-kit build-aliases
|
|
942
|
+
import { InvoiceError } from '../errors/invoice.error.js';
|
|
943
|
+
```
|
|
944
|
+
|
|
624
945
|
It reads `importAlias` and `importExtension` of the config: `@/platform/errors` names
|
|
625
946
|
`platform/errors.js` under `none`, `@/platform/errors.js` names it under `js`, and no other
|
|
626
947
|
spelling is guessed.
|
|
@@ -721,6 +1042,53 @@ from `docs/architecture/slices.md`, the published languages it consumes. An exis
|
|
|
721
1042
|
kept and only the file is written again; `--dry-run` creates and writes nothing. It needs a git
|
|
722
1043
|
repository with one commit at least. The worker then generates with `--local-only`.
|
|
723
1044
|
|
|
1045
|
+
## affected
|
|
1046
|
+
|
|
1047
|
+
```
|
|
1048
|
+
ddd-ts-kit affected [--base <git-ref>] [--head <git-ref>] [--format text|json|github|env]
|
|
1049
|
+
```
|
|
1050
|
+
|
|
1051
|
+
What a pull or merge request has to run, read from the context map and the paths that differ
|
|
1052
|
+
between `--base` and `--head` (`HEAD` by default; the merge base is used when the clone has it).
|
|
1053
|
+
Each changed path reaches something:
|
|
1054
|
+
|
|
1055
|
+
- a path under a declared context reaches that context and every context downstream of it, since
|
|
1056
|
+
their code may read its published language; a path under its `test/` alone reaches that
|
|
1057
|
+
context alone, since no other context may import its tests;
|
|
1058
|
+
- a document reaches nothing that runs: a Markdown file at the root, anything under `docs/`,
|
|
1059
|
+
`openapi/` or `.claude/` (the documents gate always runs);
|
|
1060
|
+
- anything else reaches everything: the platform, the shared kernel, `main.ts`, the config, the
|
|
1061
|
+
lockfile, the pipeline, a directory of `src/contexts/` the config does not declare.
|
|
1062
|
+
|
|
1063
|
+
Without `--base`, or when git cannot tell what changed (an unknown ref, a shallow clone without
|
|
1064
|
+
the base), everything is selected: that is what the default branch and the nightly run get. The
|
|
1065
|
+
selection is never narrower than the declared relationships, because `context/declared-relationship`
|
|
1066
|
+
keeps every import between contexts inside them, and that rule runs on the whole project in every
|
|
1067
|
+
pipeline.
|
|
1068
|
+
|
|
1069
|
+

|
|
1070
|
+
|
|
1071
|
+
In the example application, a branch that changes one aggregate of `scheduling`:
|
|
1072
|
+
|
|
1073
|
+
```
|
|
1074
|
+
$ npx ddd-ts-kit affected --base main
|
|
1075
|
+
Affected contexts: scheduling, notification, analytics (the changed contexts and the contexts downstream of them in the context map).
|
|
1076
|
+
Specs: src/contexts/scheduling/**/*.spec.ts src/contexts/notification/**/*.spec.ts src/contexts/analytics/**/*.spec.ts !src/contexts/**
|
|
1077
|
+
Left to the default branch: src/contexts/identity/
|
|
1078
|
+
|
|
1079
|
+
$ npx ddd-ts-kit affected --base main --format github
|
|
1080
|
+
scope=contexts
|
|
1081
|
+
contexts=scheduling,notification,analytics
|
|
1082
|
+
args=--passWithNoTests
|
|
1083
|
+
lint=--ignore-pattern src/contexts/identity/
|
|
1084
|
+
```
|
|
1085
|
+
|
|
1086
|
+
`--format github` writes `key=value` lines for `$GITHUB_OUTPUT`, `--format env` the same keys in
|
|
1087
|
+
upper case behind `AFFECTED_` for a GitLab `dotenv` report, `--json` the selection as data
|
|
1088
|
+
(`{ scope, reason, contexts, specs, leftOut }`). `contexts` is what the test runner reads as
|
|
1089
|
+
`DDD_TS_KIT_AFFECTED`, `args` what a partial run adds to it, `lint` what it adds to ESLint; the
|
|
1090
|
+
generated pipelines wire all three.
|
|
1091
|
+
|
|
724
1092
|
## check
|
|
725
1093
|
|
|
726
1094
|
```
|
|
@@ -731,7 +1099,58 @@ ddd-ts-kit check --commit-size [--staged] [--strict]
|
|
|
731
1099
|
```
|
|
732
1100
|
|
|
733
1101
|
Each violation has a rule id, a severity, a file, a line, a message and a `fix`: one sentence
|
|
734
|
-
saying what to do instead. Exit code 1 when there is at least one error.
|
|
1102
|
+
saying what to do instead. Exit code 1 when there is at least one error. Suppose that the
|
|
1103
|
+
`Reminder` aggregate of the example application read the system clock in its domain: an `isDue()`
|
|
1104
|
+
at line 63 that calls `Date.now()`, where the real one receives the instant as `isDue(asOf: Date)`.
|
|
1105
|
+
`check` would report:
|
|
1106
|
+
|
|
1107
|
+
```
|
|
1108
|
+
$ npx ddd-ts-kit check src/contexts/notification/domain
|
|
1109
|
+
src/contexts/notification/domain/aggregates/reminder.aggregate.ts:64 error purity/no-system-clock
|
|
1110
|
+
Date.now() is used in the domain layer.
|
|
1111
|
+
fix: Receive the instant as a parameter in the domain, and take it from the Clock port (`this.clock.now()`) in a handler, so that a test fixes time with FixedClock.
|
|
1112
|
+
|
|
1113
|
+
1 error, 0 warnings, 0 suppressions in 772 files.
|
|
1114
|
+
```
|
|
1115
|
+
|
|
1116
|
+
The `fix` is the change to make. Instead of that hypothetical method:
|
|
1117
|
+
|
|
1118
|
+
```ts
|
|
1119
|
+
isDue(): boolean {
|
|
1120
|
+
return this.state.status === ReminderStatus.Planned && this.state.dueAt.getTime() <= Date.now();
|
|
1121
|
+
}
|
|
1122
|
+
```
|
|
1123
|
+
|
|
1124
|
+
write:
|
|
1125
|
+
|
|
1126
|
+
<!-- checked sample -->
|
|
1127
|
+
```ts
|
|
1128
|
+
import { Clock, FixedClock } from '@ddd-ts-kit/kernel';
|
|
1129
|
+
|
|
1130
|
+
// domain: the instant is a parameter, as in the Reminder aggregate of the example
|
|
1131
|
+
export class Reminder {
|
|
1132
|
+
constructor(private readonly dueAt: Date) {}
|
|
1133
|
+
|
|
1134
|
+
isDue(asOf: Date): boolean {
|
|
1135
|
+
return this.dueAt.getTime() <= asOf.getTime();
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
// application: the handler takes the instant from the Clock port
|
|
1140
|
+
export class SendDueRemindersHandler {
|
|
1141
|
+
constructor(private readonly clock: Clock) {}
|
|
1142
|
+
|
|
1143
|
+
isDueNow(reminder: Reminder): boolean {
|
|
1144
|
+
return reminder.isDue(this.clock.now());
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
// a test fixes the time
|
|
1149
|
+
const handler = new SendDueRemindersHandler(new FixedClock(new Date('2026-03-02T09:00:00Z')));
|
|
1150
|
+
const due = handler.isDueNow(new Reminder(new Date('2026-03-01T09:00:00Z'))); // true
|
|
1151
|
+
```
|
|
1152
|
+
|
|
1153
|
+
`ddd-ts-kit explain <rule-id>` prints the same kind of bad and good example for every rule.
|
|
735
1154
|
|
|
736
1155
|
- `paths` and `--changed-since` limit what is reported, not what is analysed.
|
|
737
1156
|
`--changed-since` runs `git diff --name-only --relative <ref>...` in the project.
|
|
@@ -843,6 +1262,7 @@ saying what to do instead. Exit code 1 when there is at least one error.
|
|
|
843
1262
|
| `schema/presentation-only` | under `"stack.schemas": "shared"`: in the shared kernel outside `presentation/`, an import of a validation library; in `domain/`, `application/`, `infrastructure/`, `published-language/` and the shared kernel outside `presentation/`, an import of a file of `presentation/` of the shared kernel |
|
|
844
1263
|
| `schema/no-duplicate-shape` | under `"stack.schemas": "shared"`, in the presentation layer of one context, or in it and `presentation/` of the shared kernel: a DTO class or an object schema (`z.object`, `v.object`, `Type.Object`) declaring the shape of another one, with the place of the original (the shared kernel, then `presentation/http/shared/`); two contexts are not compared, and request parameters and shapes still carrying a `TODO(domain)` of the generator are not judged |
|
|
845
1264
|
| `errors/known-code` | under `"stack.refusals": "factories"`, in a `*.route.ts`: a code of the `errors` list that no refusal of the context of the route sets, that no class outside the contexts sets, and that is not a code of the kit |
|
|
1265
|
+
| `domain/value-object-class` | under `"stack.values": "classes"`: in `domain/value-objects/` of a context or a `*.vo.ts` of the shared kernel outside `presentation/`, no exported class or a second one, a class that extends neither `ValueObject` nor another value object of the project, a public constructor, a concrete class without a static method, an exported function, arrow constant or object literal (inline, listed or by default); in `domain/` and in the shared kernel outside `presentation/`, an exported function that answers a record of data: data entries, no spread, nothing a call of the model built |
|
|
846
1266
|
|
|
847
1267
|
More ids belong to `@ddd-ts-kit/eslint-plugin`, which reads the syntax tree where this
|
|
848
1268
|
checker reads text: `domain/aggregate-encapsulation`, `domain/value-object-immutability`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"architecture-rules.d.ts","sourceRoot":"","sources":["../../../src/check/catalogue/architecture-rules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"architecture-rules.d.ts","sourceRoot":"","sources":["../../../src/check/catalogue/architecture-rules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAiF7C,gHAAgH;AAChH,eAAO,MAAM,kBAAkB,EAAE,SAAS,IAAI,EAgF7C,CAAC"}
|
|
@@ -25,6 +25,7 @@ import { declarationsConstructorInjection } from '../rules/declarations-construc
|
|
|
25
25
|
import { declarationsRoleDeclared } from '../rules/declarations-role-declared.js';
|
|
26
26
|
import { domainEventsDispatchedOnSave } from '../rules/domain-events-dispatched-on-save.js';
|
|
27
27
|
import { domainEventsTransactionalBeforeCommit } from '../rules/domain-events-transactional-before-commit.js';
|
|
28
|
+
import { domainValueObjectClass } from '../rules/domain-value-object-class.js';
|
|
28
29
|
import { errorsCodeUnique } from '../rules/errors-code-unique.js';
|
|
29
30
|
import { eventSourcingPastTense } from '../rules/event-sourcing-past-tense.js';
|
|
30
31
|
import { eventSourcingProjectionNeverAppends } from '../rules/event-sourcing-projection-never-appends.js';
|
|
@@ -116,6 +117,7 @@ export const ARCHITECTURE_RULES = [
|
|
|
116
117
|
cleanBraces,
|
|
117
118
|
cleanOneExportPerFile,
|
|
118
119
|
cleanFileSize,
|
|
120
|
+
domainValueObjectClass,
|
|
119
121
|
eventsOutboxRequired,
|
|
120
122
|
eventsConsumerIdempotent,
|
|
121
123
|
domainEventsDispatchedOnSave,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"architecture-rules.js","sourceRoot":"","sources":["../../../src/check/catalogue/architecture-rules.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"architecture-rules.js","sourceRoot":"","sources":["../../../src/check/catalogue/architecture-rules.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-docs.d.ts","sourceRoot":"","sources":["../../../src/check/explain/domain-docs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAEpD,eAAO,MAAM,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA4BzD,CAAC"}
|