# ADR-0001: Loose coupling to upstream Common Ground modules - **Status:** Accepted - **Date:** 2026-06-03 - **Deciders:** Respellion engineering - **Template note:** This is the first ADR and doubles as the worked example of the Nygard template. Copy its shape for new ADRs (`adr-NNNN-title.md`). ## Context This reference application orchestrates several upstream Common Ground modules — OpenZaak (ZGW APIs), Open Notificaties (NRC), Objecten/Objecttypen, Flowable, Keycloak. Each is an independently developed, independently deployed peer. The temptation in a demo is to reach straight into a peer's database or couple to its internal schema to move faster. That coupling is exactly what makes Common Ground landscapes brittle and un-upgradeable in practice. We need a stance, recorded up front, on how our services may talk to these peers. ## Decision **We integrate with upstream modules only through their documented public APIs, and we isolate that integration behind explicit anti-corruption boundaries.** Concretely (mirrors CLAUDE.md §8): 1. The **ACL** is the only code that talks to ZGW APIs; no other service constructs ZGW URLs. 2. The **Workflow Client** is the only code that talks to Flowable; BPMN models hold no OpenZaak knowledge. 3. **Portals talk only to the BFF** — never directly to a backend or a peer module. 4. **No direct database access across services or to any peer.** Each service owns its schema; the Read Projection is a rebuildable derived artefact. 5. **Idempotency at every event boundary** (the Event Subscriber tolerates duplicate and out-of-order NRC events). Bending any of these is an ADR-worthy moment (CLAUDE.md §14): stop and open an `adr-proposal` issue first. ## Consequences **Positive** - Upstream modules can be upgraded or swapped behind their APIs without rippling through our services. - Coupling is visible and minimal — anti-corruption code lives in one named place. - The architecture teaches the Common Ground pattern by enforcing it. **Negative / costs** - More indirection: a translation layer (ACL, Workflow Client) instead of direct calls. Accepted — it's the point. - Eventual consistency across aggregates must be designed for, not assumed away. **Follow-up** - Each integration slice that touches a boundary references this ADR; new boundary decisions get their own ADR.