@domain-first/project-structure 2.7.0 → 2.7.2

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 CHANGED
@@ -1,54 +1,263 @@
1
1
  # @domain-first/project-structure
2
2
 
3
- Stop hand-rolling folders. Scaffold a clean, consistent **Domain-First** architecture — bounded contexts, aggregates, use cases, commands/queries, ports, REST endpoints, React modules — with one command, every time.
3
+ **Spend less time setting up files. Spend more time building your domain.**
4
+
5
+ An interactive CLI for growing TypeScript applications around business domains. Scaffold bounded contexts, aggregates, use cases, commands, queries, adapters, REST endpoints, and React modules with a consistent structure.
6
+
7
+ Choose what you need, answer a few prompts, and get the source files, optional test skeletons, and wiring to start implementing the feature.
4
8
 
5
9
  ```bash
10
+ npm install --save-dev @domain-first/project-structure
6
11
  npx df-ps
7
12
  ```
8
13
 
9
- ## Why
14
+ [Quick start](#quick-start) · [Project structure](#project-structure) · [Generators](#generators) · [Configuration](#configuration) · [Example workflow](#example-workflow)
10
15
 
11
- - **Zero boilerplate** — aggregates, repositories, wiring and tests generated in seconds, correctly named and cross-linked
12
- - **One consistent architecture** — every bounded context, layer and file lands in the same place, no matter who on the team runs it
13
- - **Grows with your stack** — adapts output to whichever `@domain-first` packages (`types`, `errors`, `handlers`, `wire`, `handlers-rest`) you use, or stays framework-free if you don't
14
- - **Interactive, not magic** — a guided CLI menu, not a rigid template you fight against
16
+ ## Why use it?
15
17
 
16
- ## Install
18
+ Adding an aggregate often means adding a repository contract, infrastructure implementations, tests, and dependency wiring too. Repeating that setup by hand takes time and lets conventions drift.
17
19
 
18
- ```bash
19
- npm i -D @domain-first/project-structure
20
- ```
20
+ `df-ps` turns those recurring decisions into a guided workflow:
21
+
22
+ - **Keep the team consistent.** Give each kind of component a predictable home, filename, and class name.
23
+ - **Grow feature by feature.** Add a bounded context or a single component as the application evolves.
24
+ - **Keep business code organized.** Group code by bounded context, with domain, application, infrastructure, and presentation layers inside it.
25
+ - **Prepare for multiple implementations.** Generate primary and test implementations of repositories, execution commands, queries, and application ports.
26
+ - **Use the integrations you need.** Enable templates for the `@domain-first` packages in your project and optional React scaffolding.
27
+
28
+ The generated files belong to your project. Fill in the business rules, schemas, dependencies, and integration code as you would with any other source file.
21
29
 
22
30
  ## Quick start
23
31
 
32
+ Run the CLI inside a project that has a `package.json`. For a new project, create one with `npm init -y` first. Use Node.js 22.13+ within the Node 22 release line, or Node.js 24+.
33
+
34
+ ### 1. Install and initialize
35
+
24
36
  ```bash
37
+ npm install --save-dev @domain-first/project-structure
25
38
  npx df-ps
26
39
  ```
27
40
 
28
- First run walks you through a short setup (source folder, packages, testing library) and writes a config file. Every run after that opens a menu to scaffold whatever you need next — a new aggregate, use case, endpoint, widget — into your existing structure.
41
+ You can also run it without adding it as a development dependency:
29
42
 
30
- ## What gets generated
43
+ ```bash
44
+ npm exec --package=@domain-first/project-structure -- df-ps
45
+ ```
46
+
47
+ On the first run, the CLI asks for:
48
+
49
+ | Setting | What to enter |
50
+ | ---------------------------------- | ----------------------------------------------------------------------------------------------------------- |
51
+ | Root folder | The source directory, such as `src` or `src/server`. Default: `src`. |
52
+ | `@domain-first` packages | The integrations to use in generated templates. |
53
+ | Default persistence implementation | A label such as `prisma`, used as the suggested implementation for execution commands and queries. |
54
+ | React | Whether to show the React module menu. |
55
+ | Unit tests | Whether to offer test generation, and which module to import test helpers from. Suggested module: `vitest`. |
31
56
 
32
- Pick a piece, get the whole slice — class, tests, infra implementation and wiring, all linked together:
57
+ Setup writes `domain-first.project-structure.config.json` beside your `package.json` and creates shared helpers for the selected `wire` and `errors` integrations. That first run then exits.
33
58
 
34
- - **Domain** — aggregates, repositories, services, namespaced errors
35
- - **Application** — use cases, commands & queries (execution or orchestration), ports
36
- - **Infrastructure** — adapter implementations per port/repository (e.g. `prisma`, `in-memory`)
37
- - **Presentation** — REST endpoints, React pages/widgets
59
+ The CLI uses the nearest `package.json` found by walking up from the current directory. In a monorepo, run it inside the package you want to scaffold.
38
60
 
39
- Everything lands with consistent naming and imports. For example:
61
+ ### 2. Create a bounded context
40
62
 
63
+ Run `npx df-ps` again, choose **Scaffold new bounded context**, and enter a name such as `sales`.
64
+
65
+ ### 3. Add a component
66
+
67
+ Run `npx df-ps`, choose **Bounded Context: sales**, select a layer, and choose a generator. For example:
68
+
69
+ ```text
70
+ Bounded Context: sales → Domain → New Aggregate
41
71
  ```
42
- bounded-contexts/
43
- order/
44
- domain/aggregates/order/
45
- application/use-cases/
46
- application/commands/
47
- infrastructure/repositories/
48
- presentation/rest/
49
- wiring/
72
+
73
+ Each invocation performs one scaffolding operation. Run the command again to add the next component. Folders are created as needed.
74
+
75
+ ## Project structure
76
+
77
+ A bounded context groups a business area and its implementation. Within it, each layer has a specific role:
78
+
79
+ | Location | Purpose |
80
+ | ----------------- | ----------------------------------------------------------------------------------------------------- |
81
+ | `domain/` | Aggregates, repository contracts, domain services, and business errors. |
82
+ | `application/` | Use cases, commands, queries, ports, and application services. |
83
+ | `infrastructure/` | Concrete repositories, adapters, execution handlers, and infrastructure services. |
84
+ | `presentation/` | REST endpoints and presentation services. |
85
+ | `wiring/` | Factories that construct components and select implementations, when `@domain-first/wire` is enabled. |
86
+
87
+ The **Shared Layer** menu provides cross-context errors, application ports, and application, infrastructure, and presentation services under `<rootFolder>/shared`.
88
+
89
+ A project using handler and wiring integrations can grow into this structure:
90
+
91
+ ```text
92
+ src/
93
+ ├── bounded-contexts/
94
+ │ └── sales/
95
+ │ ├── domain/
96
+ │ │ ├── aggregates/order/
97
+ │ │ ├── errors/
98
+ │ │ └── services/
99
+ │ ├── application/
100
+ │ │ ├── commands/ # execution/ and orchestration/
101
+ │ │ ├── queries/ # execution/ and orchestration/
102
+ │ │ ├── ports/
103
+ │ │ ├── services/
104
+ │ │ └── use-cases/
105
+ │ ├── infrastructure/
106
+ │ │ ├── application-adapters/
107
+ │ │ ├── commands/
108
+ │ │ ├── queries/
109
+ │ │ ├── repositories/
110
+ │ │ └── services/
111
+ │ ├── presentation/
112
+ │ │ ├── rest/
113
+ │ │ └── services/
114
+ │ └── wiring/
115
+ ├── shared/
116
+ └── presentation/
117
+ ├── rest/
118
+ └── react/
119
+ ├── pages/
120
+ └── widgets/
50
121
  ```
51
122
 
123
+ This is an example of the accumulated output; creating a context alone creates its directory and, when enabled, its error namespace.
124
+
125
+ ## Generators
126
+
127
+ Choose a bounded context, then a layer:
128
+
129
+ | Layer / menu option | Generated output |
130
+ | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
131
+ | Domain → New Aggregate | Aggregate root and barrel export; optional repository contract, primary and test implementations, tests, and repository wiring. |
132
+ | Domain → Errors | Errors in the context namespace, nested namespaces, and exports. Uses `@domain-first/errors`. |
133
+ | Domain → New Service | Domain service class, with optional tests and wiring. |
134
+ | Application → New Use Case | Use case class; handler schemas and `handle` when enabled; optional tests and wiring. |
135
+ | Application → New Command / New Query | Execution or orchestration templates, with optional tests and wiring. |
136
+ | Application → New Port | Abstract port and an adapter implementation, with an optional test adapter, tests, and wiring. |
137
+ | Application → New Service | Application service class, with optional tests and wiring. |
138
+ | Infrastructure → New Service | Infrastructure service class, with optional tests and wiring. |
139
+ | Presentation → New Service | Presentation service class, with optional tests and wiring. |
140
+ | Presentation → New Handlers-REST Endpoint | Endpoint class for a controller, method, and relative path; wiring and handler exports when enabled. Requires `@domain-first/handlers-rest` to appear in the menu. |
141
+
142
+ ### Commands and queries
143
+
144
+ Both generators offer two modes:
145
+
146
+ - **Execution (with infrastructure implementation):** creates an abstract application contract and a concrete implementation under `infrastructure/commands/<implementation>` or `infrastructure/queries/<implementation>`. You can add a separate implementation for tests.
147
+ - **Orchestrator (no infrastructure implementation):** creates an application class under `application/commands/orchestration` or `application/queries/orchestration`, ready for coordinating other operations.
148
+
149
+ With `@domain-first/handlers`, templates include input/output schemas and a typed handler contract or `defineHandler` implementation. Execution contracts go in the corresponding `execution/` directory; without that integration, they go directly in `application/commands` or `application/queries`.
150
+
151
+ ### Wiring and tests
152
+
153
+ With `@domain-first/wire`, generators create `wireClass` factories. Repositories, execution commands, queries, and ports use the shared `envBranchedWire` helper to choose an implementation based on `NODE_ENV`:
154
+
155
+ | Environment | Implementation |
156
+ | ------------- | ---------------------------------------------------------------------------- |
157
+ | `test` | The test implementation, if requested; otherwise the primary implementation. |
158
+ | `development` | The primary implementation. Also used when `NODE_ENV` is unset. |
159
+ | `production` | The primary implementation. |
160
+
161
+ Implementation names such as `prisma`, `api`, `in-memory`, and `mock` determine names and locations for generated classes. Add the database queries, API calls, or in-memory behavior yourself. Fill in the dependency lists in wiring factories as you implement constructors.
162
+
163
+ When `testingLibrary` is configured, supported generators ask whether to add `.spec.ts` files. These start with construction or wiring checks; extend them with tests for your business behavior. Repository, command, query, and port test templates reference wiring factories, so use the wiring integration or adapt those tests to your own construction setup.
164
+
165
+ ### REST endpoints
166
+
167
+ Select an existing controller or create one, choose `get`, `post`, `patch`, `delete`, or `put`, and enter a relative path. For example, context `sales`, controller `orders`, path `recent`, and method `get` produce:
168
+
169
+ ```text
170
+ src/bounded-contexts/sales/presentation/rest/orders/recent/GET.ts
171
+ ```
172
+
173
+ The route metadata uses `/sales/orders/recent`. Connect the endpoint to an application handler and provide its `EndpointGenerator`; the generated handler dependency starts as a `never` placeholder.
174
+
175
+ With wiring enabled, the generator also adds endpoint factories and exports collected as `restHandlers` in `<rootFolder>/presentation/rest/index.ts`. Connect these handlers to your HTTP server.
176
+
177
+ ### React modules
178
+
179
+ Enable `useReact` and choose **Scaffold React module** from the main menu. Modules are created under `<rootFolder>/presentation/react`.
180
+
181
+ | Generator | Output |
182
+ | ------------------------------ | ---------------------------------------------------------------------------------------------------------- |
183
+ | New page | `pages/<name>-page.tsx` with a typed props object and a component. |
184
+ | New widget → monolithic | `widgets/<name>/index.tsx` with props and rendering in one file. |
185
+ | New widget → with separated ui | Widget entry point, `types.ts`, `ui/index.tsx`, and `hooks/use-ui-model.ts`. |
186
+ | New widget → headless | Widget entry point, types, and a UI-model hook; the rendering component is supplied through the `UI` prop. |
187
+
188
+ ## Configuration
189
+
190
+ The project configuration is stored in `domain-first.project-structure.config.json`. Commit it to share generation settings with your team. For example:
191
+
192
+ ```json
193
+ {
194
+ "rootFolder": "src",
195
+ "domainFirstPackages": [
196
+ "@domain-first/types",
197
+ "@domain-first/errors",
198
+ "@domain-first/handlers",
199
+ "@domain-first/wire",
200
+ "@domain-first/handlers-rest"
201
+ ],
202
+ "defaultPersistenceLayerImplementation": "prisma",
203
+ "useReact": true,
204
+ "testingLibrary": "vitest"
205
+ }
206
+ ```
207
+
208
+ | Field | Effect |
209
+ | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
210
+ | `rootFolder` | Base path for generated source, relative to the project root. |
211
+ | `domainFirstPackages` | Explicit list of integrations used by templates. An empty array selects basic class templates where supported. |
212
+ | `defaultPersistenceLayerImplementation` | Suggested implementation label for execution commands and queries. Repository and port prompts have their own defaults: `prisma` and `api`. |
213
+ | `useReact` | Shows or hides the React module menu. |
214
+ | `testingLibrary` | Module imported by generated tests. Omit this field to disable test prompts. Templates expect helpers such as `describe`, `test`, `expect`, and `beforeEach`. |
215
+
216
+ ### Package integrations
217
+
218
+ | Package | What it enables |
219
+ | ----------------------------- | ------------------------------------------------------------------------------------------------------------------ |
220
+ | `@domain-first/types` | Aggregate roots extending `domainType()`. |
221
+ | `@domain-first/errors` | Context and shared error namespaces initialized during setup/context creation. Error generation uses this package. |
222
+ | `@domain-first/handlers` | Handler contracts, `defineHandler`, and input/output schema placeholders for use cases, commands, and queries. |
223
+ | `@domain-first/wire` | Construction factories and environment-based implementation selection. |
224
+ | `@domain-first/handlers-rest` | The REST endpoint generator using `EndpointGenerator`. |
225
+
226
+ Select the packages you intend to use and install them in your application separately. The CLI reads this list from configuration; it does not detect or install dependencies. React and your testing library also need their own project setup.
227
+
228
+ You can edit the configuration for future runs. Existing files are left as they are when you edit settings; enabling `wire` or `errors` later also requires adding the shared helpers that initial setup would create, plus context error namespaces where needed.
229
+
230
+ ## Example workflow
231
+
232
+ To start an order feature, enable `@domain-first/wire` during initialization and run `npx df-ps` for each step:
233
+
234
+ 1. Choose **Scaffold new bounded context** and name it `sales`.
235
+ 2. Choose **Bounded Context: sales → Domain → New Aggregate**, name it `order`, and add a repository with `prisma` and an `in-memory` test implementation.
236
+ 3. Choose **Application → New Use Case** within `sales` and name it `place-order`.
237
+ 4. Implement the aggregate's rules, repository operations, and use case; connect their dependencies in the generated wiring files.
238
+
239
+ The key files from these selections are:
240
+
241
+ ```text
242
+ src/bounded-contexts/sales/
243
+ ├── domain/aggregates/order/
244
+ │ ├── index.ts
245
+ │ ├── order.aggregate-root.ts
246
+ │ └── order-repository.ts
247
+ ├── application/use-cases/
248
+ │ └── place-order-use-case.ts
249
+ ├── infrastructure/repositories/
250
+ │ ├── prisma/prisma-order-repository.ts
251
+ │ └── in-memory/in-memory-order-repository.ts
252
+ └── wiring/
253
+ ├── repositories/wire-order-repository.ts
254
+ └── use-cases/wire-place-order-use-case.ts
255
+ ```
256
+
257
+ Use lowercase kebab-case for entered names, such as `place-order` and `payment-gateway`. Names are used in paths, and class and variable names are derived from their hyphen-separated words: `place-order` becomes `PlaceOrderUseCase` for a use case.
258
+
259
+ Generation writes files directly and can overwrite matching filenames without confirmation. Use a new name for a new component, and review the generated changes before continuing implementation.
260
+
52
261
  ## License
53
262
 
54
- MIT
263
+ [MIT](./LICENSE)
package/dist/index.cjs CHANGED
@@ -366,7 +366,7 @@ const scaffoldNewApplicationPort = async (boundedContextFolder)=>{
366
366
  });
