@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.
Files changed (65) hide show
  1. package/README.md +43 -0
  2. package/bin/create-modular-monolith.js +132 -0
  3. package/package.json +39 -0
  4. package/template/README.md +73 -0
  5. package/template/backend/package-lock.json +882 -0
  6. package/template/backend/package.json +20 -0
  7. package/template/backend/scripts/check-module-boundaries.mjs +69 -0
  8. package/template/backend/scripts/check-module-layers.mjs +152 -0
  9. package/template/backend/src/core/module-loader.js +35 -0
  10. package/template/backend/src/core/server.js +24 -0
  11. package/template/backend/src/modules/.gitkeep +0 -0
  12. package/template/backend/src/modules/_reference/README.md +11 -0
  13. package/template/backend/src/modules/_reference/adapters/README.md +3 -0
  14. package/template/backend/src/modules/_reference/config/index.js +4 -0
  15. package/template/backend/src/modules/_reference/domain/README.md +3 -0
  16. package/template/backend/src/modules/_reference/evals/README.md +6 -0
  17. package/template/backend/src/modules/_reference/evals/datasets/example.cases.json +12 -0
  18. package/template/backend/src/modules/_reference/evals/runners/example.eval.mjs +25 -0
  19. package/template/backend/src/modules/_reference/events/index.js +4 -0
  20. package/template/backend/src/modules/_reference/index.js +9 -0
  21. package/template/backend/src/modules/_reference/prompts/manifest.json +14 -0
  22. package/template/backend/src/modules/_reference/prompts/templates/example.prompt.js +7 -0
  23. package/template/backend/src/modules/_reference/repositories/.gitkeep +0 -0
  24. package/template/backend/src/modules/_reference/routes/health.routes.js +10 -0
  25. package/template/backend/src/modules/_reference/routes/index.js +8 -0
  26. package/template/backend/src/modules/_reference/schemas/health.schema.js +8 -0
  27. package/template/backend/src/modules/_reference/services/health.service.js +7 -0
  28. package/template/backend/src/modules/_reference/tests/integration/health.routes.test.js +20 -0
  29. package/template/backend/src/modules/_reference/tests/unit/health.service.test.js +9 -0
  30. package/template/backend/src/modules/_reference/utils/index.js +3 -0
  31. package/template/backend/src/shared/ai/prompt-registry.js +42 -0
  32. package/template/backend/src/shared/events/index.js +8 -0
  33. package/template/backend/src/shared/http/errors.js +10 -0
  34. package/template/backend/src/shared/testing/create-test-app.js +13 -0
  35. package/template/docs/DEVLOG_V2.md +369 -0
  36. package/template/docs/PUBLISHING.md +39 -0
  37. package/template/docs/README.md +13 -0
  38. package/template/docs/STARTER_PACK.md +98 -0
  39. package/template/docs/architecture/ARCHITECTURE_GUARDRAILS.md +74 -0
  40. package/template/docs/architecture/MODULE_INTERNAL_CONTRACT.md +164 -0
  41. package/template/frontend/index.html +12 -0
  42. package/template/frontend/package-lock.json +1724 -0
  43. package/template/frontend/package.json +21 -0
  44. package/template/frontend/src/core/App.jsx +35 -0
  45. package/template/frontend/src/core/moduleRegistry.jsx +39 -0
  46. package/template/frontend/src/index.css +53 -0
  47. package/template/frontend/src/main.jsx +10 -0
  48. package/template/frontend/src/modules/.gitkeep +0 -0
  49. package/template/frontend/src/modules/_reference/README.md +3 -0
  50. package/template/frontend/src/modules/_reference/components/ModuleHealthCard.jsx +14 -0
  51. package/template/frontend/src/modules/_reference/hooks/use-module-health.js +27 -0
  52. package/template/frontend/src/modules/_reference/index.jsx +7 -0
  53. package/template/frontend/src/modules/_reference/pages/_referencePage.jsx +11 -0
  54. package/template/frontend/src/modules/_reference/prompts/README.md +3 -0
  55. package/template/frontend/src/modules/_reference/schemas/health.schema.js +3 -0
  56. package/template/frontend/src/modules/_reference/services/health-api.js +5 -0
  57. package/template/frontend/src/modules/_reference/tests/unit/health.schema.test.js +8 -0
  58. package/template/frontend/src/modules/_reference/utils/index.js +3 -0
  59. package/template/frontend/src/shared/api/client.js +10 -0
  60. package/template/frontend/vite.config.js +6 -0
  61. package/template/package.json +16 -0
  62. package/template/scripts/lib/module-scaffold.mjs +409 -0
  63. package/template/scripts/new-module.mjs +58 -0
  64. package/template/scripts/run-module-evals.mjs +43 -0
  65. 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).