From d52795fde3da862d31e938789a15365fa0d7a163 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sun, 26 Apr 2026 16:35:08 +0200 Subject: [PATCH] Add 7 ADRs documenting backend architecture decisions --- docs/decisions/.gitkeep | 0 docs/decisions/0001-framework-expressjs.md | 24 ++++++++++++++++ ...002-database-migration-mongodb-to-mysql.md | 27 ++++++++++++++++++ docs/decisions/0003-rest-api-with-swagger.md | 23 +++++++++++++++ .../0004-route-organisation-by-domain.md | 22 +++++++++++++++ .../0005-two-separate-repositories.md | 24 ++++++++++++++++ docs/decisions/0006-jwt-authentication.md | 28 +++++++++++++++++++ docs/decisions/0007-legacy-routes-removal.md | 25 +++++++++++++++++ 8 files changed, 173 insertions(+) create mode 100644 docs/decisions/.gitkeep create mode 100644 docs/decisions/0001-framework-expressjs.md create mode 100644 docs/decisions/0002-database-migration-mongodb-to-mysql.md create mode 100644 docs/decisions/0003-rest-api-with-swagger.md create mode 100644 docs/decisions/0004-route-organisation-by-domain.md create mode 100644 docs/decisions/0005-two-separate-repositories.md create mode 100644 docs/decisions/0006-jwt-authentication.md create mode 100644 docs/decisions/0007-legacy-routes-removal.md diff --git a/docs/decisions/.gitkeep b/docs/decisions/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/decisions/0001-framework-expressjs.md b/docs/decisions/0001-framework-expressjs.md new file mode 100644 index 0000000..7a00d0f --- /dev/null +++ b/docs/decisions/0001-framework-expressjs.md @@ -0,0 +1,24 @@ +# ADR 0001: Backend Framework — Express.js + +**Date:** 2026-04-26 +**Status:** Accepted + +## Context + +The backend serves a JSON REST API consumed by the Angular frontend. The requirements are straightforward: HTTP routing, middleware chaining, JSON body parsing, JWT authentication, and database access. No server-side rendering, no real-time features, no heavy framework conventions are needed. + +## Decision + +Use Express.js as the HTTP framework. The application is structured around: +- `src/routes/api//` — route definitions by functional domain +- `src/controllers/` — request/response handling +- `src/services/` — business logic +- `src/middlewares/` — cross-cutting concerns (auth, error handling, async wrapper) +- `createApp.js` — application factory (separates app creation from server startup, enabling testability) + +## Consequences + +- **Positive:** Minimal abstraction. Full control over middleware order and request lifecycle. +- **Positive:** Large ecosystem. Well-understood by the team. +- **Negative:** No convention over configuration — project structure is manually maintained. +- **Negative:** Async error handling requires explicit wrapping (`asyncHandler` middleware) since Express 4 does not catch promise rejections natively. Express 5 (not yet used) handles this automatically. diff --git a/docs/decisions/0002-database-migration-mongodb-to-mysql.md b/docs/decisions/0002-database-migration-mongodb-to-mysql.md new file mode 100644 index 0000000..8a73ce2 --- /dev/null +++ b/docs/decisions/0002-database-migration-mongodb-to-mysql.md @@ -0,0 +1,27 @@ +# ADR 0002: Database Migration — MongoDB to MySQL with Sequelize + +**Date:** 2026-04-26 +**Status:** Accepted + +## Context + +The project initially used MongoDB (evidence: `mongo:start`/`mongo:stop` Docker scripts still present in `package.json`, and `$oid` ObjectId references in the original data export). MongoDB was likely chosen for its flexible schema during early prototyping. + +As the data model stabilised and relational queries became more common (joins between users, jumps, canopies, drop zones, etc.), a relational database became a better fit. MySQL is a well-known, widely hosted relational database with strong Sequelize support. + +## Decision + +Migrate to MySQL with Sequelize as the ORM. Sequelize provides: +- Model definitions with typed fields +- Migration system (`sequelize-cli db:migrate`) for schema versioning +- Seeders for initial reference data +- Relationship declarations (`src/database/relationships/`) + +The `mongo:start`/`mongo:stop` npm scripts are legacy artefacts and can be removed when confirmed no longer needed. + +## Consequences + +- **Positive:** Relational integrity enforced at the database level. Joins are first-class. +- **Positive:** Sequelize migrations provide a reproducible setup path (`npm run setup:api`). +- **Negative:** Less flexible schema than MongoDB — changes require migrations. +- **Note:** The original data was exported from MongoDB (documents with `$oid` fields) and re-imported into MySQL via a one-shot migration script. That script has since been removed from the frontend codebase. diff --git a/docs/decisions/0003-rest-api-with-swagger.md b/docs/decisions/0003-rest-api-with-swagger.md new file mode 100644 index 0000000..864fc9b --- /dev/null +++ b/docs/decisions/0003-rest-api-with-swagger.md @@ -0,0 +1,23 @@ +# ADR 0003: REST API with Swagger Documentation + +**Date:** 2026-04-26 +**Status:** Accepted + +## Context + +The API must be understandable and testable without reading source code. Two documentation approaches were considered: +- **OpenAPI/Swagger** — industry standard, generates interactive UI, supports code generation. +- **Postman collection** — already present (`tests/adastra-api-tests.postman_collection.json`), good for integration testing but not a substitute for living documentation. + +## Decision + +Use `swagger-jsdoc` + `swagger-ui-express` to generate and serve an interactive OpenAPI 3.0 documentation at a dedicated route. Annotations are written as JSDoc comments directly in route/controller files. + +Postman collections are kept for integration/regression testing (run via `npm run test:postman` with Newman), complementing rather than replacing Swagger. + +## Consequences + +- **Positive:** Living documentation — always in sync with the code. +- **Positive:** Interactive UI allows manual endpoint testing without a separate tool. +- **Positive:** OpenAPI spec can be used to generate client types if needed. +- **Negative:** JSDoc annotations add verbosity to route files. Annotations must be kept up to date manually. diff --git a/docs/decisions/0004-route-organisation-by-domain.md b/docs/decisions/0004-route-organisation-by-domain.md new file mode 100644 index 0000000..2aa8854 --- /dev/null +++ b/docs/decisions/0004-route-organisation-by-domain.md @@ -0,0 +1,22 @@ +# ADR 0004: Route Organisation by Functional Domain + +**Date:** 2026-04-26 +**Status:** Accepted + +## Context + +The API covers four independent functional domains (skydive, cms, ecommerce, herowars), each with distinct models, business logic, and frontend consumers. A flat route structure would make it difficult to reason about domain boundaries and onboard new contributors. + +## Decision + +Routes are grouped under `src/routes/api//` with a barrel index (`src/routes/api/index.js`). Each domain owns its routes, controllers, services, and models independently. The domain prefix is reflected in the API path (e.g. `/api/skydive/jumps`, `/api/cms/articles`). + +This structure mirrors the frontend's domain organisation (see frontend ADR 0011), making the full-stack data flow traceable: a frontend service under `core/services/skydive/` calls `/skydive/` routes, which map to `src/routes/api/skydive/`. + +Legacy `v1/`, `v2/`, `v3/` directories exist alongside the active routes as backup snapshots from earlier iterations. They are slated for removal once their contents are confirmed no longer needed (see ADR 0007). + +## Consequences + +- **Positive:** Domain boundaries are explicit and enforced by directory structure. +- **Positive:** Consistent mapping between frontend service paths and backend routes simplifies debugging. +- **Negative:** Cross-domain features (shared auth middleware, user model) live in `src/middlewares/` and `src/database/models/` respectively, outside any domain folder. diff --git a/docs/decisions/0005-two-separate-repositories.md b/docs/decisions/0005-two-separate-repositories.md new file mode 100644 index 0000000..e41e5ec --- /dev/null +++ b/docs/decisions/0005-two-separate-repositories.md @@ -0,0 +1,24 @@ +# ADR 0005: Frontend and Backend in Separate Repositories + +**Date:** 2026-04-26 +**Status:** Accepted + +## Context + +The frontend (Angular SPA) and backend (Express API) are distinct deployment units with different runtimes, dependencies, and release cycles. Two organisational options exist: + +- **Monorepo** — single repository containing both applications, possibly managed with Nx or Turborepo. +- **Separate repositories** — each application in its own repository with independent versioning. + +## Decision + +Maintain two separate repositories: `adastra_app` (frontend) and `adastra_api` (backend). Both are treated as a single product during development — tasks that span both (e.g. adding a new API endpoint and its frontend consumer) are handled in a single working session across both repos. + +## Consequences + +- **Positive:** Independent dependency management. Frontend and backend `package.json` files don't interfere with each other. +- **Positive:** Simpler CI/CD pipeline per repo when production deployment is set up. +- **Positive:** Each repo's git history reflects only its own changes. +- **Negative:** No shared type definitions between frontend and backend. API contract changes must be coordinated manually. +- **Negative:** Cross-repo changes require two separate commits/PRs. A monorepo would allow atomic cross-boundary commits. +- **Tooling note:** The Angular frontend's `CLAUDE.md` declares `adastra_api` as an additional working directory so that cross-repo tasks can be handled in a single session. diff --git a/docs/decisions/0006-jwt-authentication.md b/docs/decisions/0006-jwt-authentication.md new file mode 100644 index 0000000..25c596a --- /dev/null +++ b/docs/decisions/0006-jwt-authentication.md @@ -0,0 +1,28 @@ +# ADR 0006: Authentication — JWT with express-jwt + +**Date:** 2026-04-26 +**Status:** Accepted + +## Context + +The API must authenticate requests from the Angular frontend. Options considered: +- **Session-based auth** — server stores session state; requires sticky sessions or shared session store in multi-instance deployments. +- **JWT (JSON Web Tokens)** — stateless; token carries the user identity; no server-side session storage. + +Given that the application is currently single-instance with no horizontal scaling requirement, either would work. JWT is simpler to operate and aligns with the frontend's existing token-based auth flow. + +## Decision + +Use JWT for authentication: +- Tokens are issued by the API on successful login (`jsonwebtoken` library, `bcrypt` for password hashing). +- Incoming requests are validated by the `express-jwt` middleware, which populates `req.auth` with the decoded token payload. +- The `src/middlewares/auth.js` middleware wraps `express-jwt` and handles role-based access control (`Admin` role required for protected admin routes). + +The frontend stores the token in `localStorage` and sends it as `Authorization: Token ` (see frontend ADR 0005). + +## Consequences + +- **Positive:** Stateless — no session store needed. Horizontally scalable without sticky sessions. +- **Positive:** Single middleware handles auth for all routes. +- **Negative:** Tokens cannot be invalidated server-side before expiry. Acceptable for this use case (internal application, low revocation risk). +- **Security:** Passwords are hashed with `bcrypt`. The JWT secret must be kept in environment configuration, never committed. diff --git a/docs/decisions/0007-legacy-routes-removal.md b/docs/decisions/0007-legacy-routes-removal.md new file mode 100644 index 0000000..210463d --- /dev/null +++ b/docs/decisions/0007-legacy-routes-removal.md @@ -0,0 +1,25 @@ +# ADR 0007: Removal of Legacy Route Directories (v1, v2, v3) + +**Date:** 2026-04-26 +**Status:** Proposed + +## Context + +The `src/routes/` directory contains three legacy subdirectories (`v1/`, `v2/`, `v3/`) alongside the active `api/` directory. These are backup snapshots of earlier API iterations, kept during the migration to the current domain-based structure. They are not mounted in the application and serve no runtime purpose. + +Their presence: +- Creates confusion about which routes are active. +- Adds noise to `grep` and IDE navigation. +- Risks accidentally referencing stale logic. + +## Decision + +Remove `v1/`, `v2/`, and `v3/` directories once the following is confirmed: +- No active code references them (direct imports or dynamic requires). +- Their content has been superseded by the equivalent routes under `src/routes/api/`. + +## Consequences + +- **Positive:** Cleaner repository. Only active routes remain. +- **Risk:** If any code path still references these directories, removal would cause a runtime error. Verify with `grep -r "routes/v[123]"` before deleting. +- **Status rationale:** Marked as *Proposed* rather than *Accepted* until the verification step is completed and the deletion is committed.