Skip to content

Architecture

The new backend is a NestJS application organized around modules, shared infrastructure services, and a strongly typed runtime.

It is architected very differently from the legacy PHP monolith. Instead of server-rendered route handling, it is designed as a modern API service with centralized bootstrap configuration and modular domain boundaries.

The application starts from backend/src/main.ts.

At bootstrap time, the backend does the following:

  • loads Sentry instrumentation first through import './instrument'
  • creates the Nest application with buffered logs
  • replaces the default logger with a custom application logger
  • applies a global ValidationPipe
  • applies a global response interceptor
  • enables CORS using environment-driven configuration
  • enables Swagger in non-production environments
  • starts listening on process.env.PORT or port 3000

This means the backend has one centralized bootstrap layer where cross-cutting concerns are wired once for the whole service.

Several architectural decisions are applied globally instead of per module.

The backend uses Nest’s global ValidationPipe with:

  • transformation enabled
  • whitelist enabled
  • non-whitelisted fields not forbidden by default

This indicates a request-validation-first design where DTOs are expected to define the allowed request shape.

A global response interceptor is registered:

  • ResponseInterceptor

This suggests the backend aims to normalize response structure across modules instead of letting each controller define a different response style.

The application uses:

  • CustomLogger

This logger is injected into the application bootstrap and also used by lower-level services such as Prisma-related infrastructure.

CORS is configured through environment variables:

  • CORS_ORIGIN
  • CORS_METHODS
  • CORS_HEADERS

This makes the backend deployment-aware and easier to adapt across development, staging, and production.

The root composition happens in backend/src/app.module.ts.

The AppModule imports:

  • global configuration via ConfigModule
  • global i18n configuration
  • Sentry module support
  • Prisma module
  • health module
  • auth module
  • consultants module
  • reference module
  • development module only in development mode

This confirms the backend follows a root-module composition pattern where common infrastructure is loaded first and feature modules are registered explicitly.

Configuration is provided through Nest ConfigModule and environment files.

The current root config setup loads:

  • .env.local
  • .env

This indicates a layered environment strategy where local overrides take precedence over shared defaults.

The backend includes Sentry-based observability through:

The Sentry setup currently:

  • enables logging capture based on environment
  • filters sensitive headers
  • attaches contextual tags such as errorId
  • disables remote reporting in development

This shows the backend is designed with production diagnostics in mind, not as a purely local development service.

Two global filters are registered in AppModule:

  • SentryGlobalFilter
  • HttpExceptionFilter

This means exceptions are expected to pass through a centralized error-handling path rather than being handled ad hoc in individual controllers.

The i18n system is globally configured in backend/src/i18n/i18n.module.ts.

The current i18n design includes:

  • fallback language support
  • locale loading from filesystem
  • query-based language resolution through lang or locale
  • Accept-Language header support
  • generated TypeScript typing for translation keys

This is a strong sign that localization is treated as a first-class architectural concern.

The currently visible module areas include:

  • auth
  • consultants
  • reference
  • health
  • messaging
  • database
  • i18n
  • development

These are not flat folders only for organization. They are actual Nest modules that separate controllers, services, and infrastructure responsibilities.

The auth module is one of the most architecturally significant modules.

From the current implementation, it includes:

  • controller-driven auth endpoints
  • JWT configuration
  • Passport integration
  • JWT strategy
  • a global auth guard
  • messaging integration for OTP-like flows
  • Prisma access for identity persistence
  • translation-aware error messaging

The auth module therefore acts as both:

  • a feature module
  • a security boundary provider for the rest of the application

APP_GUARD is registered inside the auth module using AuthGuard.

This strongly suggests authentication is applied globally by default, with explicit decorators such as @Public() or other scoped decorators used to opt routes out or change access behavior.

The backend uses Prisma as the clearest active data access layer.

The current database architecture includes:

  • a global PrismaModule
  • a PrismaService extending PrismaClient
  • Nest lifecycle integration through OnModuleInit and OnModuleDestroy
  • connection health checks

This means database access is exposed as an application-wide injectable infrastructure dependency rather than being re-created per module.

Because PrismaModule is marked @Global(), any module can inject PrismaService without re-declaring the provider chain. This reduces friction, but it also increases the need for discipline in how domain modules access persistence.

The health module exposes a public operational endpoint using Terminus.

The health controller currently checks:

  • disk health
  • heap memory health
  • database health through Prisma

This reflects a backend that is being prepared for deployment, uptime monitoring, and operational checks across environments.

Swagger is configured centrally in main.ts for non-production environments.

The current Swagger setup includes:

  • title and description
  • versioning
  • bearer authentication support
  • local, staging, and production server entries
  • global Accept-Language header documentation

This indicates the backend is intended to be discoverable and explorable by developers and integrators during development and staging.

The current security model is shaped by several layers working together:

  • JWT token issuance and refresh behavior
  • Passport integration
  • global auth guard
  • route decorators such as @Public(), @Private(), and refresh-auth behavior
  • scope-aware access control inside auth flows

This is a cleaner and more explicit security model than the session-driven style of the legacy PHP application.

The DevelopmentModule is conditionally imported only when NODE_ENV === 'development'.

This is an important architectural signal:

  • the backend already differentiates between development-only capabilities and normal runtime behavior
  • environment-sensitive module registration is part of the design

The current backend can be described as:

  • modular monolithic API service
  • infrastructure-first NestJS application
  • centralized bootstrap with global pipes, filters, and interceptors
  • Prisma-backed data layer
  • JWT-based stateless auth model
  • observability-aware and deployment-aware service

It is not yet a microservice architecture, but it is clearly designed to be cleaner, more testable, and more operationally mature than the legacy website backend.

Based on the current implementation, the architecture already has several strong qualities:

  • clear modular boundaries
  • centralized cross-cutting concerns
  • typed request validation
  • built-in observability hooks
  • explicit health monitoring
  • development-friendly Swagger exposure
  • structured authentication foundation

A few important tradeoffs are also visible:

  • Prisma is clearly active, but TypeORM is still present in dependencies, which may indicate partial transition or unused stack weight
  • global infrastructure patterns reduce duplication, but can hide coupling if domain boundaries are not kept clean
  • auth being globally enforced is powerful, but requires disciplined use of public/private decorators across controllers
  • a global Prisma dependency is convenient, but can encourage modules to bypass stronger repository or service boundaries if not managed carefully

For documentation purposes, this backend should currently be framed as:

  • the new backend foundation
  • a NestJS modular API platform
  • a service-oriented replacement path for selected legacy responsibilities
  • a typed, validated, observable backend designed for growth

That framing matches the code more accurately than describing it as only a basic Nest starter or a complete distributed microservice system.