New skill files: - clean-code.instructions.md — naming, functions, classes, error handling, formatting, DRY/KISS/SOLID, Helm YAML conventions - clean-architecture.instructions.md — dependency rule, layers, boundaries, SOLID foundation, Helm as outermost layer - helm.instructions.md — resource ownership, values hygiene, required vs defaults, umbrella chart pattern, two-file values layering, KISS principle, hook ordering, config files pattern, dependency caching, template testing Register all three in ai-root-instructions.md skills list and routing table. Remove .ai from .gitignore — .ai/ is the product in this repo and must be tracked; the apply.sh skip-by-marker mechanism prevents changes to other repos.
85 lines
4.4 KiB
Markdown
85 lines
4.4 KiB
Markdown
# Clean Architecture
|
|
|
|
Based on Robert C. Martin's *Clean Architecture*. Apply these principles when designing systems, services, and infrastructure — including Helm chart structure and Kubernetes deployments.
|
|
|
|
---
|
|
|
|
## The Dependency Rule
|
|
|
|
**Source code dependencies point only inward.** Inner layers define interfaces; outer layers implement them.
|
|
|
|
```
|
|
[ Frameworks & Drivers ] ← outermost: UI, DB, APIs, Helm, K8s
|
|
[ Interface Adapters ] ← controllers, presenters, gateways
|
|
[ Use Cases ] ← application business rules
|
|
[ Entities ] ← enterprise business rules (innermost)
|
|
```
|
|
|
|
- Inner layers know nothing about outer layers
|
|
- Data crossing a boundary is always in the form the inner layer prefers — never a raw DB row or HTTP request object
|
|
- If an outer layer changes (swap MySQL for Postgres, REST for gRPC), the inner layers are untouched
|
|
|
|
## Layers
|
|
|
|
### Entities — Core Business Rules
|
|
- Enterprise-wide business objects with their validation and invariants
|
|
- Pure domain models: no framework imports, no DB annotations, no HTTP concepts
|
|
- Stable — change only if fundamental business rules change
|
|
|
|
### Use Cases — Application Business Rules
|
|
- Orchestrate entities to fulfil one actor's goal: `TransferMoneyUseCase`, `ProvisionClusterUseCase`
|
|
- Define input/output via interfaces owned by this layer — no knowledge of controllers or DB implementations
|
|
- One use case = one reason to exist
|
|
|
|
### Interface Adapters
|
|
- Convert data between use cases and the outside world
|
|
- Controllers translate HTTP/CLI input → use case input models
|
|
- Gateways implement repository interfaces defined by use cases
|
|
- Presenters format use case output → UI/API response format
|
|
|
|
### Frameworks & Drivers
|
|
- The outermost ring: web frameworks, ORMs, message brokers, Kubernetes, Helm
|
|
- These are details — they plug into the system via the adapters; the core doesn't know they exist
|
|
|
|
## SOLID as the Foundation
|
|
|
|
- **SRP** — one module, one actor, one reason to change
|
|
- **OCP** — extend behaviour through new implementations, not modification of existing code
|
|
- **LSP** — subtypes must honour the contracts of their interfaces
|
|
- **ISP** — prefer narrow, focused interfaces over fat ones
|
|
- **DIP** — high-level policy depends on abstractions; low-level detail implements them
|
|
|
|
## Boundaries
|
|
|
|
- Boundaries are where the power lives — they isolate components so that one side can change without affecting the other
|
|
- Define boundaries at the most volatile points: DB, UI, external services
|
|
- Cross a boundary only via an interface; never pass a concrete type across
|
|
|
|
## What This Means in Practice
|
|
|
|
- Business rules have no imports from frameworks, ORMs, or infrastructure libraries
|
|
- Tests of use cases and entities need no running database, no HTTP server, no cluster
|
|
- Swapping the database or the delivery mechanism (REST → gRPC, monolith → microservices) touches only the outer rings
|
|
- "Screaming architecture" — the top-level structure should reflect the use cases, not the framework: `invoice/`, `user/`, `payment/` not `controllers/`, `models/`, `views/`
|
|
|
|
---
|
|
|
|
## In Helm and Kubernetes
|
|
|
|
Clean Architecture applies to how infrastructure is organised, not just application code.
|
|
|
|
**Helm charts are the outermost layer — they are the delivery mechanism, not the system.**
|
|
|
|
- A chart should deploy exactly one service or one cohesive unit — not a bundle of unrelated things
|
|
- Business rules belong in application config (`values.yaml` keys mapped to env vars); they must not be hardcoded in templates
|
|
- Separate concerns across charts: one chart for the app, separate charts (or dependencies) for databases, queues, and shared infrastructure
|
|
- A chart change (resource limits, replica count, ingress rules) must never require touching application business logic
|
|
- Use `_helpers.tpl` as the adapter layer — it translates chart-level abstractions into Kubernetes primitives; templates themselves stay thin
|
|
- Environments (`values-dev.yaml`, `values-prod.yaml`) are driver-layer configuration — they override defaults without touching templates
|
|
- Test charts with `helm template` + schema validation; business logic is tested independently of the chart
|
|
|
|
**Dependency Rule for Helm:**
|
|
- App charts depend on library charts, not the reverse
|
|
- Library charts define interfaces (helpers, common labels); app charts implement them
|
|
- Avoid circular chart dependencies — they indicate a missing layer or a misplaced responsibility
|