ADR-003: Monorepo Architecture β Multi-Service Polyglot Design
Metadataβ
| Field | Value |
|---|---|
| Status | Accepted |
| Date | 2026-02-06 |
| Linear | N/A (Foundational) |
Contextβ
STOA Platform is a comprehensive API Management solution comprising multiple services: a control plane API, multiple frontends, an AI-native gateway, infrastructure automation, and tooling. These components share common dependencies, require coordinated releases, and benefit from unified CI/CD pipelines.
The architectural decision on repository structure significantly impacts:
- Developer Experience β How easy is it to contribute across components?
- CI/CD Complexity β How do we build, test, and deploy interdependent services?
- Code Sharing β How do we share utilities, types, and components?
- Release Coordination β How do we version and release the platform?
The Problemβ
"Should each service live in its own repository (polyrepo) or should everything coexist in a single repository (monorepo)?"
This is a foundational decision that affects every aspect of development workflow.
Considered Optionsβ
1. Monorepo (Single Repository)β
All application code, infrastructure definitions, and shared components in one repository. Separate repositories only for runtime configuration (GitOps) and infrastructure-as-code secrets.
2. Polyrepo (Multiple Repositories)β
Each service in its own repository: stoa-api, stoa-console, stoa-portal, stoa-gateway, stoa-cli, etc.
3. Hybrid (Monorepo + Satellites)β
Core services in a monorepo, with specialized components (infrastructure, docs) in separate repositories.
Decision Outcomeβ
Chosen option: Hybrid Monorepo β Core application code in stoa, with dedicated repos for infrastructure (stoa-infra), documentation (stoa-docs), quickstart (stoa-quickstart), and CLI (stoactl).
Repository Structureβ
| Repository | Purpose | Content |
|---|---|---|
| stoa | Application monorepo | All services, shared code, CI/CD |
| stoa-infra | Infrastructure IaC | Terraform, Ansible, Helm (canonical) |
| stoa-docs | Documentation | Docusaurus site, ADRs |
| stoa-quickstart | Self-hosted quickstart | Docker Compose |
| stoactl | CLI tool | Go + Cobra |
| stoa-gitops | Runtime config | Tenant configs, ArgoCD apps |
Monorepo Structureβ
stoa/
βββ control-plane-api/ # Python 3.11 β FastAPI backend
βββ control-plane-ui/ # React 18 + TypeScript β Console UI
βββ portal/ # React 18 + TypeScript β Developer Portal
βββ mcp-gateway/ # Python 3.11 β MCP Gateway (current)
βββ stoa-gateway/ # Rust (stable) β Future unified gateway
βββ cli/ # Python β Internal CLI
βββ e2e/ # Playwright + BDD β E2E tests
βββ landing-api/ # Python 3.12 β Landing page API
β
βββ shared/ # Shared frontend components
β βββ components/ # React components (Toast, CommandPalette)
β βββ contexts/ # React contexts (Theme)
β
βββ charts/ # Helm charts (synced with stoa-infra)
β βββ stoa-platform/ # Main platform chart
β
βββ deploy/ # Deployment configurations
β βββ argocd/ # ArgoCD ApplicationSets
β βββ demo/ # Demo tenant seeding
β βββ docker-compose/ # Local development
β βββ platform-bootstrap/ # Initial platform setup
β
βββ ansible/ # Ansible playbooks
β βββ reconcile-webmethods/ # Gateway reconciliation
β
βββ scripts/ # Utility scripts
βββ docs/ # Internal documentation
βββ services/ # Supporting services
β βββ kafka-bridge/ # Kafka bridge service
β
βββ stoa-catalog/ # UAC schemas and templates
βββ stoa-policy-engine-rs/ # Rust OPA evaluation library
β
βββ .github/workflows/ # CI/CD workflows
βββ CLAUDE.md # AI context file
Technology Stack Matrixβ
| Component | Language | Framework | Runtime |
|---|---|---|---|
| control-plane-api | Python 3.11 | FastAPI, SQLAlchemy 2.0 | Async |
| control-plane-ui | TypeScript | React 18, Vite, Zustand | Node 20 |
| portal | TypeScript | React 18, Vite | Node 20 |
| mcp-gateway | Python 3.11 | FastAPI, OPA | Async |
| stoa-gateway | Rust (stable) | Tokio, Axum | Native |
| cli | Python 3.11 | Typer, Rich | Sync |
| e2e | TypeScript | Playwright, BDD | Node 20 |
| landing-api | Python 3.12 | FastAPI | Async |
CI/CD Architectureβ
Path-Based Triggersβ
Each component has its own CI workflow triggered by path changes:
# .github/workflows/control-plane-api-ci.yml
on:
push:
paths:
- 'control-plane-api/**'
- 'shared/**' # Shared dependencies
Workflow Matrixβ
| Component | Workflow | Triggers | Actions |
|---|---|---|---|
| control-plane-api | control-plane-api-ci.yml | control-plane-api/** | ruff, mypy, pytest, bandit |
| control-plane-ui | control-plane-ui-ci.yml | control-plane-ui/**, shared/** | eslint, vitest |
| mcp-gateway | mcp-gateway-ci.yml | mcp-gateway/** | ruff, mypy, pytest |
| stoa-gateway | stoa-gateway-ci.yml | stoa-gateway/** | cargo test, clippy |
| portal | portal-ci.yml | portal/**, shared/** | eslint, vitest |
| e2e | e2e-tests.yml | Manual, PR | Playwright BDD |
Reusable Workflowsβ
.github/workflows/
βββ reusable-python-ci.yml # Python linting, testing, coverage
βββ reusable-node-ci.yml # Node linting, testing
βββ reusable-rust-ci.yml # Rust build, test, clippy
βββ reusable-docker-ecr.yml # Docker build, ECR push
βββ reusable-k8s-deploy.yml # Kubernetes deployment
Shared Code Strategyβ
Frontend Shared Componentsβ
shared/
βββ components/
β βββ CommandPalette/ # Global search
β βββ Toast/ # Notifications
β βββ ThemeToggle/ # Dark/light mode
β βββ Breadcrumb/
β βββ ConfirmDialog/
β βββ EmptyState/
β βββ FormWizard/
β βββ Skeleton/
βββ contexts/
βββ ThemeContext.tsx # Theme provider
Both control-plane-ui and portal import from shared/:
// portal/src/App.tsx
import { ThemeProvider } from '../../shared/contexts/ThemeContext';
import { Toast } from '../../shared/components/Toast';
Python Shared Patternsβ
Python components share patterns via copy (not packages) due to different deployment contexts:
- Auth patterns: RBAC decorators, Keycloak integration
- Logging: structlog configuration
- OPA integration: Policy evaluation client
Dependency Managementβ
Python (pyproject.toml per component)β
# control-plane-api/pyproject.toml
[project]
name = "control-plane-api"
requires-python = ">=3.11"
[tool.ruff]
line-length = 120
[tool.pytest.ini_options]
asyncio_mode = "auto"
Node (package.json per component + root)β
// Root package.json β workspace management
{
"workspaces": ["control-plane-ui", "portal", "e2e"],
"devDependencies": {
"commitlint": "^19.0.0",
"husky": "^9.0.0"
}
}
Rust (Cargo.toml per component)β
# stoa-gateway/Cargo.toml
[package]
name = "stoa-gateway"
edition = "2021"
[dependencies]
tokio = { version = "1", features = ["full"] }
axum = "0.7"
Cross-Repo Synchronization Rulesβ
| Rule | Source | Destination |
|---|---|---|
| API changes | stoa/control-plane-api | stoa-docs/docs/api/ |
| Helm charts | stoa/charts/ | stoa-infra/charts/ |
| CRD definitions | stoa-infra/charts/stoa-platform/crds/ | Source of truth |
| CLI commands | stoa/cli/ or stoactl/ | stoa-docs/docs/reference/cli.md |
Consequencesβ
Positiveβ
- Atomic Changes β Cross-component changes are single PRs with unified review
- Shared CI β Reusable workflows reduce maintenance overhead
- Dependency Visibility β Breaking changes are caught at PR time
- Simplified Onboarding β One clone, one setup script
- Coordinated Releases β Release Please manages versioning across components
Negativeβ
- Repository Size β Clone includes all components (~500MB with history)
- CI Complexity β Path-based triggers require careful configuration
- Partial Failures β One broken component can block main branch
- IDE Performance β Large monorepo can slow some IDEs
Mitigationsβ
| Challenge | Mitigation |
|---|---|
| Large clone | Shallow clone for CI (--depth=1) |
| CI complexity | Reusable workflows + clear ownership |
| Partial failures | Required status checks per component |
| IDE performance | Component-specific .vscode/ configs |
Rationale for Satellite Reposβ
stoa-infra (Separate)β
- Security β Terraform state, AWS credentials, Vault secrets
- Access Control β Restricted to platform operators
- Audit Trail β Separate git history for compliance
stoa-docs (Separate)β
- Public Site β docs.gostoa.dev served via Vercel
- Community Contributions β Lower barrier for doc PRs
- Docusaurus Build β Independent build pipeline
stoa-quickstart (Separate)β
- Self-Contained β Users clone without full monorepo
- Docker Compose β Simple local setup
- Versioned β Tags match platform releases
stoactl (Separate)β
- Go Binary β Different build toolchain
- Independent Release β CLI can release faster than platform
- Distribution β Homebrew, apt, direct download
Validationβ
OSS Contributor Personaβ
"Je veux contribuer une feature au portal. Je dois tout cloner?"
Response:
# Repository access granted to beta participants
# Request access: christophe@hlfh.io
git clone --depth=1 <repository-url>
cd stoa/portal
npm install
npm run dev
Full history unnecessary. Path-based CI runs only portal tests.
Enterprise Architect Personaβ
"Comment vous gΓ©rez les dΓ©pendances entre control-plane-api et mcp-gateway?"
Response:
- Both are Python 3.11 with shared patterns (copy, not package)
- API changes require both CI pipelines to pass
- Integration tested via E2E suite
- Shared schemas in
stoa-catalog/
Referencesβ
- ADR-024 β Unified Gateway Architecture
- ADR-031 β CI/CD Reusable Workflows
- CONTRIBUTING.md
- Monorepo vs Polyrepo β Thoughtworks
Standard Marchemalo: A 40-year veteran architect understands in 30 seconds