feat(F-001): scaffold modular monolith skeleton with boundary checker

- TypeScript + Fastify skeleton under project/ (src/modules, shared, infrastructure, app)
- scripts/check-module-boundaries.mjs enforcing module public-API rules (tested with fixtures)
- GET /health endpoint, error envelope without stack leakage
- specs/F-001-scaffold (SPEC/DESIGN/TASKS/TESTS), spec/tech.md dependency justification
- 30-ticket MercadoDeVida roadmap in backlog/features.json, spec/roadmap.md
- All gates approved: reviewer, security, qa; verify.sh green
This commit is contained in:
rikrdo
2026-08-14 21:46:54 +02:00
commit 1d4eebca54
76 changed files with 9430 additions and 0 deletions

View File

@@ -0,0 +1,78 @@
# DESIGN — F-001 Scaffold modular monolith skeleton
## Affected modules
- New: `project/` application skeleton (no business modules yet, only exemplar `health` module).
## Modules touched
- `src/modules/health` (exemplar module proving the layout and boundary rule)
- `src/shared` (error envelope helper)
- `src/infrastructure/http` (Fastify server bootstrap)
- `src/app` (composition root)
## Modules NOT touched
- Everything else. No business modules exist yet. No harness files outside `work/artifacts/` and `specs/`.
## New interfaces
- `health` module public API: `registerHealthRoutes(app: FastifyInstance): Promise<void>` exported only from `src/modules/health/index.ts`.
- Boundary checker script: `node scripts/check-module-boundaries.mjs src` → exit 0 ok / exit 1 violation.
## API changes
- Adds `GET /health``200 {"status":"ok"}`.
## Database changes
- None.
## Events
- None.
## External integrations
- None.
## Cache changes
- None.
## Security considerations
- Fastify default JSON error handler replaced with an envelope that never leaks stack traces.
- No dependencies beyond Fastify + toolchain.
## Layout
```text
project/
├── package.json
├── tsconfig.json
├── eslint.config.mjs
├── vitest.config.ts
├── .gitignore
├── scripts/
│ └── check-module-boundaries.mjs
└── src/
├── app/
│ └── build-app.ts # composition root: wires modules
├── infrastructure/
│ └── http/
│ └── server.ts # listen entrypoint
├── modules/
│ └── health/
│ ├── index.ts # public API
│ ├── api/
│ │ └── health.routes.ts
│ └── tests/
│ └── health.test.ts
└── shared/
└── errors.ts
```
## Boundary rules enforced by the checker
1. Files inside `src/modules/<mod>/` may only import: own subtree (relative), `src/shared/...` (relative or alias-free path), Node builtins, and npm packages.
2. Any relative import escaping `src/modules/<mod>/` toward another module or toward `src/app`/`src/infrastructure` is a violation.
3. Files outside modules (`src/app`, `src/infrastructure`) may import a module only via its `index.ts` (direct deep import = violation).
## Toolchain
- TypeScript strict, Fastify 5, Vitest, ESLint (flat config) + Prettier, tsc build to `dist/`.
## Migration strategy
- None (greenfield).
## Rollback strategy
- Delete `project/` content added by this ticket; no other system depends on it yet.

View File

@@ -0,0 +1,55 @@
# SPEC — F-001 Scaffold modular monolith skeleton
## Problem
No codebase exists. MercadoDeVida vNext needs a boring, typed, modular home before any business feature.
## Goal
TypeScript modular monolith skeleton with strict module boundaries and a green toolchain.
## Non-goals
- No business logic
- No database access
- No frontend
## User story
As a developer (human or AI), I can run install/build/lint/typecheck/test from `project/` and get a running HTTP app with a health endpoint, so every later feature starts from a known green base.
## Functional requirements
1. `project/` contains a Node + TypeScript app with Fastify.
2. Layout: `src/modules/`, `src/shared/`, `src/infrastructure/`, `src/app/`.
3. Every module folder exposes its public API through `index.ts`.
4. A boundary checker fails when a module imports another module's internal files.
5. `GET /health` returns 200 `{"status":"ok"}`.
## Business rules
- Modules communicate only through public interfaces (`index.ts`).
- Composition happens only in `src/app` (composition root).
## Inputs
- `GET /health`: none.
## Outputs
- `GET /health`: `200 application/json {"status":"ok"}`.
## Edge cases
- Invalid route → 404 JSON envelope.
- Server start failure (port busy) → non-zero exit with clear error.
## Acceptance criteria
1. `npm install`, `npm run build`, `npm run lint`, `npm run typecheck`, `npm test` all green in `project/`.
2. `GET /health` returns HTTP 200 with `{"status":"ok"}`.
3. `src/modules`, `src/shared`, `src/infrastructure`, `src/app` exist.
4. A module importing another module's internal file fails the boundary check (demonstrated by test).
5. `./scripts/verify.sh` green at repo root.
## Dependencies
- None (first ticket).
## Security implications
- No secrets, no auth surface. Error responses must not leak stack traces.
## SEO implications
- None.
## Performance implications
- None measurable yet; baseline latency observable via test only.

View File

@@ -0,0 +1,13 @@
# TASKS — F-001 Scaffold modular monolith skeleton
- TASK-001 Init `project/` package: package.json (type module), tsconfig strict, .gitignore
- TASK-002 Add Fastify + shared error envelope + `build-app.ts` composition root
- TASK-003 Add `health` exemplar module (index.ts public API, routes, tests)
- TASK-004 Add HTTP server entrypoint (`src/infrastructure/http/server.ts`)
- TASK-005 Add boundary checker script + fixtures proving it fails on violations
- TASK-006 Add toolchain: ESLint flat config (incl. no deep cross-module imports), Prettier, Vitest
- TASK-007 Wire npm scripts: build, lint, lint:boundaries, typecheck, test, start
- TASK-008 Write project README with run commands
- TASK-009 Run full verification and write implementer evidence
Each task is independently understandable and leaves the tree consistent.

View File

@@ -0,0 +1,25 @@
# TESTS — F-001 Scaffold modular monolith skeleton
## Unit / integration (Vitest)
- `src/modules/health/tests/health.test.ts`: module-scoped; registers the module public API on a bare Fastify instance; `GET /health` → 200 and `{"status":"ok"}`.
- `src/app/tests/build-app.test.ts`: composition root; health wired through `buildApp()`; unknown route → 404 JSON envelope without stack leak.
- `scripts/tests/boundary-checker.test.ts`: run `check-module-boundaries.mjs` against fixture trees:
- fixture `ok` (imports inside own module + shared) → exit 0
- fixture `cross-module-internal` (imports another module's `domain/`) → exit 1
- fixture `deep-from-app` (app imports `modules/x/api/...` directly) → exit 1
## Toolchain checks (manual/CI commands)
- `npm run build` exits 0
- `npm run typecheck` exits 0
- `npm run lint` exits 0
- `npm run lint:boundaries` exits 0 on clean tree
- `npm test` exits 0
## Acceptance traceability
| Criterion | Evidence |
|---|---|
| install/build/lint/typecheck/test green | command outputs in implementer.md |
| GET /health 200 ok | health.test.ts + build-app.test.ts |
| src/{modules,shared,infrastructure,app} exist | tree listing in implementer.md |
| boundary violation fails lint | boundary-checker.test.ts fixtures |
| verify.sh green | qa.json evidence |