Bluewoo HRMS

Backend Architecture

NestJS architecture principles and patterns

Backend Architecture

Goal

Build a clean, maintainable NestJS backend with consistent patterns that scales with the team and codebase.

Three-Layer Architecture

Controllers → Services (optional) → Repositories → Database
LayerPurposeWhen to Use
ControllersHTTP handling, validation, routingAlways
ServicesBusiness logic, orchestrationWhen logic needed
RepositoriesDatabase access via PrismaAlways

Two Patterns

Simple Pattern: Controller → Repository

Use for straightforward CRUD without business logic.

Full Pattern: Controller → Service → Repository

Use when you need:

  • Business rules or validation beyond DTOs
  • Multiple database operations
  • External service calls
  • Background jobs (BullMQ)

Default: Start simple, add service layer when complexity grows.

Project Structure

src/
├── modules/           # Feature modules
│   ├── employees/
│   │   ├── employees.controller.ts
│   │   ├── employees.service.ts    # Optional
│   │   ├── employees.repository.ts
│   │   └── dto/
│   ├── time-off/
│   ├── documents/
│   └── ...
├── common/            # Shared utilities
│   ├── guards/
│   ├── decorators/
│   └── pipes/
└── main.ts

Required NestJS Features

FeatureWhy Required
TenantGuardSecurity - validates tenant context from x-tenant-id header (MVP uses Auth.js sessions, not JWT)
ValidationPipeValidate all DTOs automatically (configured globally in main.ts)
ParseIntPipeType-safe route parameters

Optional Features (Use When Needed)

  • Interceptors - Logging, response transforms, timeouts
  • Custom Pipes - Complex data transformations
  • Exception Filters - Custom error formatting

NOT Used

  • EventEmitter - Use BullMQ for background jobs instead
  • Complex patterns (CQRS, Event Sourcing) - Keep it simple

Key Principles

Tenant Isolation

  • Every repository method takes tenantId as first parameter
  • Every query filters by tenantId
  • Never expose data across tenants

Data Access Policy

  • API-First: All frontend data fetching must go through the API (apps/api).
  • Auth Exception: The ONLY exception is apps/web connecting to the database for Auth.js (NextAuth). This is due to library limitations.

Error Handling

  • Throw NestJS exceptions (NotFoundException, BadRequestException, etc.)
  • Let exception filter handle HTTP responses
  • Services throw, controllers delegate

Transactions

  • Use Prisma $transaction() for multi-step operations
  • Keep transactions short
  • No external calls inside transactions

Background Jobs

Use BullMQ for:

  • Email sending
  • Report generation
  • File processing
  • Scheduled tasks

Don't use BullMQ for:

  • Synchronous operations
  • Operations needing immediate results
  • Simple CRUD

Feature Modules

Employees

  • CRUD operations
  • Manager-employee relationships (dual role supported)
  • Org chart queries

Time-Off

  • Request submission and approval workflow
  • Balance tracking and validation
  • Manager approval for direct reports

Documents

  • Upload to Cloud Storage
  • Metadata in PostgreSQL
  • Access control (employee, manager, admin)

Team Feed

  • Posts with media (text, image, video, voice)
  • Likes and comments
  • AI-generated summaries

Goals/OKR

  • Personal, team, company goals
  • Key results with progress tracking
  • AI goal suggestions

Workflows

  • Onboarding/offboarding templates
  • Task assignment by role
  • Progress tracking

Implementation details to be defined during development