Files
apf_portal/decisions/0002-adopt-nx-monorepo-apps-preset.md
T
Julien Gautier 880c7ded6b chore: rename project from adastra_portal to apf_portal
The host organisation - APF France Handicap - was confirmed on
2026-04-30. Update all in-repo references to use apf_portal
(snake_case in prose) and apf-portal (kebab-case workspace name and
repo URL). Touched: CLAUDE.md, ADRs 0001/0002/0003/0015, and the Nx
bootstrap setup guide.

The historical name is preserved as a single sentence in ADR-0003 as
a self-validating example of the function-prefixed naming convention
designed exactly for this scenario - the apps (portal-shell,
portal-bff) and the lib conventions (feature-<name>, shared-<scope>)
were unaffected by the rename, which was the explicit point of
ADR-0003.

Memory state aligned out-of-band: project_adastra.md retired,
project_apf_portal.md created with the expanded APF context (host
org, health + financial data scope, ASVS L3 pending RSSI input, UI
stack decision spartan-ng + CDK + Tailwind, expanded phase-3 status).

Pending follow-ups (user-side, not in this commit):
- rename the Gitea repo julien/adastra_portal -> julien/apf_portal
- git remote set-url origin gitea@git.unespace.com:julien/apf_portal.git
- optionally rename the local working directory ~/Works/adastra_portal/
  -> ~/Works/apf_portal/
2026-04-30 13:31:38 +02:00

4.6 KiB

status, date, decision-makers, tags
status date decision-makers tags
accepted 2026-04-29 R&D Lead
infrastructure
frontend
backend

Adopt Nx monorepo with the apps preset

Context and Problem Statement

apf-portal will host two cooperating runtime artifacts from day one — an Angular SPA frontend and a Node.js BFF backend — and is expected to grow with feature libraries shared between them (auth, data-access, UI primitives). Strongly-typed contracts must travel from back to front without round-tripping through a published package, dependency hygiene must be enforceable, and CI must run only on what changed.

How do we structure the repository so both apps coexist with shared libraries, builds and tests run incrementally, and the tooling is mainstream enough to be supported long-term?

Decision Drivers

  • Two cooperating runtimes (Angular + NestJS), both TypeScript, sharing types and contracts on the wire.
  • Need for incremental, cached builds and tests on what changed (affected-style workflow).
  • Long-term enterprise track record — no exotic stack.
  • Compatibility with pnpm (already mandated by project setup).
  • Enforceable dependency boundaries between apps and libs.

Considered Options

  • Polyrepos — separate repository per runtime.
  • Nx with the apps preset — generic multi-tech workspace, plugins added as needed (@nx/angular, @nx/nest).
  • Nx with the angular-monorepo preset — front-first preset, BFF grafted on later.
  • Turborepo + pnpm workspaces — lighter alternative to Nx.
  • pnpm workspaces alone — manual orchestration, no monorepo tool.

Decision Outcome

Chosen option: Nx monorepo with the apps preset, managed by pnpm.

The workspace will host:

  • apps/portal-shell — Angular SPA frontend (see ADR-0004);
  • apps/portal-bff — NestJS BFF backend (see ADR-0005);
  • libs/shared/<scope> and libs/feature/<name> for shared and feature code (see ADR-0003).

Plugins enabled at bootstrap: @nx/angular, @nx/nest, @nx/eslint, @nx/vite (Vitest).

Consequences

  • Good, because front and back share types, lint config, formatter, and test runner with no extra plumbing.
  • Good, because Nx's project graph + affected make CI fast and frugal as the codebase grows.
  • Good, because the apps preset stays neutral — neither runtime is privileged in workspace structure.
  • Good, because Nx is mainstream, used by major enterprises, and actively maintained by Nrwl.
  • Bad, because Nx adds a non-trivial conceptual surface (executors, generators, project graph) — a one-time learning cost.
  • Bad, because monorepos require discipline to keep dependency boundaries clean — mitigated by Nx's enforce-module-boundaries lint rule.

Confirmation

  • Workspace is bootstrapped with pnpm dlx create-nx-workspace@latest apf-portal --preset=apps --pm=pnpm.
  • nx.json has @nx/eslint/enforce-module-boundaries enabled and configured against the project tags.
  • CI runs pnpm nx affected -t lint test build on every push.

Pros and Cons of the Options

Polyrepos

  • Good, because each runtime has independent versioning and CI scope.
  • Bad, because sharing TypeScript types across repos requires a published package (overhead, version drift).
  • Bad, because cross-cutting changes (e.g. adjusting an auth claim shape) touch two repos and two PRs.

Nx with apps preset (chosen)

  • Good, because runtime-agnostic — Angular and Node coexist as peers.
  • Good, because Nx provides build orchestration, caching, generators, and lint boundaries out of the box.

Nx with angular-monorepo preset

  • Good, because faster initial setup for a front-first project.
  • Bad, because the preset implies the project is "an Angular workspace" — the BFF feels grafted, the workspace package.json ends up biased toward Angular tooling.

Turborepo

  • Good, because lighter, no codegen surface.
  • Neutral, because caching/affected is comparable.
  • Bad, because no first-class Angular plugin; both runtimes would need manual integration.
  • Bad, because smaller enterprise track record than Nx.

pnpm workspaces alone

  • Good, because no extra tool to learn.
  • Bad, because no caching, no affected, no generators — every piece reinvented by hand (the bricolage we're forbidden).

More Information