architecture-selection
Structured decision framework for choosing a system topology (monolith, modular monolith, microservices), carving module boundaries, classifying subdomains, and assigning an architecture recipe per mo
SKILL.md.claude/skills/architecture-selection/SKILL.md
GitHubArchitecture Selection
Output of this skill: a profile.yml (or a diff) + docs/adr/0001-architecture-baseline.md (or a new ADR).
It never recommends a style without evidence from the forces below.
Phase 1 — Interview (ask only what the codebase/user has not answered)
Group questions; ask at most 5 at a time.
- Business capabilities: list the 5–15 things the system does, in business verbs.
- Rules: which capabilities have rules that are regulated, audited, or costly when wrong?
- Change: which parts change most often, and who requests the changes?
- Integrations: external systems, their volatility and failure modes.
- Scale & ops: users, data volume, peak loads, availability target, deployment constraints.
- Team: number of developers now/in 2 years, ownership split, experience with DDD/CQRS.
- Lifetime: prototype, product, or long-lived line-of-business system?
- Existing code: greenfield or brownfield (then also load
architecture-migration).
Phase 2 — Topology (A1)
Default: modular-monolith for any system with ≥ 3 capabilities; monolith for smaller. Choose microservices only if at least two hold, with evidence:
- independent deployment is required by separate teams with separate release cadence
- materially different scaling or availability profiles per capability, measured or contractual
- hard isolation requirements (security, regulatory, tenancy) that process boundaries solve
- the team already operates distributed systems (observability, messaging, CI per service) Otherwise microservices are recorded as a rejected option with these reasons.
Phase 3 — Boundaries (A2)
- Group capabilities by language (same term, same meaning) and change coupling (change together).
- Each group owns its data. If two groups must write the same table, the boundary is wrong or one is the owner and the other consumes via contract/event.
- Name modules after capabilities (
Invoicing), not entities (InvoiceService) or layers. - Classify each: core (differentiating, complex), supporting (necessary, specific), generic (commodity, often integration or off-the-shelf).
- Check: can each module be described in one sentence without "and"? If not, split or rename.
Phase 4 — Recipe per module
Apply the scoring in architecture-composition §3, then adjust:
- Core subdomain with score < 4 → re-check the rules question; core with no rules is usually misclassified.
- Generic subdomain dominated by an external system →
hexagonal-integration. - Reporting / exports / fiscal declarations (e.g. SAF-T) →
pure-sliceswithtable-module, read-only views. - Team inexperienced with DDD →
sliced-domainbeforeclean-sliced; the migration path is cheap.
Phase 5 — System-wide conventions
Decide once: endpoint placement, dispatch (default direct), error model (default result),
validation, write/read data access defaults, migrations tool, test stack. Each non-default needs one
line of reason in the ADR.
Phase 6 — Record
- Write
profile.ymlwithstatus: draft. - Write the baseline ADR (skill
adr): context = forces; decision = topology, modules table, recipes, conventions; consequences; rejected options. - List architecture tests to generate (skill
architecture-fitness-tests). - Ask the user to review; set
status: activeonly after approval.
Red flags to call out explicitly
- Same recipe for every module → boundaries or scoring were skipped.
- Every module
core→ classification is aspirational, not real. - Modules named after tables or layers.
- Microservices chosen for "scalability" without numbers.
- Clean + DDD + CQRS separate-stores on a supporting module.
See references/decision-record-example.md for a worked example.