# Architecture ## Overview dwellops-platform is a TypeScript-first monorepo for a modern HOA management platform. It is designed for: - **Self-hosted single-tenant** deployments (one HOA per server). - **Future SaaS multi-tenant** evolution without major structural changes. ## Workspace layout ``` apps/ api/ — Fastify REST API web/ — Next.js 16 frontend (App Router) packages/ config/ — shared config (ESLint, Prettier, Stylelint, tsconfig, Vitest) types/ — TypeScript-only domain types schemas/ — Zod schemas (reusable across frontend/backend) db/ — Prisma client + data access boundary i18n/ — locale helpers ui/ — shared React UI primitives (CSS Modules + design tokens) test-utils/ — test factories, render helpers ``` ## Dependency rules - `apps/api` → `@dwellops/db`, `@dwellops/types`, `@dwellops/schemas` - `apps/web` → `@dwellops/ui`, `@dwellops/types`, `@dwellops/schemas`, `@dwellops/i18n` - **Prisma is only ever imported from `@dwellops/db`** — never directly in apps. - `packages/types` has zero runtime dependencies. - `packages/schemas` depends only on Zod. ## Frontend structure (`apps/web/src`) | Layer | Description | Rules | | ------------- | ------------------------------------- | ------------------------------- | | `components/` | Pure presentational building blocks | No API calls, no business logic | | `widgets/` | Composed UI units with local behavior | May hold local state | | `views/` | Page-level server compositions | Orchestrates data + layout | All styling uses **CSS Modules + PostCSS**. Tailwind is explicitly forbidden. Design tokens live in `packages/ui/src/tokens/tokens.css` as CSS custom properties. ## Backend structure (`apps/api/src`) | Directory | Description | | ----------- | --------------------------------------------------------------- | | `plugins/` | Fastify plugins (cors, swagger, auth, etc.) | | `modules/` | Feature modules (health, auth, hoa, …) each with routes + tests | | `services/` | Business logic services | | `lib/` | Utilities: env validation, error types, logger, auth instance | **Route handlers are thin**: validate → check auth/permissions → call service → shape response. ## Authentication Powered by [Better Auth](https://better-auth.com). | Method | Status | | -------------- | ------------------------------------- | | Magic link | ✅ Enabled by default | | Passkeys | ✅ Enabled by default | | OIDC | ⚙️ Optional (set `OIDC_ENABLED=true`) | | Email/password | ❌ Disabled | Auth state is resolved in a Fastify plugin (`plugins/auth.ts`) on every request and attached as `request.session`. Route-level enforcement uses the `requireAuth` preHandler helper. ## Authorization Roles: `ADMIN`, `BOARD_MEMBER`, `TREASURER`, `OWNER`, `TENANT`, `VIEWER`. A `Membership` record connects a `User` to an `Hoa` with a `Role`, optionally scoped to a `Unit`. This enables future multi-tenant expansion: a user can hold different roles in different HOAs. ## Database PostgreSQL + Prisma. All access goes through `@dwellops/db`. Core models: - `User`, `Session`, `Account`, `Verification` — Better Auth required - `Hoa` — homeowners association - `Unit` — dwelling unit within an HOA - `Membership` — user ↔ HOA ↔ role ↔ optional unit - `AuditLog` — immutable record of sensitive actions ## Internationalization All user-facing strings use [next-intl](https://next-intl.dev). Translation files: - `apps/web/messages/.json` — aggregated messages (do not edit directly) - `src/.../translations.json` — component-local translation fragments - `scripts/aggregate-translations.ts` — merges component files into the messages file ## Audit logging `AuditService` (`apps/api/src/services/audit.service.ts`) records sensitive actions to the `AuditLog` table. It never throws — audit failures are logged but do not block primary operations.