@domain-first/project-structure 2.2.0 → 2.3.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.md +47 -4
- package/dist/index.cjs +23 -1
- package/dist/index.js +23 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,11 +1,54 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @domain-first/project-structure
|
|
2
2
|
|
|
3
|
-
|
|
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.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
```bash
|
|
6
|
+
npx df-ps
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Why
|
|
10
|
+
|
|
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
|
|
15
|
+
|
|
16
|
+
## Install
|
|
6
17
|
|
|
7
18
|
```bash
|
|
8
|
-
npm i @domain-first/project-structure
|
|
19
|
+
npm i -D @domain-first/project-structure
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick start
|
|
9
23
|
|
|
24
|
+
```bash
|
|
10
25
|
npx df-ps
|
|
11
26
|
```
|
|
27
|
+
|
|
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.
|
|
29
|
+
|
|
30
|
+
## What gets generated
|
|
31
|
+
|
|
32
|
+
Pick a piece, get the whole slice — class, tests, infra implementation and wiring, all linked together:
|
|
33
|
+
|
|
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
|
|
38
|
+
|
|
39
|
+
Everything lands with consistent naming and imports. For example:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
bounded-contexts/
|
|
43
|
+
order/
|
|
44
|
+
domain/aggregates/order/
|
|
45
|
+
application/use-cases/
|
|
46
|
+
application/commands/
|
|
47
|
+
infrastructure/repositories/
|
|
48
|
+
presentation/rest/
|
|
49
|
+
wiring/
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## License
|
|
53
|
+
|
|
54
|
+
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -360,6 +360,10 @@ const scaffoldNewApplicationPort = async (boundedContextFolder)=>{
|
|
|
360
360
|
message: 'Name: '
|
|
361
361
|
});
|
|
362
362
|
const naming = new UnknownFormatNaming(name);
|
|
363
|
+
let scaffoldUnitTests;
|
|
364
|
+
if (ConfigFile.Instance.data.testingLibrary) scaffoldUnitTests = await (0, prompts_namespaceObject.confirm)({
|
|
365
|
+
message: 'Scaffold unit-tests'
|
|
366
|
+
});
|
|
363
367
|
const wiringFolder = boundedContextFolder.subitem([
|
|
364
368
|
'wiring',
|
|
365
369
|
'ports'
|
|
@@ -373,8 +377,26 @@ export abstract class ${naming.ClassName} {
|
|
|
373
377
|
|
|
374
378
|
}
|
|
375
379
|
`.trim());
|
|
380
|
+
if (scaffoldUnitTests) applicationPortsFolder.createFile(`${naming.fileName}.spec.ts`, `
|
|
381
|
+
import { define, test, expect, beforeEach } from '${ConfigFile.Instance.data.testingLibrary}'
|
|
382
|
+
import { type ${naming.ClassName} } from './${naming.fileName}'
|
|
383
|
+
import { wire${naming.ClassName} } from '../../wiring/ports/wire-${naming.fileName}'
|
|
384
|
+
|
|
385
|
+
let ${naming.variableName}: ${naming.ClassName}
|
|
386
|
+
|
|
387
|
+
beforeEach(() => {
|
|
388
|
+
${naming.variableName} = wire${naming.ClassName}()
|
|
389
|
+
})
|
|
390
|
+
|
|
391
|
+
define('${naming.withSpaces}', () => {
|
|
392
|
+
test('can be wired', () => {
|
|
393
|
+
expect(${naming.variableName}).toBeDefined()
|
|
394
|
+
})
|
|
395
|
+
})
|
|
396
|
+
`.trim());
|
|
376
397
|
const implementationType = await (0, prompts_namespaceObject.input)({
|
|
377
|
-
message: 'Adapter implementation type:'
|
|
398
|
+
message: 'Adapter implementation type:',
|
|
399
|
+
default: 'api'
|
|
378
400
|
});
|
|
379
401
|
const addTestImplementation = await (0, prompts_namespaceObject.confirm)({
|
|
380
402
|
message: 'With test implementation'
|
package/dist/index.js
CHANGED
|
@@ -330,6 +330,10 @@ const scaffoldNewApplicationPort = async (boundedContextFolder)=>{
|
|
|
330
330
|
message: 'Name: '
|
|
331
331
|
});
|
|
332
332
|
const naming = new UnknownFormatNaming(name);
|
|
333
|
+
let scaffoldUnitTests;
|
|
334
|
+
if (ConfigFile.Instance.data.testingLibrary) scaffoldUnitTests = await prompts_confirm({
|
|
335
|
+
message: 'Scaffold unit-tests'
|
|
336
|
+
});
|
|
333
337
|
const wiringFolder = boundedContextFolder.subitem([
|
|
334
338
|
'wiring',
|
|
335
339
|
'ports'
|
|
@@ -343,8 +347,26 @@ export abstract class ${naming.ClassName} {
|
|
|
343
347
|
|
|
344
348
|
}
|
|
345
349
|
`.trim());
|
|
350
|
+
if (scaffoldUnitTests) applicationPortsFolder.createFile(`${naming.fileName}.spec.ts`, `
|
|
351
|
+
import { define, test, expect, beforeEach } from '${ConfigFile.Instance.data.testingLibrary}'
|
|
352
|
+
import { type ${naming.ClassName} } from './${naming.fileName}'
|
|
353
|
+
import { wire${naming.ClassName} } from '../../wiring/ports/wire-${naming.fileName}'
|
|
354
|
+
|
|
355
|
+
let ${naming.variableName}: ${naming.ClassName}
|
|
356
|
+
|
|
357
|
+
beforeEach(() => {
|
|
358
|
+
${naming.variableName} = wire${naming.ClassName}()
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
define('${naming.withSpaces}', () => {
|
|
362
|
+
test('can be wired', () => {
|
|
363
|
+
expect(${naming.variableName}).toBeDefined()
|
|
364
|
+
})
|
|
365
|
+
})
|
|
366
|
+
`.trim());
|
|
346
367
|
const implementationType = await input({
|
|
347
|
-
message: 'Adapter implementation type:'
|
|
368
|
+
message: 'Adapter implementation type:',
|
|
369
|
+
default: 'api'
|
|
348
370
|
});
|
|
349
371
|
const addTestImplementation = await prompts_confirm({
|
|
350
372
|
message: 'With test implementation'
|