Architecture
The browser applications depend on the API contract and shared UI. The API runtime composes domain services with PostgreSQL repository adapters.
apps/dawn ─────┐
├── packages/api ──────── packages/foundation
apps/patient ──┘ │
├── packages/domain
└── packages/capabilities
apps/api ── packages/api ── packages/domain ── packages/foundation
│ contract
└────── packages/db ── Drizzle/Postgres
apps/agents ── packages/api ── packages/capabilities
apps/docs ──────────────────── packages/capabilities
apps/* ─────────────────────── packages/ui
apps/* ─────────────────────── packages/brandOwnership
| Package | Owns | Must not own |
|---|---|---|
@dawn/brand | Identity, mark, metadata, favicon, and basic color tokens | Product-specific copy or layouts |
@dawn/capabilities | Semantic capability catalog and composition helpers | Transport, UI, or implementation |
@dawn/foundation | Errors and result primitives | Healthcare rules, HTTP, persistence |
@dawn/domain | Contracts, module state, repository ports, functional services | Next.js, Drizzle, UI |
@dawn/db | Schema, database client, repository adapters, migrations | HTTP or app presentation |
@dawn/api | Endpoints, HTTP envelopes, client, and OpenAPI generation | Domain behavior or runtime adapters |
@dawn/api-runtime | Next.js routes, CORS, access context, and composition | A second copy of API contracts |
@dawn/ui | Shared shadcn primitives, cn, and Tailwind v4 theme | Product-specific layouts |
Every Next.js workspace has a components.json, but primitive source is owned
once by packages/ui. Product-specific composition and layout stay with their
application.
Capability definitions sit above transport. The API binds them to typed endpoints, while apps add their own presentation, execution, or approval details. See Capabilities.
Dependency injection
Domain services are functions that receive repository ports. Concrete PostgreSQL adapters are selected in the API composition root. Keep external failures inside result values so route handlers remain thin transport adapters.
Tenancy
The workspace is the tenant boundary:
workspace
├── members
├── settings
├── patients
├── practitioners
└── appointmentsEvery persisted resource has a workspaceId. Composite appointment foreign
keys prevent a patient or practitioner from another workspace being referenced.
API routes receive a functional access-context resolver. It verifies the
short-lived Dawn token from a bearer header or HttpOnly cookie, then exposes a
workspace-scoped member or patient actor. Patient reads are pinned to the
patient ID in the claim. Member commands derive { workspaceId, memberId }
internally, and owner-only commands check the role. Clients do not send tenant
IDs. Static demo context is disabled in production.