4.2 KiB
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/schemasapps/web→@dwellops/ui,@dwellops/types,@dwellops/schemas,@dwellops/i18n- Prisma is only ever imported from
@dwellops/db— never directly in apps. packages/typeshas zero runtime dependencies.packages/schemasdepends 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.
| 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 requiredHoa— homeowners associationUnit— dwelling unit within an HOAMembership— user ↔ HOA ↔ role ↔ optional unitAuditLog— immutable record of sensitive actions
Internationalization
All user-facing strings use next-intl. Translation files:
apps/web/messages/<locale>.json— aggregated messages (do not edit directly)src/.../translations.json— component-local translation fragmentsscripts/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.