Skip to Content
DocsHow Dawn worksArchitecture

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/brand

Ownership

PackageOwnsMust not own
@dawn/brandIdentity, mark, metadata, favicon, and basic color tokensProduct-specific copy or layouts
@dawn/capabilitiesSemantic capability catalog and composition helpersTransport, UI, or implementation
@dawn/foundationErrors and result primitivesHealthcare rules, HTTP, persistence
@dawn/domainContracts, module state, repository ports, functional servicesNext.js, Drizzle, UI
@dawn/dbSchema, database client, repository adapters, migrationsHTTP or app presentation
@dawn/apiEndpoints, HTTP envelopes, client, and OpenAPI generationDomain behavior or runtime adapters
@dawn/api-runtimeNext.js routes, CORS, access context, and compositionA second copy of API contracts
@dawn/uiShared shadcn primitives, cn, and Tailwind v4 themeProduct-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 └── appointments

Every 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.

Last updated on