Add 7 ADRs documenting backend architecture decisions

This commit is contained in:
2026-04-26 16:35:08 +02:00
parent 66821427be
commit d52795fde3
8 changed files with 173 additions and 0 deletions
View File
@@ -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/<domain>/` — 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.
@@ -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.
@@ -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.
@@ -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/<domain>/` 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.
@@ -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.
+28
View File
@@ -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 <jwt>` (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.
@@ -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.