@pukujan/create-modular-monolith 2.0.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 +43 -0
- package/bin/create-modular-monolith.js +132 -0
- package/package.json +39 -0
- package/template/README.md +73 -0
- package/template/backend/package-lock.json +882 -0
- package/template/backend/package.json +20 -0
- package/template/backend/scripts/check-module-boundaries.mjs +69 -0
- package/template/backend/scripts/check-module-layers.mjs +152 -0
- package/template/backend/src/core/module-loader.js +35 -0
- package/template/backend/src/core/server.js +24 -0
- package/template/backend/src/modules/.gitkeep +0 -0
- package/template/backend/src/modules/_reference/README.md +11 -0
- package/template/backend/src/modules/_reference/adapters/README.md +3 -0
- package/template/backend/src/modules/_reference/config/index.js +4 -0
- package/template/backend/src/modules/_reference/domain/README.md +3 -0
- package/template/backend/src/modules/_reference/evals/README.md +6 -0
- package/template/backend/src/modules/_reference/evals/datasets/example.cases.json +12 -0
- package/template/backend/src/modules/_reference/evals/runners/example.eval.mjs +25 -0
- package/template/backend/src/modules/_reference/events/index.js +4 -0
- package/template/backend/src/modules/_reference/index.js +9 -0
- package/template/backend/src/modules/_reference/prompts/manifest.json +14 -0
- package/template/backend/src/modules/_reference/prompts/templates/example.prompt.js +7 -0
- package/template/backend/src/modules/_reference/repositories/.gitkeep +0 -0
- package/template/backend/src/modules/_reference/routes/health.routes.js +10 -0
- package/template/backend/src/modules/_reference/routes/index.js +8 -0
- package/template/backend/src/modules/_reference/schemas/health.schema.js +8 -0
- package/template/backend/src/modules/_reference/services/health.service.js +7 -0
- package/template/backend/src/modules/_reference/tests/integration/health.routes.test.js +20 -0
- package/template/backend/src/modules/_reference/tests/unit/health.service.test.js +9 -0
- package/template/backend/src/modules/_reference/utils/index.js +3 -0
- package/template/backend/src/shared/ai/prompt-registry.js +42 -0
- package/template/backend/src/shared/events/index.js +8 -0
- package/template/backend/src/shared/http/errors.js +10 -0
- package/template/backend/src/shared/testing/create-test-app.js +13 -0
- package/template/docs/DEVLOG_V2.md +369 -0
- package/template/docs/PUBLISHING.md +39 -0
- package/template/docs/README.md +13 -0
- package/template/docs/STARTER_PACK.md +98 -0
- package/template/docs/architecture/ARCHITECTURE_GUARDRAILS.md +74 -0
- package/template/docs/architecture/MODULE_INTERNAL_CONTRACT.md +164 -0
- package/template/frontend/index.html +12 -0
- package/template/frontend/package-lock.json +1724 -0
- package/template/frontend/package.json +21 -0
- package/template/frontend/src/core/App.jsx +35 -0
- package/template/frontend/src/core/moduleRegistry.jsx +39 -0
- package/template/frontend/src/index.css +53 -0
- package/template/frontend/src/main.jsx +10 -0
- package/template/frontend/src/modules/.gitkeep +0 -0
- package/template/frontend/src/modules/_reference/README.md +3 -0
- package/template/frontend/src/modules/_reference/components/ModuleHealthCard.jsx +14 -0
- package/template/frontend/src/modules/_reference/hooks/use-module-health.js +27 -0
- package/template/frontend/src/modules/_reference/index.jsx +7 -0
- package/template/frontend/src/modules/_reference/pages/_referencePage.jsx +11 -0
- package/template/frontend/src/modules/_reference/prompts/README.md +3 -0
- package/template/frontend/src/modules/_reference/schemas/health.schema.js +3 -0
- package/template/frontend/src/modules/_reference/services/health-api.js +5 -0
- package/template/frontend/src/modules/_reference/tests/unit/health.schema.test.js +8 -0
- package/template/frontend/src/modules/_reference/utils/index.js +3 -0
- package/template/frontend/src/shared/api/client.js +10 -0
- package/template/frontend/vite.config.js +6 -0
- package/template/package.json +16 -0
- package/template/scripts/lib/module-scaffold.mjs +409 -0
- package/template/scripts/new-module.mjs +58 -0
- package/template/scripts/run-module-evals.mjs +43 -0
- package/template/scripts/sync-cli-template.mjs +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# @pukujan/create-modular-monolith
|
|
2
|
+
|
|
3
|
+
CLI for a **scalable modular monolith** starter: feature modules with strict boundaries, internal MVC layers, prompts, evals, tests, and automated architecture lint. Domain-agnostic — use it for any platform that should grow without rewrite.
|
|
4
|
+
|
|
5
|
+
## Create a project
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm create @pukujan/modular-monolith@2 my-platform
|
|
9
|
+
cd my-platform
|
|
10
|
+
cd backend && npm install && cd ../frontend && npm install
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Options:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm create @pukujan/modular-monolith@2 my-platform -- --install --git
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
| Flag | Effect |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| `--install` | Run `npm install` in backend and frontend |
|
|
22
|
+
| `--git` | `git init` and initial commit |
|
|
23
|
+
|
|
24
|
+
## What you get
|
|
25
|
+
|
|
26
|
+
- **Modular monolith** — auto-loaded backend modules + frontend route discovery
|
|
27
|
+
- **Internal contract** — routes → services → repositories / domain / adapters
|
|
28
|
+
- **AI-ready** — versioned `prompts/` and `evals/` per feature module
|
|
29
|
+
- **Lint** — cross-module boundaries + layer direction (`lint:architecture`)
|
|
30
|
+
|
|
31
|
+
## Publish (maintainers)
|
|
32
|
+
|
|
33
|
+
From repo root on branch `v2`:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm run sync:cli-template
|
|
37
|
+
cd packages/create-modular-monolith
|
|
38
|
+
npm publish --access public
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Source
|
|
42
|
+
|
|
43
|
+
[litigation-workflow-application](https://github.com/Pukujan/litigation-workflow-application) branch `v2` (originated for legal workflows; architecture is industry-agnostic).
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
cpSync,
|
|
4
|
+
existsSync,
|
|
5
|
+
mkdirSync,
|
|
6
|
+
readFileSync,
|
|
7
|
+
readdirSync,
|
|
8
|
+
writeFileSync
|
|
9
|
+
} from "fs";
|
|
10
|
+
import { join, resolve, basename } from "path";
|
|
11
|
+
import { fileURLToPath } from "url";
|
|
12
|
+
import { spawnSync } from "child_process";
|
|
13
|
+
|
|
14
|
+
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
|
15
|
+
const templateDir = join(__dirname, "..", "template");
|
|
16
|
+
|
|
17
|
+
const args = process.argv.slice(2).filter((a) => a !== "--");
|
|
18
|
+
const installDeps = args.includes("--install");
|
|
19
|
+
const initGit = args.includes("--git");
|
|
20
|
+
const positional = args.filter((a) => !a.startsWith("--"));
|
|
21
|
+
|
|
22
|
+
const targetArg = positional[0];
|
|
23
|
+
const cwd = process.cwd();
|
|
24
|
+
const targetDir = targetArg ? resolve(cwd, targetArg) : resolve(cwd, "my-platform");
|
|
25
|
+
const projectName = basename(targetDir);
|
|
26
|
+
|
|
27
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(projectName)) {
|
|
28
|
+
console.error(
|
|
29
|
+
"Project folder name must be kebab-case (example: my-platform).\n",
|
|
30
|
+
"Usage: npm create @pukujan/modular-monolith@2 my-platform"
|
|
31
|
+
);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!existsSync(templateDir)) {
|
|
36
|
+
console.error("Template missing. Reinstall @pukujan/create-modular-monolith.");
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (existsSync(targetDir)) {
|
|
41
|
+
try {
|
|
42
|
+
const entries = readdirSync(targetDir);
|
|
43
|
+
if (entries.length > 0) {
|
|
44
|
+
console.error(`Target directory is not empty: ${targetDir}`);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
} catch {
|
|
48
|
+
console.error(`Cannot use target: ${targetDir}`);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const displayName = toTitleCase(projectName);
|
|
54
|
+
|
|
55
|
+
console.log(`\nCreating modular monolith starter: ${projectName}`);
|
|
56
|
+
console.log(` → ${targetDir}\n`);
|
|
57
|
+
|
|
58
|
+
mkdirSync(targetDir, { recursive: true });
|
|
59
|
+
cpSync(templateDir, targetDir, { recursive: true });
|
|
60
|
+
|
|
61
|
+
replaceInTree(targetDir, [
|
|
62
|
+
["modular-monolith-starter-backend", `${projectName}-backend`],
|
|
63
|
+
["modular-monolith-starter-frontend", `${projectName}-frontend`],
|
|
64
|
+
["modular-monolith-starter", projectName],
|
|
65
|
+
["Modular Monolith Platform Starter", displayName]
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
if (installDeps) {
|
|
69
|
+
console.log("Installing dependencies…");
|
|
70
|
+
run("npm", ["install"], join(targetDir, "backend"));
|
|
71
|
+
run("npm", ["install"], join(targetDir, "frontend"));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (initGit) {
|
|
75
|
+
run("git", ["init"], targetDir);
|
|
76
|
+
run("git", ["add", "."], targetDir);
|
|
77
|
+
run("git", ["commit", "-m", "chore: scaffold modular monolith starter"], targetDir);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
console.log("\nDone.\n");
|
|
81
|
+
console.log("Next steps:");
|
|
82
|
+
console.log(` cd ${targetArg || projectName}`);
|
|
83
|
+
if (!installDeps) {
|
|
84
|
+
console.log(" cd backend && npm install");
|
|
85
|
+
console.log(" cd ../frontend && npm install");
|
|
86
|
+
}
|
|
87
|
+
console.log(" npm run dev:backend # terminal 1");
|
|
88
|
+
console.log(" npm run dev:frontend # terminal 2");
|
|
89
|
+
console.log(` npm run new:module -- my-feature --label "My Feature"`);
|
|
90
|
+
console.log(" npm run lint:architecture && npm test\n");
|
|
91
|
+
|
|
92
|
+
function replaceInTree(dir, pairs) {
|
|
93
|
+
for (const file of walkFiles(dir)) {
|
|
94
|
+
if (!/\.(json|md|js|jsx|mjs|html)$/.test(file)) continue;
|
|
95
|
+
let content = readFileSync(file, "utf8");
|
|
96
|
+
let changed = false;
|
|
97
|
+
for (const [from, to] of pairs) {
|
|
98
|
+
if (content.includes(from)) {
|
|
99
|
+
content = content.split(from).join(to);
|
|
100
|
+
changed = true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (changed) writeFileSync(file, content, "utf8");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function walkFiles(dir, acc = []) {
|
|
108
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
109
|
+
const full = join(dir, entry.name);
|
|
110
|
+
if (entry.isDirectory()) {
|
|
111
|
+
if (entry.name === "node_modules") continue;
|
|
112
|
+
walkFiles(full, acc);
|
|
113
|
+
} else {
|
|
114
|
+
acc.push(full);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return acc;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function toTitleCase(value) {
|
|
121
|
+
return value
|
|
122
|
+
.split("-")
|
|
123
|
+
.map((p) => p.charAt(0).toUpperCase() + p.slice(1))
|
|
124
|
+
.join(" ");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function run(cmd, cmdArgs, dir) {
|
|
128
|
+
const result = spawnSync(cmd, cmdArgs, { cwd: dir, stdio: "inherit" });
|
|
129
|
+
if (result.status !== 0) {
|
|
130
|
+
console.warn(`Warning: ${cmd} ${cmdArgs.join(" ")} exited with ${result.status}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pukujan/create-modular-monolith",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Scaffold a scalable modular monolith: strict module boundaries, MVC layers, prompts, evals, and architecture lint",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-modular-monolith": "bin/create-modular-monolith.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"template"
|
|
12
|
+
],
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=18"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"modular-monolith",
|
|
18
|
+
"monolith",
|
|
19
|
+
"architecture",
|
|
20
|
+
"scaffold",
|
|
21
|
+
"create-app",
|
|
22
|
+
"platform",
|
|
23
|
+
"microservices-ready",
|
|
24
|
+
"legal-tech"
|
|
25
|
+
],
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/Pukujan/litigation-workflow-application.git",
|
|
33
|
+
"directory": "packages/create-modular-monolith"
|
|
34
|
+
},
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/Pukujan/litigation-workflow-application/issues"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/Pukujan/litigation-workflow-application#readme"
|
|
39
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Modular Monolith Platform Starter (v2)
|
|
2
|
+
|
|
3
|
+
A **scalable modular monolith** boilerplate: strict feature-module boundaries, internal MVC layers, prompts/evals for AI workflows, and automated architecture lint. Built for platforms that should grow toward services **without** a rewrite.
|
|
4
|
+
|
|
5
|
+
Domain-agnostic — use it for legal tech, ops, marketplaces, or any multi-feature product. (This repo started as a litigation workflow; the architecture is not limited to that.)
|
|
6
|
+
|
|
7
|
+
> **v1 (minimal)** — branch [`main`](https://github.com/Pukujan/litigation-workflow-application/tree/main). **v2 (platform)** — this branch + npm CLI.
|
|
8
|
+
|
|
9
|
+
**Documentation:** [docs/README.md](./docs/README.md) · [DEVLOG v2](./docs/DEVLOG_V2.md) · [guardrails](./docs/architecture/ARCHITECTURE_GUARDRAILS.md) · [internal contract](./docs/architecture/MODULE_INTERNAL_CONTRACT.md)
|
|
10
|
+
|
|
11
|
+
**Repository:** [github.com/Pukujan/litigation-workflow-application](https://github.com/Pukujan/litigation-workflow-application)
|
|
12
|
+
|
|
13
|
+
## What is included
|
|
14
|
+
|
|
15
|
+
- Backend module auto-loader + frontend route discovery
|
|
16
|
+
- **Inter-module** guardrails (no cross-feature imports; event bus + HTTP)
|
|
17
|
+
- **Intra-module** contract (routes → services → repositories / domain / adapters)
|
|
18
|
+
- Prompts + evals colocated per feature module
|
|
19
|
+
- `lint:architecture`, `npm test`, `npm run test:evals`
|
|
20
|
+
- `_reference` example module (documentation only; not loaded at runtime)
|
|
21
|
+
|
|
22
|
+
## Create a new project (npm CLI)
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm create @pukujan/modular-monolith@2 my-platform
|
|
26
|
+
cd my-platform
|
|
27
|
+
cd backend && npm install && cd ../frontend && npm install
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Publish first: `@pukujan/create-modular-monolith` — see [docs/PUBLISHING.md](./docs/PUBLISHING.md).
|
|
31
|
+
|
|
32
|
+
Local (without publish):
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
node packages/create-modular-monolith/bin/create-modular-monolith.js ../my-platform
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Repo structure
|
|
39
|
+
|
|
40
|
+
```text
|
|
41
|
+
modular-monolith-starter/
|
|
42
|
+
├── backend/src/{core,shared,modules}/
|
|
43
|
+
├── frontend/src/{core,shared,modules}/
|
|
44
|
+
├── docs/architecture/
|
|
45
|
+
├── scripts/new-module.mjs
|
|
46
|
+
└── packages/create-modular-monolith/ # npm CLI
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Run locally (this repo)
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
cd backend && npm install && npm run dev
|
|
53
|
+
# other terminal:
|
|
54
|
+
cd frontend && npm install && npm run dev
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Create your first module
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm run new:module -- billing --label "Billing"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Restart backend; refresh frontend.
|
|
64
|
+
|
|
65
|
+
## Architecture checks
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm run lint:architecture
|
|
69
|
+
npm test
|
|
70
|
+
npm run test:evals
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
See `backend/src/modules/_reference/` for the full internal layout.
|