← Back to Engineering

Architecture Decision Records

Formal record of significant technical decisions. Status: Accepted unless noted otherwise.

ADR-0001: Turborepo Monorepo Orchestration

Status: Accepted

Date: 2026-07-06

Deciders: Li Hong

Related: 15-Monorepo-Structure.md, 02-Tech-Stack.md


Context

NovaDesk contains five applications, six backend services, and eight shared packages in a single repository. We need build orchestration that:

  1. Parallelizes tasks across workspaces
  2. Caches build outputs between CI runs and local development
  3. Respects dependency order (packages before services before apps)
  4. Keeps CI fast enough for a solo maintainer workflow

Decision

Adopt pnpm workspaces for dependency linking and Turborepo 2 for task pipelines (build, lint, test, typecheck, dev).

Pipeline rules in turbo.json:

  • build depends on ^build (upstream packages first)
  • Outputs cached: .next/**, dist/**
  • dev is persistent and uncached

Alternatives considered

AlternativeRejected because
NxHeavier configuration for portfolio scope; Turborepo sufficient
LernaLess active ecosystem; weaker caching story
Separate reposLoses atomic cross-service changes and shared CI
npm workspaces onlyNo task caching or pipeline DAG

Consequences

Positive

  • Single pnpm turbo build validates entire monorepo
  • CI time reduced via remote-aware cache keys
  • Clear task graph visible in turbo.json

Negative

  • Turborepo adds a dependency and learning curve
  • Cache invalidation requires discipline on env var changes

Compliance

Aligns with 00-Vision.md goal of demonstrating monorepo engineering maturity.

↑ Back to Engineering index

ADR-0002: NestJS for Backend Microservices

Status: Accepted

Date: 2026-07-06

Deciders: Li Hong

Related: 01-Architecture.md, 16-Service-Catalog.md


Context

NovaDesk requires six backend services with consistent patterns for authentication guards, validation, OpenAPI documentation, health checks, and testing. Framework choice affects hiring signal, maintainability, and integration with TypeScript monorepo tooling.


Decision

Use NestJS 10 for all backend services:

  • @novadesk/gateway — API Gateway
  • @novadesk/auth-service — Identity provider
  • @novadesk/notification-service — Email and notifications
  • @novadesk/helpdesk-api — HelpDesk domain API
  • @novadesk/analytics-api — Analytics domain API
  • @novadesk/realtime-chat-service — WebSocket messaging

Each service is a standalone NestJS application with its own Dockerfile, captain-definition, and database schema where applicable.


Alternatives considered

AlternativeRejected because
Fastify standaloneLess structure for multi-module domains; Spell used Fastify but NovaDesk needs consistency across 6 services
ExpressNo built-in DI, module system, or OpenAPI integration
Go microservicesBreaks TypeScript end-to-end; shared types with frontend lost
Single Express monolithDoes not demonstrate service boundary thinking

Consequences

Positive

  • Uniform module/guard/interceptor patterns via @novadesk/auth and @novadesk/logger
  • Swagger auto-generated per service
  • Jest/Vitest testing with Nest testing utilities

Negative

  • NestJS boilerplate per service
  • Cold start slightly higher than minimal Fastify (acceptable for portfolio)

Compliance

Documented in service READMEs and 03-Coding-Standards.md.

↑ Back to Engineering index

ADR-0003: Next.js App Router for Frontends

Status: Accepted

Date: 2026-07-06

Deciders: Li Hong

Related: 02-Tech-Stack.md, 03-Coding-Standards.md


Context

NovaDesk has four SaaS applications plus a public marketing/documentation website. Frontend technology must support SSR for SEO (website), client-heavy dashboards (HelpDesk, Analytics), and shared UI components across apps.


Decision

Use Next.js 14 with App Router for all frontends:

AppPackagePurpose
Website@novadesk/websitePortfolio, case studies, engineering docs
HelpDesk@novadesk/helpdesk-saasTicket management UI
Analytics@novadesk/analytics-dashboardKPI dashboards
Admin@novadesk/admin-panelPlatform administration
Chat@novadesk/realtime-chatRealtime messaging UI

Conventions:

  • Server Components by default
  • Client Components only for interactivity (forms, charts, WebSocket)
  • Feature-Sliced Design folder structure in SaaS apps
  • Shared UI from @novadesk/ui

Alternatives considered

AlternativeRejected because
Vite + React SPANo SSR for website; worse SEO for portfolio
RemixSmaller hiring signal; team familiarity with Next.js
Pages RouterApp Router is current standard; RSC benefits

Consequences

Positive

  • One React framework across all apps
  • SSG for case studies and engineering pages
  • API routes only at integration boundaries (contact form proxy)

Negative

  • App Router complexity for WebSocket-heavy chat
  • Multiple Next.js builds in CI (mitigated by Turborepo cache)

Compliance

Matches 03-Coding-Standards.md frontend conventions.

↑ Back to Engineering index

ADR-0004: PostgreSQL as Primary Data Store

Status: Accepted

Date: 2026-07-06

Deciders: Li Hong

Related: 17-Data-Architecture.md


Context

Services need durable relational storage with ACID guarantees, migration tooling, and TypeScript integration. HelpDesk requires complex tenant-scoped queries; Auth requires transactional consistency for credentials and tokens.


Decision

Use PostgreSQL 16 as the primary database with Prisma 5 ORM per service.

Database ownership:

ServiceDatabaseNotes
Auth Serviceauth_dbUsers, roles, tenants, refresh tokens
HelpDesk APIhelpdesk_dbTickets, customers, workspaces
Notificationnotification_dbTemplates, delivery log
Analytics APIanalytics_dbMetrics aggregates

No cross-service database access. Services communicate via HTTP APIs only.

Redis is used for cache, rate limiting, pub/sub, and BullMQ queues — not as primary store.


Alternatives considered

AlternativeRejected because
MongoDBWeaker relational modeling for multi-tenant RBAC
MySQLPostgreSQL JSON and extension ecosystem preferred
Shared databaseViolates service boundary isolation
Supabase/FirebaseExternal dependency; less control for portfolio demo

Consequences

Positive

  • Prisma schema as living documentation
  • Migration history per service
  • Strong consistency for auth and billing-adjacent data

Negative

  • Multiple PostgreSQL instances in production (or schemas with strict isolation)
  • Prisma migration coordination across services

Compliance

Aligned with 17-Data-Architecture.md.

↑ Back to Engineering index

ADR-0005: Docker Containerization Strategy

Status: Accepted

Date: 2026-07-06

Deciders: Li Hong

Related: 06-DevOps.md, DEPLOY-CAPROVER.md


Context

NovaDesk must run identically in local development and production. Twelve deployable units (6 services + 5 apps + nginx) require reproducible builds and independent deploy capability.


Decision

Container-first deployment:

  1. Local: docker-compose.yml with PostgreSQL, Redis, Nginx, and all services
  2. Production: CapRover with per-component captain-definition and Git webhook deploy
  3. Multi-stage Dockerfiles — build stage (pnpm + turbo) → slim runtime (Node 20 Alpine)

Each service/app has:

  • Dockerfile at package root
  • captain-definition pointing to Dockerfile path
  • Environment variables documented in .env.example

Nginx routes path prefixes to containers — single domain, multiple apps.


Alternatives considered

AlternativeRejected because
PM2 on VPSNo isolation; harder to replicate locally
KubernetesOperational overhead excessive for solo portfolio
Serverless (Lambda)WebSocket and long-running workers poorly suited
Vercel-onlyBackend microservices need persistent processes

Consequences

Positive

  • docker compose up gives full stack locally
  • Independent service deploys on CapRover
  • Demonstrates DevOps competence to reviewers

Negative

  • Image build time in CI
  • CapRover-specific deploy knowledge required

Compliance

Documented in 06-DevOps.md and infrastructure/caprover/README.md.

↑ Back to Engineering index

ADR-0006: Shared Packages for Cross-Cutting Concerns

Status: Accepted

Date: 2026-07-06

Deciders: Li Hong

Related: 15-Monorepo-Structure.md, M1 in 09-Roadmap.md


Context

Six services and five applications duplicate auth logic, env validation, logging format, UI primitives, and API types without shared packages. Duplication causes drift and undermines the monorepo value proposition.


Decision

Create eight workspace packages under packages/:

PackageResponsibility
@novadesk/tsconfigTypeScript base configs
@novadesk/eslint-configESLint flat config
@novadesk/configZod env schemas
@novadesk/sharedTypes, enums, utilities
@novadesk/loggerStructured Pino logging
@novadesk/authJWT utils, guards, decorators
@novadesk/sdkHTTP client for service calls
@novadesk/uiDesign system components

Dependency rules:

  • Packages may not import from services/ or apps/
  • Apps and services consume packages via workspace:*
  • UI package is framework-aware but primitives stay portable

Alternatives considered

AlternativeRejected because
Copy-paste utilsDrift between services; poor reviewer signal
npm published packagesOverhead for private portfolio monorepo
Single common packageGod package anti-pattern; boundaries blur

Consequences

Positive

  • Auth guard behavior identical across all NestJS services
  • UI consistency across four Next.js apps
  • Single place to update logging format

Negative

  • Package versioning discipline required
  • Breaking changes ripple via Turborepo rebuild

Compliance

Milestone M1 deliverable per 09-Roadmap.md.

↑ Back to Engineering index

ADR-0007: API Gateway as Single Entry Point

Status: Accepted

Date: 2026-07-06

Deciders: Li Hong

Related: 01-Architecture.md, 07-Security.md


Context

Multiple backend services expose REST APIs. Clients should not hold service-specific URLs, CORS policies, or JWT validation logic. A single perimeter simplifies security, rate limiting, and observability.


Decision

Implement API Gateway (@novadesk/gateway) as the sole HTTP entry point for backend APIs:

Responsibilities:

  1. JWT validation (RS256 via JWKS from Auth Service)
  2. Rate limiting (Redis-backed sliding window)
  3. Request ID assignment and propagation
  4. Reverse proxy to target service by path prefix
  5. WebSocket upgrade proxy for Realtime Chat
  6. CORS policy enforcement
  7. Circuit breaker for unhealthy upstreams (configurable)

Clients call /api/v1/* only. Internal service URLs are not exposed to browsers.

Nginx sits in front for TLS termination and static path routing to Next.js apps.


Alternatives considered

AlternativeRejected because
Direct service accessCORS and auth duplicated per service
Kong/AWS API GatewayExternal dependency; less code visibility in portfolio
GraphQL federationREST aligns with NovaDesk API standards; simpler for reviewers
Service mesh (Istio)Operational complexity unjustified

Consequences

Positive

  • Single place for auth, rate limits, and request logging
  • Services trust gateway-injected identity headers on internal network
  • Clear diagram for architecture interviews

Negative

  • Gateway becomes critical path — requires health monitoring and horizontal scale plan
  • Added network hop latency (minimal on same host/Docker network)

Compliance

Documented in 16-Service-Catalog.md and request flow page.

↑ Back to Engineering index