@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
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
# Development log: v2 platform starter
|
|
2
|
+
|
|
3
|
+
**Branch:** `v2`
|
|
4
|
+
**Base:** `main` (minimal modular monolith starter)
|
|
5
|
+
**Commit:** `f035aec` — `feat(v2): module internal contract, tests, and npm create CLI`
|
|
6
|
+
**Date:** May 2026
|
|
7
|
+
**Repository:** [Pukujan/litigation-workflow-application](https://github.com/Pukujan/litigation-workflow-application)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Executive summary
|
|
12
|
+
|
|
13
|
+
v2 turns the original minimal modular shell into a **platform-grade boilerplate** aimed at scalable products (including legal-services platforms, but not limited to them). The centerpiece is a documented and partially automated **module internal contract**: MVC-style layers inside each feature, plus first-class **prompts**, **evals**, **tests**, and **architecture lint**.
|
|
14
|
+
|
|
15
|
+
`main` remains the lightweight v1 line for simple forks and teaching. `v2` is the recommended line for production platform work and for publishing `@pukujan/create-modular-monolith`.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Changes from `main`
|
|
20
|
+
|
|
21
|
+
### Summary statistics
|
|
22
|
+
|
|
23
|
+
| Metric | Value |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| Commits on `v2` not in `main` | 1 |
|
|
26
|
+
| Files changed | 112 |
|
|
27
|
+
| Lines added (approx.) | ~5,900 |
|
|
28
|
+
| Lines removed (approx.) | ~120 |
|
|
29
|
+
|
|
30
|
+
### What `main` had (v1)
|
|
31
|
+
|
|
32
|
+
- Express backend with `module-loader.js` (auto-load `register()` from `modules/*/index.js`)
|
|
33
|
+
- Vite + React frontend with `moduleRegistry.jsx` (auto-discover `modules/*/index.jsx`)
|
|
34
|
+
- `scripts/new-module.mjs` — created a **single** `index.js` + `index.jsx` per module
|
|
35
|
+
- `check-module-boundaries.mjs` — backend-only, string scan for cross-module paths
|
|
36
|
+
- Docs: starter pack + architecture guardrails (inter-module rules only)
|
|
37
|
+
- **No** prescribed structure inside a module, no tests, no AI/prompt tooling, no npm CLI
|
|
38
|
+
|
|
39
|
+
### What `v2` adds
|
|
40
|
+
|
|
41
|
+
#### 1. Module internal contract (documentation)
|
|
42
|
+
|
|
43
|
+
| File | Purpose |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| `docs/architecture/MODULE_INTERNAL_CONTRACT.md` | Full per-module layout, layer rules, prompts/evals conventions |
|
|
46
|
+
| `docs/architecture/ARCHITECTURE_GUARDRAILS.md` | Updated to reference internal contract + new lint |
|
|
47
|
+
| `docs/STARTER_PACK.md` | Architecture checks, `_reference` module |
|
|
48
|
+
| `docs/PUBLISHING.md` | How to publish the npm CLI |
|
|
49
|
+
| `docs/DEVLOG_V2.md` | This document |
|
|
50
|
+
|
|
51
|
+
#### 2. Backend feature module layout
|
|
52
|
+
|
|
53
|
+
Each module is scaffolded with:
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
config/ routes/ services/ repositories/ domain/ adapters/
|
|
57
|
+
events/ schemas/ prompts/ evals/ utils/ tests/{unit,integration}/
|
|
58
|
+
index.js ← composition root only
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Shared platform utilities:**
|
|
62
|
+
|
|
63
|
+
| File | Role |
|
|
64
|
+
| --- | --- |
|
|
65
|
+
| `backend/src/shared/ai/prompt-registry.js` | Load versioned prompts from `prompts/manifest.json`, render `{{variables}}` |
|
|
66
|
+
| `backend/src/shared/testing/create-test-app.js` | Minimal Express app for route integration tests |
|
|
67
|
+
|
|
68
|
+
#### 3. Frontend feature module layout
|
|
69
|
+
|
|
70
|
+
```text
|
|
71
|
+
pages/ components/ hooks/ services/ schemas/ utils/ prompts/ tests/
|
|
72
|
+
index.jsx ← route contract only (route, label, Component)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
#### 4. Reference implementation
|
|
76
|
+
|
|
77
|
+
| Path | Notes |
|
|
78
|
+
| --- | --- |
|
|
79
|
+
| `backend/src/modules/_reference/` | Full backend layout + health example + evals + tests |
|
|
80
|
+
| `frontend/src/modules/_reference/` | Full frontend layout + health card hook chain |
|
|
81
|
+
|
|
82
|
+
Folders prefixed with `_` are **skipped by loaders** (not mounted in dev/prod). They serve as living documentation and copy-paste reference.
|
|
83
|
+
|
|
84
|
+
#### 5. Enforcement tooling
|
|
85
|
+
|
|
86
|
+
| Command | Script | Scope |
|
|
87
|
+
| --- | --- | --- |
|
|
88
|
+
| `npm run lint:boundaries` | `check-module-boundaries.mjs` | Backend **and** frontend — no `/modules/<other>/` imports |
|
|
89
|
+
| `npm run lint:layers` | `check-module-layers.mjs` | Backend only — layer import direction inside each module |
|
|
90
|
+
| `npm run lint:architecture` | both | CI-friendly gate |
|
|
91
|
+
| `npm test` | `node:test` | Module unit + integration tests |
|
|
92
|
+
| `npm run test:evals` | `run-module-evals.mjs` | `evals/runners/*.eval.mjs` per module |
|
|
93
|
+
|
|
94
|
+
#### 6. Scaffolder rewrite
|
|
95
|
+
|
|
96
|
+
`scripts/new-module.mjs` now delegates to `scripts/lib/module-scaffold.mjs` and generates the **entire** tree (backend + frontend), not two flat entry files.
|
|
97
|
+
|
|
98
|
+
#### 7. npm create CLI
|
|
99
|
+
|
|
100
|
+
| Piece | Location |
|
|
101
|
+
| --- | --- |
|
|
102
|
+
| Package | `@pukujan/create-modular-monolith@2.0.0` |
|
|
103
|
+
| CLI | `packages/create-modular-monolith/bin/create-modular-monolith.js` |
|
|
104
|
+
| Bundled template | `packages/create-modular-monolith/template/` |
|
|
105
|
+
| Sync script | `npm run sync:cli-template` |
|
|
106
|
+
|
|
107
|
+
**Usage (after `npm publish`):**
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
npm create @pukujan/modular-monolith@2 my-platform
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Flags: `--install`, `--git`.
|
|
114
|
+
|
|
115
|
+
#### 8. Root / package metadata
|
|
116
|
+
|
|
117
|
+
- Root `version` → `2.0.0`
|
|
118
|
+
- README distinguishes v1 (`main`) vs v2 (`v2`)
|
|
119
|
+
- `frontend/src/core/moduleRegistry.jsx` — ignores `_`-prefixed modules (aligned with backend loader)
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## The contract (two levels)
|
|
124
|
+
|
|
125
|
+
### Level 1 — Inter-module (unchanged in spirit, stronger in tooling)
|
|
126
|
+
|
|
127
|
+
Defined in [ARCHITECTURE_GUARDRAILS.md](./architecture/ARCHITECTURE_GUARDRAILS.md):
|
|
128
|
+
|
|
129
|
+
- Backend: `register(app, context)` from `modules/<name>/index.js`
|
|
130
|
+
- Frontend: default export `{ route, label, Component }` from `modules/<name>/index.jsx`
|
|
131
|
+
- **No** imports across `modules/<other-name>/`
|
|
132
|
+
- Communication via `shared/`, HTTP (`/api/<module-name>`), and in-process `eventBus`
|
|
133
|
+
|
|
134
|
+
### Level 2 — Intra-module (new in v2)
|
|
135
|
+
|
|
136
|
+
Defined in [MODULE_INTERNAL_CONTRACT.md](./architecture/MODULE_INTERNAL_CONTRACT.md):
|
|
137
|
+
|
|
138
|
+
- **Routes** talk HTTP only; delegate to **services**
|
|
139
|
+
- **Services** own use cases, orchestration, and AI flows
|
|
140
|
+
- **Repositories** and **adapters** isolate persistence and third parties
|
|
141
|
+
- **Domain** stays pure (rules without I/O)
|
|
142
|
+
- **Prompts** are versioned, declarative templates
|
|
143
|
+
- **Evals** regression-test prompts/flows through services, not live HTTP
|
|
144
|
+
- **index** files are composition roots only — no business logic
|
|
145
|
+
|
|
146
|
+
This is the contract that supports scaling without rewriting the core app on every new feature.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Problems this architecture solves
|
|
151
|
+
|
|
152
|
+
### 1. Monolith spaghetti as teams grow
|
|
153
|
+
|
|
154
|
+
**Problem:** Everyone imports everyone; features become impossible to extract or test in isolation.
|
|
155
|
+
|
|
156
|
+
**How v2 helps:** Hard module boundaries (linted) + directed layers inside each module. New work has a obvious place to land (`services/` vs `routes/` vs `adapters/`).
|
|
157
|
+
|
|
158
|
+
### 2. “We'll split into microservices later” with no seams
|
|
159
|
+
|
|
160
|
+
**Problem:** Deferred distribution without boundaries means a big-bang rewrite.
|
|
161
|
+
|
|
162
|
+
**How v2 helps:** Each feature module already owns its API prefix, UI route, events, and internal layers. A module that respects the contract is a **candidate service** with a known surface area (`/api/<name>`, event contracts, owned data).
|
|
163
|
+
|
|
164
|
+
### 3. AI / LLM features bolted on ad hoc
|
|
165
|
+
|
|
166
|
+
**Problem:** Prompts in random strings, no regression, no audit trail for legal workflows.
|
|
167
|
+
|
|
168
|
+
**How v2 helps:** `prompts/` + `manifest.json` + version bumps; `evals/` with datasets and runners in CI. Colocated with the feature that owns the workflow (intake, discovery, etc.).
|
|
169
|
+
|
|
170
|
+
### 4. Refactor churn on every new integration
|
|
171
|
+
|
|
172
|
+
**Problem:** Court APIs, e-file, storage, and auth get mixed into route handlers.
|
|
173
|
+
|
|
174
|
+
**How v2 helps:** `adapters/` and `repositories/` absorb external shape; services stay stable; routes stay thin.
|
|
175
|
+
|
|
176
|
+
### 5. Onboarding friction
|
|
177
|
+
|
|
178
|
+
**Problem:** “Where does this code go?”
|
|
179
|
+
|
|
180
|
+
**How v2 helps:** Scaffolder + `_reference` module + lint errors that point at the contract doc.
|
|
181
|
+
|
|
182
|
+
### 6. Starter vs product confusion
|
|
183
|
+
|
|
184
|
+
**Problem:** One repo trying to be both minimal demo and full platform.
|
|
185
|
+
|
|
186
|
+
**How v2 helps:** **`main` = v1 minimal**, **`v2` = platform line**, npm CLI packages v2 for new repos without cloning GitHub every time.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Growth potential: path to a legal-services platform OS
|
|
191
|
+
|
|
192
|
+
### Near term (modules in one deployable)
|
|
193
|
+
|
|
194
|
+
- Add business modules: `matter-intake`, `deadlines`, `document-review`, `billing`, etc.
|
|
195
|
+
- Each module: own routes, UI, prompts, evals, and eventually own DB schema namespace
|
|
196
|
+
- `shared/` holds true cross-cutting concerns only (auth middleware, logging, prompt registry, HTTP client)
|
|
197
|
+
|
|
198
|
+
### Medium term (modular monolith at scale)
|
|
199
|
+
|
|
200
|
+
- **Capability subfolders** inside a module (`services/filing/`, `services/service-of-process/`) before splitting modules
|
|
201
|
+
- **Outbox / domain events** replacing raw `EventEmitter` for reliability (same module boundaries)
|
|
202
|
+
- **Per-module migrations** (e.g. `repositories/migrations/`) when Postgres lands
|
|
203
|
+
- **Feature flags** in `config/` per module
|
|
204
|
+
|
|
205
|
+
### Long term (distribution without rewrite)
|
|
206
|
+
|
|
207
|
+
| Step | Action |
|
|
208
|
+
| --- | --- |
|
|
209
|
+
| 1 | Extract module HTTP + events to a separate process |
|
|
210
|
+
| 2 | Replace in-process bus with message broker (same event names) |
|
|
211
|
+
| 3 | Move module DB to dedicated instance; keep repository interfaces |
|
|
212
|
+
| 4 | Frontend module → federated route or micro-frontend if needed |
|
|
213
|
+
|
|
214
|
+
The contract is deliberately **boring**: it mirrors how mature legal-tech and enterprise teams already think (matters, workflows, integrations), but encodes it in folder structure and lint so the codebase does not depend on tribal knowledge.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Benefits
|
|
219
|
+
|
|
220
|
+
| Benefit | Why it matters for legal platform OS |
|
|
221
|
+
| --- | --- |
|
|
222
|
+
| **Clear ownership** | Module = product boundary (intake, calendaring, conflicts) |
|
|
223
|
+
| **Test pyramid per feature** | Unit (services), integration (routes), evals (AI) |
|
|
224
|
+
| **Audit-friendly AI** | Versioned prompts + eval datasets |
|
|
225
|
+
| **Replaceable integrations** | Adapters swap when court systems change |
|
|
226
|
+
| **Onboarding** | Scaffold + reference + failing lint with doc links |
|
|
227
|
+
| **Dual distribution** | Git branch for contributors, npm CLI for consumers |
|
|
228
|
+
| **v1 preserved** | `main` still valid for tutorials and minimal forks |
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Cons and tradeoffs
|
|
233
|
+
|
|
234
|
+
| Con | Reality check |
|
|
235
|
+
| --- | --- |
|
|
236
|
+
| **More folders than a todo app needs** | Overhead for tiny spikes; pay off after ~2–3 real modules |
|
|
237
|
+
| **Lint is string/path based, not type-aware** | Can miss dynamic imports or clever aliases; complement with code review and optional TypeScript later |
|
|
238
|
+
| **Frontend layers not fully linted** | Documented rules only; backend layers are automated |
|
|
239
|
+
| **In-process event bus** | Easy now; not durable — plan outbox before production workflows depend on it |
|
|
240
|
+
| **Template duplication in npm package** | `sync:cli-template` must run before publish; risk of drift if forgotten |
|
|
241
|
+
| **No database/auth in starter** | Intentional; modules will need shared conventions for tenancy and matter scoping |
|
|
242
|
+
| **`_reference` not loaded** | Good for docs; newcomers must read it deliberately |
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Challenges we still face (and how to handle them)
|
|
247
|
+
|
|
248
|
+
### Cross-module workflows (e.g. intake → matter → billing)
|
|
249
|
+
|
|
250
|
+
**Challenge:** Real legal flows span modules; strict isolation can push people toward sneaky imports.
|
|
251
|
+
|
|
252
|
+
**Handle:**
|
|
253
|
+
|
|
254
|
+
- Prefer **events** + well-named payloads (`matter.created`, `intake.completed`)
|
|
255
|
+
- For read models, use **shared read APIs** or a thin `shared/queries/` only if absolutely necessary — document every exception
|
|
256
|
+
- Long term: **saga / process manager** in `core/` or dedicated `workflows/` module that orchestrates without owning domain rules
|
|
257
|
+
|
|
258
|
+
### Multi-tenancy and matter scoping
|
|
259
|
+
|
|
260
|
+
**Challenge:** Every module must respect firm/client/matter boundaries.
|
|
261
|
+
|
|
262
|
+
**Handle:**
|
|
263
|
+
|
|
264
|
+
- Add `shared/auth/` + `shared/tenancy/` early; pass `context.tenant` from `register(app, context)`
|
|
265
|
+
- Enforce in **services**, not routes; repositories always filter by tenant id
|
|
266
|
+
- Schema tests that assert no cross-tenant leakage
|
|
267
|
+
|
|
268
|
+
### Data ownership
|
|
269
|
+
|
|
270
|
+
**Challenge:** One Postgres instance vs schema-per-module vs DB-per-service.
|
|
271
|
+
|
|
272
|
+
**Handle:**
|
|
273
|
+
|
|
274
|
+
- Start: single DB, **table prefixes** per module (`intake_leads`, `billing_invoices`)
|
|
275
|
+
- Repositories are the only DB touchpoint — eases later split
|
|
276
|
+
- No cross-module SQL joins; use events or API calls
|
|
277
|
+
|
|
278
|
+
### AI in production (not just evals)
|
|
279
|
+
|
|
280
|
+
**Challenge:** Evals pass but production drifts (model change, prompt injection, PII).
|
|
281
|
+
|
|
282
|
+
**Handle:**
|
|
283
|
+
|
|
284
|
+
- Keep **prompts versioned**; log `promptId` + `version` on every LLM call
|
|
285
|
+
- Run **evals in CI** on prompt changes
|
|
286
|
+
- Add redaction in `adapters/` before sending text to models
|
|
287
|
+
- Human-in-the-loop gates for high-risk actions (filing, sending to courts)
|
|
288
|
+
|
|
289
|
+
### Durability and compliance
|
|
290
|
+
|
|
291
|
+
**Challenge:** Legal requires audit logs, retention, e-discovery.
|
|
292
|
+
|
|
293
|
+
**Handle:**
|
|
294
|
+
|
|
295
|
+
- Append-only **audit event stream** in `shared/` (who did what, when, on which matter)
|
|
296
|
+
- Module services emit audit events alongside domain events
|
|
297
|
+
- Do not store sensitive drafts only in ephemeral LLM context
|
|
298
|
+
|
|
299
|
+
### Team discipline
|
|
300
|
+
|
|
301
|
+
**Challenge:** Lint can be bypassed (`//`, dynamic import, copy-paste).
|
|
302
|
+
|
|
303
|
+
**Handle:**
|
|
304
|
+
|
|
305
|
+
- PR checklist: `lint:architecture`, `test`, `test:evals` for modules touched
|
|
306
|
+
- CODEOWNERS per `modules/<name>/`
|
|
307
|
+
- Optional: stricter ESLint `import/no-restricted-paths` later
|
|
308
|
+
|
|
309
|
+
### TypeScript migration
|
|
310
|
+
|
|
311
|
+
**Challenge:** JavaScript starter may outgrow structural lint.
|
|
312
|
+
|
|
313
|
+
**Handle:**
|
|
314
|
+
|
|
315
|
+
- Introduce TS per module (`"allowJs": true`) starting at `schemas/` and `domain/`
|
|
316
|
+
- Layer rules still apply; types strengthen repository and API contracts
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Operational commands (v2)
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
# New feature
|
|
324
|
+
npm run new:module -- matter-intake --label "Matter Intake"
|
|
325
|
+
|
|
326
|
+
# Quality gates
|
|
327
|
+
npm run lint:architecture
|
|
328
|
+
npm test
|
|
329
|
+
npm run test:evals -- matter-intake
|
|
330
|
+
|
|
331
|
+
# CLI template sync (maintainers, before npm publish)
|
|
332
|
+
npm run sync:cli-template
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## Branch strategy
|
|
338
|
+
|
|
339
|
+
| Branch | Audience | Contents |
|
|
340
|
+
| --- | --- | --- |
|
|
341
|
+
| `main` | Minimal starter, docs, low ceremony | v1 — two-file modules, basic boundary lint |
|
|
342
|
+
| `v2` | Platform builders, legal OS direction | Internal contract, tests, evals, CLI package |
|
|
343
|
+
|
|
344
|
+
**Recommendation:** Default new product work to `v2`. Use `main` when you want the smallest possible fork or educational baseline.
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## Related documentation
|
|
349
|
+
|
|
350
|
+
- [Module internal contract](./architecture/MODULE_INTERNAL_CONTRACT.md)
|
|
351
|
+
- [Architecture guardrails](./architecture/ARCHITECTURE_GUARDRAILS.md)
|
|
352
|
+
- [Starter pack](./STARTER_PACK.md)
|
|
353
|
+
- [Publishing the CLI](./PUBLISHING.md)
|
|
354
|
+
- [create-modular-monolith README](../packages/create-modular-monolith/README.md)
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Next steps (suggested roadmap)
|
|
359
|
+
|
|
360
|
+
1. Publish `@pukujan/create-modular-monolith@2` to npm
|
|
361
|
+
2. Add `shared/auth` + tenant context in module loader
|
|
362
|
+
3. Add Postgres + first real module (`matter-intake`) with repositories
|
|
363
|
+
4. Replace `EventEmitter` with outbox table + dispatcher
|
|
364
|
+
5. Add ESLint `import/no-restricted-paths` for frontend layers
|
|
365
|
+
6. CI workflow on `v2`: `lint:architecture`, `test`, `test:evals`
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
*This log should be updated on each significant v2 release (contract changes, new shared capabilities, or publishing milestones).*
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Publishing the v2 CLI
|
|
2
|
+
|
|
3
|
+
Publish as **`@pukujan/create-modular-monolith`** — users scaffold with:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm create @pukujan/modular-monolith@2 my-platform
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## One-time setup
|
|
10
|
+
|
|
11
|
+
1. [npm account](https://www.npmjs.com/signup) and `npm login`
|
|
12
|
+
2. Ensure you can publish scope **`@pukujan`** (org or user on npm)
|
|
13
|
+
|
|
14
|
+
## Release flow
|
|
15
|
+
|
|
16
|
+
On branch **`v2`**:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm run sync:cli-template
|
|
20
|
+
# bump version in packages/create-modular-monolith/package.json if needed
|
|
21
|
+
cd packages/create-modular-monolith
|
|
22
|
+
npm publish --access public
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Package identity
|
|
26
|
+
|
|
27
|
+
| Field | Value |
|
|
28
|
+
| --- | --- |
|
|
29
|
+
| npm name | `@pukujan/create-modular-monolith` |
|
|
30
|
+
| bin | `create-modular-monolith` |
|
|
31
|
+
| user command | `npm create @pukujan/modular-monolith@2 <folder>` |
|
|
32
|
+
| major pin | `@2` = platform starter line |
|
|
33
|
+
|
|
34
|
+
## Branches
|
|
35
|
+
|
|
36
|
+
| Branch | Purpose |
|
|
37
|
+
| --- | --- |
|
|
38
|
+
| `main` | Minimal v1 modular shell |
|
|
39
|
+
| `v2` | Full internal contract + CLI |
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Documentation
|
|
2
|
+
|
|
3
|
+
This folder describes the **modular monolith platform starter** and how **architecture guardrails** keep feature modules isolated and easy to extend.
|
|
4
|
+
|
|
5
|
+
| Document | Purpose |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| [Dev log: v2](./DEVLOG_V2.md) | Changes from `main`, contract rationale, benefits/cons, challenges, growth path |
|
|
8
|
+
| [Starter pack](./STARTER_PACK.md) | What ships in the repo, how to run it, and how to add modules |
|
|
9
|
+
| [Architecture guardrails](./architecture/ARCHITECTURE_GUARDRAILS.md) | Module contracts, boundaries, naming, and how enforcement works |
|
|
10
|
+
| [Module internal contract](./architecture/MODULE_INTERNAL_CONTRACT.md) | MVC layers, prompts, evals, tests inside each feature module |
|
|
11
|
+
| [Publishing the CLI](./PUBLISHING.md) | Release `@pukujan/create-modular-monolith` to npm |
|
|
12
|
+
|
|
13
|
+
Canonical repository: [https://github.com/Pukujan/litigation-workflow-application](https://github.com/Pukujan/litigation-workflow-application)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Modular monolith starter pack
|
|
2
|
+
|
|
3
|
+
This repository is a **scalable modular monolith starter**: Express backend and Vite + React frontend with **no business modules** out of the box. You add features as self-contained modules under `backend/src/modules/` and `frontend/src/modules/` without rewriting application core.
|
|
4
|
+
|
|
5
|
+
The goal is to grow a multi-feature platform safely: each module owns its HTTP surface and UI route, while **shared** code lives under `src/shared/` on each side.
|
|
6
|
+
|
|
7
|
+
## Canonical remote
|
|
8
|
+
|
|
9
|
+
Source of truth for this codebase:
|
|
10
|
+
|
|
11
|
+
- [https://github.com/Pukujan/litigation-workflow-application](https://github.com/Pukujan/litigation-workflow-application)
|
|
12
|
+
|
|
13
|
+
Clone with:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
git clone https://github.com/Pukujan/litigation-workflow-application.git
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## What is included
|
|
20
|
+
|
|
21
|
+
| Piece | Role |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| `backend/src/core/module-loader.js` | Scans `backend/src/modules/*/index.js`, imports each, calls `register(app, context)` when present |
|
|
24
|
+
| `backend/src/core/server.js` | Express app shell; loads modules at startup |
|
|
25
|
+
| `frontend/src/core/moduleRegistry.jsx` | Eager `import.meta.glob` over `../modules/*/index.jsx`; builds the route list for the shell UI |
|
|
26
|
+
| `frontend/src/core/App.jsx` | Renders discovered module routes and navigation |
|
|
27
|
+
| `backend/src/shared/*`, `frontend/src/shared/*` | Cross-cutting helpers (HTTP errors, API client, events, and so on) |
|
|
28
|
+
| `scripts/new-module.mjs` | Creates a matching backend + frontend module skeleton with correct naming |
|
|
29
|
+
| `backend/scripts/check-module-boundaries.mjs` | Cross-module import check (backend + frontend) |
|
|
30
|
+
| `backend/scripts/check-module-layers.mjs` | Intra-module layer import rules (backend) |
|
|
31
|
+
| `docs/architecture/ARCHITECTURE_GUARDRAILS.md` | Inter-module contract |
|
|
32
|
+
| `docs/architecture/MODULE_INTERNAL_CONTRACT.md` | Intra-module MVC, prompts, evals, tests |
|
|
33
|
+
| `backend/src/modules/_reference/` | Example layout (skipped by loaders) |
|
|
34
|
+
|
|
35
|
+
Guardrails are **documented** and **automated** where practical. See [Architecture guardrails](./architecture/ARCHITECTURE_GUARDRAILS.md) and [Module internal contract](./architecture/MODULE_INTERNAL_CONTRACT.md).
|
|
36
|
+
|
|
37
|
+
## Repository layout
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
├── backend/src/core/ # server bootstrap, module loader
|
|
41
|
+
├── backend/src/modules/ # feature modules (empty except .gitkeep until you scaffold)
|
|
42
|
+
├── backend/src/shared/ # allowed shared imports for modules
|
|
43
|
+
├── frontend/src/core/ # app shell, module registry
|
|
44
|
+
├── frontend/src/modules/ # feature UI modules
|
|
45
|
+
├── frontend/src/shared/
|
|
46
|
+
├── docs/ # this documentation
|
|
47
|
+
└── scripts/new-module.mjs # module scaffolder
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Run locally
|
|
51
|
+
|
|
52
|
+
Install and start backend and frontend (from repo root):
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
cd backend && npm install && npm run dev
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
In another terminal:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
cd frontend && npm install && npm run dev
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Root `package.json` also exposes `npm run dev:backend` and `npm run dev:frontend`.
|
|
65
|
+
|
|
66
|
+
## Create a module
|
|
67
|
+
|
|
68
|
+
From the repository root:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
node scripts/new-module.mjs <kebab-case-name> --label "Readable name"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Example:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
node scripts/new-module.mjs intake-triage --label "Intake Triage"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Equivalent:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npm run new:module -- intake-triage --label "Intake Triage"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Restart the backend and refresh the frontend so the new route and API mount are picked up.
|
|
87
|
+
|
|
88
|
+
## Architecture checks
|
|
89
|
+
|
|
90
|
+
Before opening a pull request or in CI:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
npm run lint:architecture # boundaries + layers
|
|
94
|
+
npm test
|
|
95
|
+
npm run test:evals
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Details: [Architecture guardrails](./architecture/ARCHITECTURE_GUARDRAILS.md), [Module internal contract](./architecture/MODULE_INTERNAL_CONTRACT.md).
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Architecture guardrails
|
|
2
|
+
|
|
3
|
+
This document is the **contract** for feature modules and describes how the starter **enforces or encourages** that contract.
|
|
4
|
+
|
|
5
|
+
**Inside each module**, follow the layered layout in [Module internal contract](./MODULE_INTERNAL_CONTRACT.md) (routes → services → repositories/domain, plus prompts, evals, and tests). The scaffolder and `lint:layers` enforce that shape.
|
|
6
|
+
|
|
7
|
+
## Module contract
|
|
8
|
+
|
|
9
|
+
### Backend
|
|
10
|
+
|
|
11
|
+
Each backend module must export `register(app, context)` from:
|
|
12
|
+
|
|
13
|
+
- `backend/src/modules/<module-name>/index.js`
|
|
14
|
+
|
|
15
|
+
The loader (`backend/src/core/module-loader.js`) only loads directories under `backend/src/modules/` that contain `index.js`. Directories whose names start with `_` or `.` are skipped.
|
|
16
|
+
|
|
17
|
+
`context` currently includes `eventBus` (from `backend/src/shared/events/index.js`). Modules may emit or subscribe through it without importing another module’s code.
|
|
18
|
+
|
|
19
|
+
### Frontend
|
|
20
|
+
|
|
21
|
+
Each frontend module must provide a **default export** that the registry can turn into a route record. The shape expected by `frontend/src/core/moduleRegistry.jsx` is:
|
|
22
|
+
|
|
23
|
+
- `route` — string path (for example `"/intake-triage"`)
|
|
24
|
+
- `label` — short string for the nav menu
|
|
25
|
+
- `Component` — React component rendered at that route
|
|
26
|
+
|
|
27
|
+
The entry file must live at:
|
|
28
|
+
|
|
29
|
+
- `frontend/src/modules/<module-name>/index.jsx`
|
|
30
|
+
|
|
31
|
+
Discovery uses Vite’s `import.meta.glob("../modules/*/index.jsx", { eager: true })`. Any file that does not export a default with `route`, `label`, and `Component` is ignored.
|
|
32
|
+
|
|
33
|
+
## Boundaries (design rules)
|
|
34
|
+
|
|
35
|
+
- Module code may import from **its own** folder under `modules/<name>/`.
|
|
36
|
+
- Module code may import from **`src/shared/*`** on the same app (backend or frontend).
|
|
37
|
+
- Module code may import **external npm packages**.
|
|
38
|
+
- Module code **must not** import from **other** module folders (`modules/<other-name>/`).
|
|
39
|
+
|
|
40
|
+
These rules keep modules replaceable and reduce hidden coupling as the platform grows.
|
|
41
|
+
|
|
42
|
+
## Naming
|
|
43
|
+
|
|
44
|
+
- Use **kebab-case** module folder names (enforced by `scripts/new-module.mjs` for new modules).
|
|
45
|
+
- Prefer API base path aligned with the folder name: `/api/<module-name>`.
|
|
46
|
+
- Prefer frontend route aligned with the folder name: `/<module-name>`.
|
|
47
|
+
|
|
48
|
+
The scaffolder wires `/api/<module-name>` and `/<module-name>` by default.
|
|
49
|
+
|
|
50
|
+
## How guardrails are applied
|
|
51
|
+
|
|
52
|
+
| Mechanism | What it does |
|
|
53
|
+
| --- | --- |
|
|
54
|
+
| **Module loader** | Only `register` from `index.js` is used; missing or invalid modules are skipped or logged. |
|
|
55
|
+
| **Module registry** | Only well-shaped default exports become routes; incomplete modules do not appear in the menu. |
|
|
56
|
+
| **Boundary script** | `backend/scripts/check-module-boundaries.mjs` (via `npm run lint:boundaries`) scans backend and frontend module trees for `/modules/<other>/` path strings. |
|
|
57
|
+
| **Layer script** | `backend/scripts/check-module-layers.mjs` (via `npm run lint:layers`) enforces import direction inside each backend module. |
|
|
58
|
+
| **Scaffolder** | `scripts/new-module.mjs` creates the full internal layout for new modules. |
|
|
59
|
+
| **This document** | Single place for reviewers and contributors to align on contracts and naming. |
|
|
60
|
+
|
|
61
|
+
### Boundary script scope (important)
|
|
62
|
+
|
|
63
|
+
The checker detects **literal path strings** of the form `/modules/<other>/` in `.js`, `.mjs`, and `.jsx` files. It does not replace a full type-aware import resolver. Frontend **layer** rules are documented in [Module internal contract](./MODULE_INTERNAL_CONTRACT.md) and are not fully linted yet.
|
|
64
|
+
|
|
65
|
+
## Related files
|
|
66
|
+
|
|
67
|
+
- `backend/src/core/module-loader.js` — backend registration
|
|
68
|
+
- `frontend/src/core/moduleRegistry.jsx` — frontend route discovery
|
|
69
|
+
- `scripts/new-module.mjs` — paired module skeleton (full internal layout)
|
|
70
|
+
- `backend/scripts/check-module-boundaries.mjs` — cross-module import check
|
|
71
|
+
- `backend/scripts/check-module-layers.mjs` — intra-module layer check
|
|
72
|
+
- [Module internal contract](./MODULE_INTERNAL_CONTRACT.md) — MVC-style layout, prompts, evals, tests
|
|
73
|
+
|
|
74
|
+
For a high-level overview of the starter, see [Starter pack](../STARTER_PACK.md).
|