chore: relocate ADRs from decisions/ to docs/decisions/ to consolidate documentation

Move the ADR folder under docs/ alongside the rest of the project
documentation. Convention (flat folder, globally-sequential 4-digit
numbering, tags-based categorization, MADR 4.0.0 format) is unchanged
- only the path moved.

- git mv decisions docs/decisions preserves history for all 18 ADRs +
  README + template (19 files renamed in this commit).
- ADR-0001 amended in-place with a dated note documenting the
  relocation. Status remains 'accepted' - the location detail
  changed, the decision did not.
- All cross-references updated:
  - CLAUDE.md (~17 ADR links + 3 mentions of decisions/ in the Project
    rules section)
  - docs/README.md (now references decisions/ as a sibling under docs/)
  - docs/setup/03-angular-nx-monorepo.md (paths shortened from
    ../../decisions/ to ../decisions/, since setup/ and decisions/ are
    now both inside docs/)
  - docs/decisions/0003 ../CLAUDE.md adjusted to ../../CLAUDE.md
    (one extra level of nesting)
  - docs/decisions/template.md mention of the README path
  - notes/asvs-level-decision-briefing-rssi.md mention of the index

Sanity verified: every ADR link in CLAUDE.md, docs/setup/03, and
docs/decisions/0001 resolves to an existing file. pnpm nx run-many
-t lint passes on 8 projects.
This commit is contained in:
Julien Gautier
2026-04-30 18:57:59 +02:00
parent bd8eefb44a
commit 0e58e32d29
27 changed files with 1653 additions and 1570 deletions
@@ -0,0 +1,91 @@
---
status: accepted
date: 2026-04-29
decision-makers: R&D Lead
tags: [backend]
---
# Backend stack — NestJS over Express, Fastify, Hono
## Context and Problem Statement
The backend (`portal-bff`) is the BFF: it owns authentication, sessions, and downstream API orchestration; it persists business data; it must be observable and auditable. The frontend never holds tokens or talks directly to upstream APIs. The choice of HTTP framework has architectural ripple effects (DI shape, validation pipeline, security defaults, ecosystem of integrations).
Which Node TypeScript framework gives us the right combination of structure, ecosystem, performance, and longevity for a security-sensitive BFF in an enterprise context?
## Decision Drivers
- Security is a first-class concern — the framework must impose structure rather than leave it to be hand-built.
- Architectural alignment with the Angular frontend (DI, decorators, modules, RxJS).
- First-class ecosystem for the things we will need: Passport / MSAL, Pino logging, OpenTelemetry, Prisma, Redis, validation, health checks.
- Long-term enterprise viability (no pre-1.0, no single-maintainer projects).
- Performance acceptable for a BFF (request mediation, not raw throughput benchmarks).
## Considered Options
- **NestJS** (latest stable major). (Chosen.)
- **Express 5** — minimal, ubiquitous.
- **Fastify 5** — performance-oriented, plugin-based.
- **Hono** — modern, multi-runtime.
- **tRPC** — typed RPC over HTTP.
## Decision Outcome
Chosen option: **NestJS at the latest stable major**, mounted on the **Express adapter** by default. Migration to the Fastify adapter remains an option later if profiling justifies it; this is a configuration change, not an application rewrite, because both adapters are first-class in NestJS.
### Consequences
- Good, because NestJS imposes a clear, opinionated architecture (modules, providers, controllers, pipes, guards, interceptors) — minimal room for bricolage.
- Good, because the team lives in the same paradigm as Angular (DI, decorators, RxJS).
- Good, because every supporting concern has a first-class NestJS integration: `@nestjs/passport` + `@azure/msal-node` for auth, `nestjs-pino` for logs, `nestjs-cls` for AsyncLocalStorage, `@nestjs/terminus` for health checks, `nestjs-prisma` for persistence.
- Good, because NestJS is at v11+ with a healthy enterprise track record (Adidas, Roche, Capgemini publicly), an active core team, and commercial support options.
- Good, because the adapter pattern lets us swap to Fastify later without touching application code.
- Bad, because NestJS has a non-trivial conceptual surface (DI hierarchy, lifecycle hooks, request-scoped providers) — one-time learning cost.
- Bad, because the framework is heavier than Express or Fastify alone — acceptable for our profile.
### Confirmation
- `apps/portal-bff/package.json` depends on `@nestjs/core`, `@nestjs/common`, `@nestjs/platform-express`.
- `apps/portal-bff/src/main.ts` bootstraps via `NestFactory.create`.
- All HTTP entry points are `@Controller`-based; no raw Express routes outside framework escape hatches.
- A global `ValidationPipe` is applied with `whitelist: true, forbidNonWhitelisted: true, transform: true`.
## Pros and Cons of the Options
### NestJS (chosen)
- Good, because opinionated structure sets a clear bar across the team.
- Good, because alignment with Angular reduces cognitive distance.
- Good, because the ecosystem covers every BFF concern we have.
- Bad, because verbose for tiny services — acceptable here, this is a real BFF.
### Express 5
- Good, because the lowest common denominator — every Node engineer knows it.
- Bad, because minimalist by design — every cross-cutting concern (DI, validation, structured errors, lifecycle) is built by hand. For a security-sensitive BFF in an "anti-bricolage" project, this is a poor fit.
- Bad, because Express-as-foundation in 2026 is the legacy default; the modern center of gravity has moved.
### Fastify 5
- Good, because excellent raw performance and a clean plugin model.
- Good, because schema-driven validation is built in.
- Bad, because architecture is left to the application — we'd reinvent NestJS poorly.
- Note: NestJS can run on a Fastify adapter — we can have both if a perf profile demands it.
### Hono
- Good, because modern, minimal, multi-runtime (Node, Bun, Workers).
- Bad, because too young (~2023) for our longevity bar — limited enterprise track record.
- Status: keep in watch list; reconsider in 1224 months.
### tRPC
- Good, because end-to-end type safety from server to client without code generation.
- Bad, because it tightly couples frontend and backend builds — incompatible with a BFF that may also need to serve non-Angular consumers later (downstream API gateways, mobile, partner integrations).
- Bad, because not a general HTTP framework — orthogonal to the choice we're making.
## More Information
- NestJS documentation: https://docs.nestjs.com
- NestJS Fastify adapter: https://docs.nestjs.com/techniques/performance
- Related ADRs: [ADR-0002](0002-adopt-nx-monorepo-apps-preset.md), [ADR-0006](0006-persistence-postgresql-prisma.md), ADR-0008 (auth flow, future), ADR-0009 (sessions, future), ADR-0012 (observability, future).