ADR-007: Monorepo for Integration Source Code
| Field | Value |
|---|---|
| Status | Accepted |
| Date | 2026-04-07 |
| Related SAD | SAD-001 |
| Related ADR | ADR-006 |
| Related Research | ORCA Platform |
Context
The xRED ELN integration layer consists of multiple integration adapters (Lookups, Apps, Jobs), each with its own FastAPI backend and optional React frontend. The question is whether these should live in a single repository or in separate repositories.
This is distinct from ADR-006 (monolith vs microservices), which concerns deployment topology. A monorepo can still produce independently deployed services — which is the current xRED approach.
ORCA took the opposite path: 96 separate repositories, one per service. Their own user feedback acknowledges the overhead this creates.
Decision
All integration source code lives in a single monorepo (xred-eln-integrations) with
each app in its own apps/ subdirectory. Infrastructure manifests live in a separate
repo (xred-eln-infrastructure) per Minerva’s GitOps requirements.
xred-eln-integrations/ ← source code monorepo
├── apps/
│ ├── demo/
│ │ ├── backend/
│ │ └── frontend/
│ ├── poc-signals/
│ │ ├── backend/
│ │ └── frontend/
│ └── next-integration/
│ └── backend/
├── certs/
└── .github/workflows/
xred-eln-infrastructure/ ← GitOps manifests (separate repo)
├── k8s/
│ ├── demo/
│ ├── poc-signals/
│ └── kustomization.yamlRationale
Shared CI pipeline with selective execution
The monorepo CI uses git diff to detect which apps changed and runs jobs only for
those apps via a matrix strategy. This gives the efficiency of per-app pipelines without
maintaining separate CI configurations. A single .github/workflows/ directory governs
lint, test, security, and build for all integrations.
Shared infrastructure (certs, configs, standards)
Roche CA certificates (certs/), shared linting configs (Ruff, ESLint), and security
scanning run once at the repo level. In a multi-repo setup, these would need to be
duplicated or managed via a shared template — both of which drift over time.
Atomic cross-integration changes
When a shared pattern changes (e.g. a new auth middleware, a Signals client update, or a Dockerfile base image bump), a single PR updates all integrations atomically. In ORCA’s 96-repo setup, this requires 96 separate MRs.
Discoverability
A new team member can see all integrations, their structure, and their tests in one place. With ORCA’s multi-repo approach, understanding the full landscape requires navigating dozens of repositories.
Infrastructure repo remains separate
Per Minerva’s GitOps model (Minerva Onboarding), Kubernetes manifests live in a separate repo that ArgoCD watches. This separation is intentional:
- Manifest changes don’t trigger application CI
- Clean audit log for deployment changes
- Developers without production push access can still develop application code
- Avoids infinite CI loops
Alternatives Considered
Multi-repo (ORCA’s approach — 96 repos)
Rejected. ORCA’s own user feedback highlights the overhead: duplicated CI configs, inconsistent patterns across repos, difficulty making cross-cutting changes, and no single view of the integration landscape. With a small team, maintaining N repos adds friction without clear benefit.
Single repo for everything (source + manifests)
Rejected. Minerva requires manifest separation for GitOps (ArgoCD pulls from a dedicated config repo). Combining them would also mean application code pushes trigger ArgoCD syncs, and developers with app-level access could inadvertently modify production manifests.
Consequences
- All integrations share the same dependency management tooling (uv, npm)
- CI pipeline complexity grows with the number of apps (matrix strategy mitigates this)
- A broken CI on
developblocks all integrations, not just one - Large PRs that touch shared code require careful review to avoid unintended side effects
- The Deploy workflow in GitHub Actions handles per-app deployment from the monorepo to the infrastructure repo