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.
4.4 KiB
4.4 KiB
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/notcontrollers/,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.yamlkeys 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.tplas 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