367
367
  const wiringFolder = boundedContextFolder.subitem([
368
368
  'wiring',
369
- 'appication-ports'
369
+ 'application-ports'
370
370
  ]);
371
371
  const applicationPortsFolder = boundedContextFolder.subitem([
372
372
  'application',
@@ -378,9 +378,9 @@ export abstract class ${naming.ClassName} {
378
378
  }
379
379
  `.trim());
380
380
  if (scaffoldUnitTests) applicationPortsFolder.createFile(`${naming.fileName}.spec.ts`, `
381
- import { define, test, expect, beforeEach } from '${ConfigFile.Instance.data.testingLibrary}'
381
+ import { describe, test, expect, beforeEach } from '${ConfigFile.Instance.data.testingLibrary}'
382
382
  import { type ${naming.ClassName} } from './${naming.fileName}'
383
- import { wire${naming.ClassName} } from '../../wiring/ports/wire-${naming.fileName}'
383
+ import { wire${naming.ClassName} } from '../../wiring/application-ports/wire-${naming.fileName}'
384
384
 
385
385
  let ${naming.variableName}: ${naming.ClassName}
386
386
 
@@ -388,7 +388,7 @@ beforeEach(() => {
388
388
  ${naming.variableName} = wire${naming.ClassName}()
389
389
  })
390
390
 
391
- define('${naming.withSpaces}', () => {
391
+ describe('${naming.withSpaces}', () => {
392
392
  test('can be wired', () => {
393
393
  expect(${naming.variableName}).toBeDefined()
394
394
  })
@@ -603,7 +603,7 @@ export class ${formatNaming.ClassName}${naming.ClassName}Command extends ${namin
603
603
  })
604
604
  }
605
605
  `.trim() : `
606
- import { ${naming.ClassName}Command } from '../../../domain/commands/${naming.fileName}-command'
606
+ import { ${naming.ClassName}Command } from '../../../application/commands/execution/${naming.fileName}-command'
607
607
 
608
608
  export class ${formatNaming.ClassName}${naming.ClassName}Command extends ${naming.ClassName}Command {
609
609
  constructor() {
@@ -624,7 +624,9 @@ export abstract class ${naming.ClassName}Command {
624
624
  abstract handle: Handler<typeof ${naming.ClassName}Command.inputSchema, typeof ${naming.ClassName}Command.outputSchema>
625
625
  }
626
626
  `.trim());
627
- else commandsFolder.createFile(`${naming.fileName}-command.ts`, `
627
+ else commandsFolder.subitem([
628
+ 'execution'
629
+ ]).createFile(`${naming.fileName}-command.ts`, `
628
630
  export abstract class ${naming.ClassName}Command {
629
631
 
630
632
  }`.trim());
@@ -1057,7 +1059,7 @@ export class ${formatNaming.ClassName}${naming.ClassName}Query extends ${naming.
1057
1059
  })
1058
1060
  }
1059
1061
  `.trim() : `
1060
- import { ${naming.ClassName}Query } from '../../../domain/queries/${naming.fileName}-query'
1062
+ import { ${naming.ClassName}Query } from '../../../application/queries/execution/${naming.fileName}-query'
1061
1063
 
1062
1064
  export class ${formatNaming.ClassName}${naming.ClassName}Query extends ${naming.ClassName}Query {
1063
1065
  constructor() {
@@ -1078,7 +1080,9 @@ export abstract class ${naming.ClassName}Query {
1078
1080
  abstract handle: Handler<typeof ${naming.ClassName}Query.inputSchema, typeof ${naming.ClassName}Query.outputSchema>
1079
1081
  }
1080
1082
  `.trim());
1081
- else queriesFolder.createFile(`${naming.fileName}-query.ts`, `
1083
+ else queriesFolder.subitem([
1084
+ 'execution'
1085
+ ]).createFile(`${naming.fileName}-query.ts`, `
1082
1086
  export abstract class ${naming.ClassName}Query {
1083
1087
 
1084
1088
  }`.trim());
package/dist/index.js CHANGED
@@ -336,7 +336,7 @@ const scaffoldNewApplicationPort = async (boundedContextFolder)=>{
336
336
  });
337
337
  const wiringFolder = boundedContextFolder.subitem([
338
338
  'wiring',
339
- 'appication-ports'
339
+ 'application-ports'
340
340
  ]);
341
341
  const applicationPortsFolder = boundedContextFolder.subitem([
342
342
  'application',
@@ -348,9 +348,9 @@ export abstract class ${naming.ClassName} {
348
348
  }
349
349
  `.trim());
350
350
  if (scaffoldUnitTests) applicationPortsFolder.createFile(`${naming.fileName}.spec.ts`, `
351
- import { define, test, expect, beforeEach } from '${ConfigFile.Instance.data.testingLibrary}'
351
+ import { describe, test, expect, beforeEach } from '${ConfigFile.Instance.data.testingLibrary}'
352
352
  import { type ${naming.ClassName} } from './${naming.fileName}'
353
- import { wire${naming.ClassName} } from '../../wiring/ports/wire-${naming.fileName}'
353
+ import { wire${naming.ClassName} } from '../../wiring/application-ports/wire-${naming.fileName}'
354
354
 
355
355
  let ${naming.variableName}: ${naming.ClassName}
356
356
 
@@ -358,7 +358,7 @@ beforeEach(() => {
358
358
  ${naming.variableName} = wire${naming.ClassName}()
359
359
  })
360
360
 
361
- define('${naming.withSpaces}', () => {
361
+ describe('${naming.withSpaces}', () => {
362
362
  test('can be wired', () => {
363
363
  expect(${naming.variableName}).toBeDefined()
364
364
  })
@@ -573,7 +573,7 @@ export class ${formatNaming.ClassName}${naming.ClassName}Command extends ${namin
573
573
  })
574
574
  }
575
575
  `.trim() : `
576
- import { ${naming.ClassName}Command } from '../../../domain/commands/${naming.fileName}-command'
576
+ import { ${naming.ClassName}Command } from '../../../application/commands/execution/${naming.fileName}-command'
577
577
 
578
578
  export class ${formatNaming.ClassName}${naming.ClassName}Command extends ${naming.ClassName}Command {
579
579
  constructor() {
@@ -594,7 +594,9 @@ export abstract class ${naming.ClassName}Command {
594
594
  abstract handle: Handler<typeof ${naming.ClassName}Command.inputSchema, typeof ${naming.ClassName}Command.outputSchema>
595
595
  }
596
596
  `.trim());
597
- else commandsFolder.createFile(`${naming.fileName}-command.ts`, `
597
+ else commandsFolder.subitem([
598
+ 'execution'
599
+ ]).createFile(`${naming.fileName}-command.ts`, `
598
600
  export abstract class ${naming.ClassName}Command {
599
601
 
600
602
  }`.trim());
@@ -1027,7 +1029,7 @@ export class ${formatNaming.ClassName}${naming.ClassName}Query extends ${naming.
1027
1029
  })
1028
1030
  }
1029
1031
  `.trim() : `
1030
- import { ${naming.ClassName}Query } from '../../../domain/queries/${naming.fileName}-query'
1032
+ import { ${naming.ClassName}Query } from '../../../application/queries/execution/${naming.fileName}-query'
1031
1033
 
1032
1034
  export class ${formatNaming.ClassName}${naming.ClassName}Query extends ${naming.ClassName}Query {
1033
1035
  constructor() {
@@ -1048,7 +1050,9 @@ export abstract class ${naming.ClassName}Query {
1048
1050
  abstract handle: Handler<typeof ${naming.ClassName}Query.inputSchema, typeof ${naming.ClassName}Query.outputSchema>
1049
1051
  }
1050
1052
  `.trim());
1051
- else queriesFolder.createFile(`${naming.fileName}-query.ts`, `
1053
+ else queriesFolder.subitem([
1054
+ 'execution'
1055
+ ]).createFile(`${naming.fileName}-query.ts`, `
1052
1056
  export abstract class ${naming.ClassName}Query {
1053
1057
 
1054
1058
  }`.trim());
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@domain-first/project-structure",
3
3
  "description": "Build and manage your Domain-First project structure with CLI",
4
- "version": "2.7.0",
4
+ "version": "2.7.2",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",