- Added Zod as a dependency in package.json. - Updated pnpm-lock.yaml to include Zod. - Refactored API interfaces: exported new modules for perk, survivor, mission, and encounter. - Removed obsolete api-interfaces.ts file. - Enhanced tests for new schemas in api-interfaces.spec.ts, covering various validation scenarios.
71 lines
3.7 KiB
Markdown
Executable File
71 lines
3.7 KiB
Markdown
Executable File
# 0002 — Nx monorepo with enforced module boundaries
|
|
|
|
- **Status:** Accepted
|
|
- **Date:** 2026-05-06
|
|
|
|
## Context and problem statement
|
|
|
|
Fog Expedition has at least three distinct deployable surfaces (Twitch Video Overlay frontend, NestJS EBS backend, content/balance library), and types must be shared between them (DTOs, domain models, encounter records). The project is being built with heavy AI assistance, which adds a specific failure mode: AI assistants happily reach across project boundaries with relative imports if not constrained.
|
|
|
|
How should the codebase be structured?
|
|
|
|
## Decision drivers
|
|
|
|
- **Type sharing without duplication.** The overlay and EBS exchange `Mission`, `Survivor`, `EncounterResult` etc. Hand-keeping types in sync across separate repos is bug-prone.
|
|
- **AI-assisted-build discipline.** AI tools generate plausible-looking code that violates architectural conventions unless those conventions are mechanically enforced.
|
|
- **Solo-developer simplicity.** Multiple repos mean multiple CI configs, multiple versioning streams, multiple onboarding paths.
|
|
- **Future scaling.** Should the project ever grow to more contributors, the architecture should still be coherent.
|
|
- **Tooling support for the chosen language stack** (TypeScript, Angular, NestJS).
|
|
|
|
## Considered options
|
|
|
|
1. **Multi-repo (polyrepo).** Frontend, backend, and shared types each in their own repository. Shared types published to a private npm registry.
|
|
2. **Loose monorepo (npm/pnpm workspaces only).** All projects in one repo, no orchestrator. No automated boundary enforcement.
|
|
3. **Nx monorepo with `enforce-module-boundaries`.** All projects in one repo. Project tags (`scope:api`, `scope:overlay`, `scope:shared`, `type:app`, `type:lib`) define dependency rules enforced at lint time.
|
|
4. **Turborepo.** Lighter monorepo orchestrator, focused on caching and task running. No native module boundary enforcement.
|
|
|
|
## Decision outcome
|
|
|
|
**Chosen: Nx monorepo with enforced module boundaries.**
|
|
|
|
Project structure:
|
|
|
|
```
|
|
apps/
|
|
api/ # scope:api, type:app
|
|
overlay/ # scope:overlay, type:app
|
|
libs/
|
|
api-interfaces/ # scope:shared, type:lib (Zod schemas, DTOs)
|
|
mission-logic/ # scope:shared, type:lib (encounter resolver, perk math)
|
|
encounter-library/ # scope:shared, type:lib (content + helpers)
|
|
```
|
|
|
|
Boundary rules enforced via `@nx/enforce-module-boundaries`:
|
|
|
|
- Apps can only depend on libs (not other apps).
|
|
- `scope:api` can depend on `scope:api` and `scope:shared`.
|
|
- `scope:overlay` can depend on `scope:overlay` and `scope:shared`.
|
|
- `scope:shared` can only depend on `scope:shared`.
|
|
|
|
Violations fail lint (verified at workspace setup with a deliberate negative test).
|
|
|
|
## Consequences
|
|
|
|
### Positive
|
|
|
|
- Type sharing is direct via TypeScript imports, no version coordination needed.
|
|
- Module boundary enforcement catches architecturally incorrect AI-generated code at lint time before code review.
|
|
- Single CI pipeline, single dependency tree, single onboarding flow.
|
|
- `nx affected` runs only what changed, keeping CI fast as the project grows.
|
|
- Per-project `package.json` files keep workspace libs publish-ready (e.g., `api-interfaces` could later be published as a community SDK without restructuring).
|
|
|
|
### Negative
|
|
|
|
- Steeper learning curve than plain pnpm workspaces — Nx generators, executors, and project configuration are non-trivial.
|
|
- Tighter coupling to Nx's release cadence; `nx migrate` is required for major version upgrades but generally handles them well.
|
|
- Some Nx generators lag behind the underlying tooling (e.g., `@nx/nest` doesn't yet support Vitest natively — see ADR-0003).
|
|
|
|
### Neutral
|
|
|
|
- The monorepo single source of truth makes architectural mistakes more visible; it also makes them more impactful when they happen.
|