Decision records
The load-bearing decisions behind a thin modular monolith on Bun. Each row opens to its own page with the context, the call, and what it costs. The throughline: take the modular-monolith boundary, and add as little framework as possible on top of Bun's primitives.
Architecture decision records
| ADR | Decision | Status |
|---|---|---|
001 |
A module is a folder, not a class A module is just a directory under app/ — no decorated class, no registration object. |
accepted |
002 |
No DI container; wiring is plain ES imports Cross-module wiring is plain import statements; Bun's module system is the container. |
accepted |
003 |
Encapsulation = a public-API boundary check at build A module may only be imported via its index.ts; deeper imports are build errors. |
accepted |
004 |
No decorators, no reflect-metadataHandlers are plain functions in a routes map — no decorator machinery. |
accepted |
005 |
No new request lifecycle; reuse compose()Module handlers run in the existing middleware chain; no new phases. |
accepted |
006 |
Validation, jobs, persistence stay libraries Call Zod, an ORM, the job primitives directly — the framework doesn't wrap them. |
accepted |
007 |
Shared state has an explicit owner and a frozen mutation window One owner, one mutation window, one freeze point per shared structure. |
draft |
008 |
The module dependency graph must be a DAG Inter-module cycles are a hard build failure, detected by patties graph. |
draft |
009 |
What's out of scope, and what stays untouched No DI, decorators, or scopes; the existing SSR/islands/UI surface is unaffected. |
accepted |
Open questions
① Route prefixing
Do module routes self-declare full paths (as shown), or does the build prefix them with the module name (/billing/...)? Self-declared is more flexible; auto-prefix is more enforced. Leaning self-declared, with collision detection at merge.
② Module root convention
Is the boundary rule scoped to app/*, or configurable for nested feature trees? How are path aliases (@billing) resolved by the check?
③ Packaging the boundary check
Is the check part of the build macro, a standalone patties CLI command, or also a Biome/lint rule for editor feedback? Likely the macro is the gate; a lint rule is a nice-to-have.
④ Relationship to patties' file routes
patties already has file-based routing for pages. Do module routes.ts maps coexist with that, or is this an alternate convention for API-first apps? (The "two front doors" question.)
Cross-cutting risks
Recorded here, not on the roadmap — these are standing concerns the design must hold to, each tied to a decision above.
Boundary check completeness
Resolving relative paths, path aliases, and re-export barrels correctly so the check (ADR-003) neither false-positives nor is trivially dodged. Scoped and tested as part of Epic 1.
Two front doors
patties would offer both file-based page routing and module routes.ts maps. The docs must make "use this for API-first / service-heavy apps" obvious, not confusing — see open question ④.
Route prefixing call
Self-declared full paths vs build-applied module prefixes — open question ①. Decide before the macro API is committed.
Staying thin
Pressure to grow this into DI / scopes / decorators. The whole value is that it doesn't — keep it convention + one macro (ADR-009).