This page stays high-level

The module convention, the public-API boundary check, the data-ownership model, and the dependency-graph checker are documented in detail under Features. Here we cover only the layer model, build-time discovery, and the deploy artifact.

Layered architecture

No DI container layer, no adapter layer. The core is the things patties already ships; modules sit on top as plain folders.

flowchart TB
    subgraph EDGE["Boundary — web standards"]
      direction LR
      REQ["Request"]
      RES["Response"]
    end
    subgraph CORE["patties core (existing)"]
      direction TB
      SRV["Bun.serve · merged route table"]
      MW["compose() middleware
+ thin PattiesContext"] SRV --> MW end subgraph DOM["Application modules (folders)"] direction LR A["billing/
index.ts · routes.ts"] B["orders/
index.ts · routes.ts"] C["catalog/
index.ts · routes.ts"] end REQ --> SRV MW --> RES MW --> DOM A -. public API .-> B classDef core fill:#1d4ed8,color:#fff,stroke:#1e3a8a,stroke-width:2px; class CORE core;
A module exposes a public index.ts and a routes.ts. Cross-module edges (dashed) go through the public index.ts only — that is the one invariant the build enforces.

Build-time discovery & boundary check

One macro, a sibling of patties' existing route/island/agent macros. It runs during Bun.build, finds modules, merges their route maps into a single literal, and checks that no module deep-imports another's internals. The runtime never repeats this work.

flowchart LR
    G["Bun.Glob
app/*/index.ts
app/*/routes.ts"] SCAN["read each module's
routes export + imports"] CHK{"only public-API
imports?"} ERR["Build error
deep import into
another module's internals
+ file:line"] MERGE["merge route maps →
one { path: { METHOD } } literal"] BUNDLE["Bun.build →
frozen server bundle"] G --> SCAN --> CHK CHK -->|no| ERR CHK -->|yes| MERGE --> BUNDLE classDef bad fill:#fee2e2,stroke:#ef4444,color:#7f1d1d; class ERR bad;
Same contract as patties' existing *.macro.ts files: the macro's return value is inlined as a literal, so the production bundle contains the merged route table and does no runtime scan (honoring the repo's build-time-discovery rule).

Build & deploy artifact

One artifact: a frozen merged-route bundle. The production server is Bun.serve({ routes }) — no filesystem scan, no container construction.

Discovery
Bun.Glob in a build macro — finds app/*/routes.ts and merges. Runtime scans nothing.
Bundler
Bun.build only. No Webpack/Rollup/esbuild/Vite/tsup.
Dev loop
bun --hot re-runs discovery on change — same as patties' route HMR today. Frozen in prod.
HTTP / WS
Bun.serve — native route table + upgrade for WebSockets.
Request flow
The existing compose() middleware + thin PattiesContext. Nothing new.
Targets
bun and edge (WinterCG export default { fetch }) — patties' existing adapters.

Key architectural rules

① Build-time discovery

Module + route discovery happens in a macro. The production bundle inlines the merged table and re-scans nothing.

② Public-API boundary

The one new invariant: cross-module imports go through index.ts. Deep imports are build errors.

③ Standards at the boundary

Request in, Response out. The ctx stays thin — no request abstraction.

④ Bun-native primitives

Bun.serve, Bun.Glob, Bun.build, bun --hot. Node-era replacements are not introduced.

Patties design specifications · a design exploration · User docs