From 41bd4d78aa536093a1da9efdfe9e59fbd932d8eb Mon Sep 17 00:00:00 2001 From: bot-implementer Date: Mon, 31 Aug 2026 00:12:48 +0000 Subject: [PATCH] docs(adr): record modular monolith decision as ADR-001 Adds docs/adr/ADR-001-modular-monolith.md with the six required sections (Context, Decision, Alternatives, Consequences, Operational impact, Revisit trigger). The decision text ("Modular monolith") matches the ADR index entry in section 70. Documentation-only; no runtime or schema impact. --- docs/adr/ADR-001-modular-monolith.md | 107 +++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 docs/adr/ADR-001-modular-monolith.md diff --git a/docs/adr/ADR-001-modular-monolith.md b/docs/adr/ADR-001-modular-monolith.md new file mode 100644 index 0000000..f53e2ec --- /dev/null +++ b/docs/adr/ADR-001-modular-monolith.md @@ -0,0 +1,107 @@ +# ADR-001: Modular monolith + +- Status: Accepted +- Date: 2026-08-31 +- Deciders: platform stream +- References: ADR index (section 70), Architecture wiki (sections 1, 4, 9) + +## Context + +EPPP is a greenfield personal blogging platform. The first visible release +(v0.1) is intentionally small: editable site identity, editable Home page, +ordered list of published posts, individual post pages, an administration +interface and the Amber theme. It must be small because optional features are +absent, not because extensibility is absent — the codebase ships explicit, +versioned boundaries from day one for content types, content blocks, page +sections, themes, extensions, extension settings, navigation, visitor +preferences, database migrations, media storage, background jobs, events and +rendering. + +The platform is owned by a single small stream, operates a single canonical +PostgreSQL database, and has no v1 requirement for independent service +deployment, service discovery, message brokers, distributed transactions or +cross-service schema versioning. The workspace is a pnpm monorepo whose +dependency boundaries are already CI-enforced (FIT-001, FIT-010, FIT-011), so +module boundaries can be structural, explicit and testable rather than purely +organisational. + +## Decision + +EPPP is a **modular monolith**. One application image contains the public +server, admin API, rendering pipeline, extension runtime, core services, built +admin assets and first-party extensions, deployed as a single deployable unit; +an optional worker uses the same image with a different command (`eppp serve` +/ `eppp worker`). Runtime is Node.js 24 LTS with Fastify 5, PostgreSQL 18 as +the sole canonical database, React 19 for server-rendered public components +and a Vite admin, and Docker Compose as the primary runtime model. + +Modules are separated in the workspace layout (`apps/`, `packages/`, +`extensions/`) and their import edges are enforced by +`dependency-boundaries.json` in CI. Every module boundary is an explicit, +versioned contract; no module may reach into another module's internals. The +decision text — **Modular monolith** — matches the ADR index entry (ADR-001, +section 70). + +## Alternatives + +- **Microservices / distributed system** — rejected: no v1 requirement + justifies distributed transactions, service discovery, brokers, + multi-service deployment or cross-service schema changes; the operational + and cognitive cost is disproportionate for a small platform stream, and it + would split one canonical database into many. +- **Classic (unstructured) monolith** — rejected: it would undermine the + extension architecture, which depends on stable versioned contracts between + core, extensions and themes, and it would make the CI-enforced dependency + boundaries impossible to honour in practice. +- **Serverless / FaaS** — rejected: it conflicts with the chosen long-lived + runtime model (Fastify 5 process, background jobs, connection-backed + PostgreSQL access) and the single-image deployment model. +- **Modular monolith with future extraction** — chosen: module boundaries + exist now; any module can be extracted into a service later on evidence + without a rewrite of the whole system. + +## Consequences + +- Positive: one deployable unit means simpler operations, atomic deploys and a + single image to build, scan and promote; all modules share one transaction + and consistency boundary via the canonical database; a small team can own + the entire platform; explicit boundaries force API discipline and keep the + extension contracts honest. +- Negative: the single process only stays modular through discipline — a + boundary violation degrades it toward a big ball of mud, which is exactly + what the CI dependency-boundary tests guard against; scaling is vertical + until a module is extracted; deploys are all-or-nothing; a fault in one + module can affect the whole process (mitigated by the optional worker + separation for background jobs and by health/readiness gates). +- Neutral: extraction of a module into a service remains possible and is a + deliberate, evidence-based decision rather than a default; the monorepo + layout already supports it because modules are physically separated. + +## Operational impact + +- One application image/container for the server plus an optional worker + container running the same image with a different command; no service + discovery, message broker or per-service observability requirements at v1. +- Single PostgreSQL 18 instance as the sole canonical database with one core + migration chain; extension-owned tables live in independent chains + (`eppp_extension_migrations`), all run behind the advisory migration lock at + startup. +- The health/readiness endpoint gates on migration completion; readiness is + reported only when the single deployable unit is fully initialised. +- Deploy is build-one-image-and-run-Compose; rollback is redeploying the + previous image. Horizontal scaling means running more instances of the same + image behind a proxy for stateless work and more workers for background + jobs; the database remains the single shared store. + +## Revisit trigger + +- Revisit this ADR when a module's change frequency, team ownership or scaling + needs diverge enough that a single deployable unit becomes a bottleneck — + for example, when a module needs an independent deploy cadence, independent + scaling or a different runtime — and there is evidence (per the architecture + review gates, ADR index section 68) that extracting that module into a + service would help. +- Revisit if the platform grows to multiple independent products or teams + requiring distributed transactions or independent data stores, or if the + v1.1 boundary contract (ADR-027 to ADR-032) pressures the single-image + model. -- 2.54.0