# 9. Architecture Decisions ## ADR-001: FastAPI as the Web Framework **Status:** Accepted **Context:** The backend must expose REST APIs with automatic OpenAPI documentation and support high-concurrency I/O without blocking threads. **Decision:** Use FastAPI for all three bounded context services. **Rationale:** FastAPI is async-first, generates OpenAPI 3.x specs automatically via Pydantic models, and has strong community adoption in the Python ecosystem. **Consequences:** All route handlers must be `async def`; blocking calls (e.g. file I/O) must be offloaded with `run_in_executor`. --- ## ADR-002: One PostgreSQL Schema per Bounded Context **Status:** Accepted **Context:** Three bounded contexts need persistent storage. Sharing tables would couple contexts at the database level. **Decision:** Use a single PostgreSQL instance with separate schemas: `users`, `posts`, `messaging`. **Rationale:** Schema-level isolation enforces context boundaries without the operational overhead of running three separate database servers. Cross-context queries are forbidden; data is fetched via API calls. **Consequences:** Each service connects with a role that has access only to its own schema. Cross-context joins are not possible by design. --- ## ADR-003: JWT RS256 for Authentication **Status:** Accepted **Context:** Multiple services need to verify user identity without coupling to the Users Service on every request. **Decision:** Issue RS256-signed JWTs from the Users Service; all other services validate tokens locally using the public key. **Rationale:** Asymmetric signing allows any service to verify tokens without access to the private key or a network call, keeping services loosely coupled. **Consequences:** The RS256 public key must be distributed to all services (via environment variable). Key rotation requires a coordinated redeployment. --- ## ADR-004: Fan-out on Read for Timeline Feed **Status:** Accepted **Context:** A user's timeline aggregates posts from all followees. Two strategies exist: fan-out on write (pre-compute per follower) and fan-out on read (query at request time). **Decision:** Use fan-out on read: query the posts of all followees at request time, paginated and sorted by `created_at`. **Rationale:** At the current scale (≤10 000 concurrent users) fan-out on read is simpler to implement and avoids write amplification for users with many followers. Can be revisited if read latency becomes a bottleneck. **Consequences:** Feed query performance depends on the number of followees. An index on `(author_id, created_at DESC)` in the posts table is required. --- ## ADR-005: Modular Monorepo over Microservices **Status:** Accepted **Context:** Three bounded contexts could be deployed as fully independent microservices or co-located in a monorepo. **Decision:** Single Git repository; three independently deployable FastAPI processes. **Rationale:** Monorepo simplifies cross-context refactoring, shared tooling (linting, CI), and onboarding while still allowing independent scaling and deployment of each service. **Consequences:** Shared libraries (e.g. JWT validation, error models) live in a `common/` package within the repo. Changes to `common/` affect all services and require coordinated releases.