92 lines
7.0 KiB
Markdown
92 lines
7.0 KiB
Markdown
# Scaffolding Handoff — for the Senior Developer
|
|
|
|
**Goal:** finish the project foundation so the rest of the team (especially the junior — see `JUNIOR_ONBOARDING.md`) can build features by copying an established pattern rather than inventing one.
|
|
|
|
**Read first:** `ARCHITECTURE.md` (the full design, with section numbers referenced throughout this doc).
|
|
|
|
---
|
|
|
|
## 1. What's already scaffolded
|
|
|
|
A **backend reference vertical slice** is in place under `backend/`. It implements one entity (`VacationDays`) end-to-end so every layer and the cross-cutting patterns are demonstrated:
|
|
|
|
```
|
|
backend/
|
|
pyproject.toml # uv project; Python >=3.14; fastapi, pydantic, pydantic-settings, aiosqlite; dev: pytest(+asyncio,+cov), ruff, httpx
|
|
app/
|
|
config.py # pydantic-settings: app config + nested frozen LeaveRules (480/390/96/60... rule defaults)
|
|
dependencies.py # DI factories — pick repo impl by settings.database_type
|
|
main.py # FastAPI app, lifespan opens DB connection, CORS locked to frontend origin
|
|
db/connection.py # connect() + ensure_schema() (inline DDL is DEV-ONLY; see §3a)
|
|
utils/errors.py # DomainError hierarchy (NotFound/Validation/InsufficientDays/...)
|
|
entities/vacation_days.py # plain dataclass (domain)
|
|
models/vacation_days.py # pydantic API schemas (+ from_entity mapper)
|
|
repositories/base.py # abstract Repository[T] (CRUD contract)
|
|
repositories/vacation_days_repository.py # abstract, entity-specific queries
|
|
repositories_impl/sqlite/vacation_days_repository.py # concrete SQLite impl (REFERENCE)
|
|
services/vacation_days_service.py # business logic (upsert/get/list)
|
|
routers/vacation_days.py # thin HTTP layer, maps domain errors -> HTTP
|
|
tests/
|
|
conftest.py # in-memory SQLite fixtures (connection/repo/service)
|
|
unit/test_vacation_days_repository.py # repo + service tests
|
|
```
|
|
|
|
**Status / caveat:** every `.py` file compiles, and the tests are written to pass, but they were **not executed** in the authoring environment (no network to install Python 3.14). Please run them first thing:
|
|
|
|
```bash
|
|
cd backend
|
|
uv sync
|
|
uv run pytest # expect the VacationDays repo+service tests to pass
|
|
uv run ruff check .
|
|
```
|
|
|
|
If anything fails, it's almost certainly an environment/version detail — the logic has been reviewed.
|
|
|
|
---
|
|
|
|
## 2. Conventions to preserve
|
|
|
|
The slice encodes these on purpose. Keeping them consistent is what makes the codebase safe to hand to a junior:
|
|
|
|
- **Dependency direction:** routers → services → repository *interfaces*. Nothing above the repo layer imports a concrete DB class.
|
|
- **Repositories:** abstract interface in `repositories/`, one concrete impl per backend in `repositories_impl/<backend>/`. Map rows ↔ entities explicitly; always parameterize SQL.
|
|
- **Services own business rules**; repositories only persist; routers only translate HTTP ↔ domain and map `DomainError` subclasses to status codes.
|
|
- **Entities** are plain dataclasses; **API schemas** are pydantic models kept separate from entities.
|
|
- **Errors:** raise `DomainError` subclasses (`utils/errors.py`), never bare exceptions; no silent `except`.
|
|
- **Everything is `async` and fully type-hinted.** Target **≥90% test coverage** on new code; lint with ruff.
|
|
- **Rule numbers** live only in `config.py::LeaveRules` — no magic numbers elsewhere.
|
|
|
|
---
|
|
|
|
## 3. What remains to finish the foundation
|
|
|
|
Recommended order (each builds on the last). Items marked **(senior)** should stay senior-owned; the rest can be delegated once the pattern is set.
|
|
|
|
**a. Database & migrations (senior).** Replace the dev-only inline DDL in `db/connection.py` with **Alembic** migrations covering the full schema in `ARCHITECTURE.md §3.2`. Decide connection strategy: SQLite single-connection for dev vs a **Postgres pool** for prod, and adjust `dependencies.get_connection` accordingly. Add the `postgres/` repo impl skeleton.
|
|
|
|
**b. Auth & security (senior).** Registration/login, password hashing (argon2/bcrypt — never SHA), and a token strategy (short-lived JWT + refresh, or server-side sessions) plus password reset / email verification. Implement the **PersonalData-vs-User identity model** (`§3.0`) — membership via `PersonalData.workspace_id`, `User` linked 1:1. Decide personnummer handling (`§11`: prefer not storing; HMAC/encrypt if required). Add rate limiting on auth + fetch endpoints.
|
|
|
|
**c. Soft-delete repository extension (senior).** Add a `soft_delete`/`hard_delete` variant of the base repo for GDPR entities (e.g. `PersonalData`). Simple entities keep the plain CRUD base.
|
|
|
|
**d. Workspace, membership & invitations.** Workspace + parent-profile CRUD, and the **single-use invitation flow** (`§3.0`) — store only a token *hash*, expire/revoke. Token security is senior-owned; the surrounding CRUD can be delegated.
|
|
|
|
**e. Försäkringskassan data access (senior, HIGH RISK).** Define the stable `FetchDataService` contract returning a `ParentalLeaveSnapshot` so all features depend on the interface, not FK's DOM. Implement the **manual-entry path first** (it must work standalone), then a stubbed scraper behind the same contract. **Do not ship scraping without the legal/ToS + BankID review flagged in `§2.3`.**
|
|
|
|
**f. Core calculation engine (senior).** `analytics_service` implementing **days-left-at-age-4** + planned-vs-actual reconciliation (`§6.5`), including the date/leap-year math and edge cases. This is the headline feature and the trickiest logic — own it, then expose clean helpers.
|
|
|
|
**g. Tips engine skeleton.** Define the `Tip` type and the rule-registry structure from `§6`, plus the predicate helpers (`weekly_intensity`, `days_left_at_age_4`, `red_day_cluster_in_plan`, etc.). Once the helpers exist, wiring individual rules is good delegated work.
|
|
|
|
**h. Frontend foundation (not started).** Stand up the Vite + React + TS project: `tsconfig`, ESLint, Vitest, `types/` (transcribe `§7`), a typed `api` client, `AuthContext`/`WorkspaceContext`, routing, and **one reference hook + component + test** that mirrors the backend slice so the junior has a frontend template too.
|
|
|
|
**i. CI.** Pipeline running ruff + pytest (coverage gate) and eslint + vitest.
|
|
|
|
---
|
|
|
|
## 4. Then hand off to the junior
|
|
|
|
Once **a, h** (and ideally **g**'s helper stubs) are in place, the junior can start. `JUNIOR_ONBOARDING.md` already points them at the `VacationDays` slice as the template and lists their tasks — notably `BreakPeriod` and `Child` CRUD (copy the slice), date/holiday utility helpers, and the presentational frontend components/types. Please skim that doc and adjust the "what's ready" note at its top to match reality.
|
|
|
|
## 5. Open product questions
|
|
|
|
`ARCHITECTURE.md` closes with unresolved questions worth settling early: configurability of tips, audit logging, plan "preview", export, the **scraping legal stance** (the real launch gate), and special cases (multiples, pre-2014 births, single custody). Resolve or escalate these before they block feature work.
|