Domain type design and architectural planning for Go code. Use when planning new features, designing self-validating types, preventing primitive obsession, or when refactoring reveals need for new...
Backward counterpart (fixing code that already fails lint/review): @refactoring.
| Notation | Skill Tool Call |
|---|---|
| @testing | Skill(go-linter-driven-development:testing) |
Scan the codebase structure: vertical (internal/feature/{handler,service}.go) vs
horizontal (internal/{handlers,services}/feature.go)?
internal/<new-feature>/.docs/architecture/vertical-slice-migration.md, continue as a slice.Architecture advises, it doesn't veto (R5's advisory posture). Ask the user: Option A — vertical slice (recommended); Option B — match the existing pattern (time pressure and team conventions are valid reasons).
Answers shape the plan; they are never findings. Maxims propose, evidence disposes —
the review phases convict only via rules (maxims.md, contract section).
| Rule | When designing, apply... |
|---|---|
../../rules/R1-primitive-obsession.md |
Which primitives become types — score every candidate with R1's juiciness scorecard; reject ceremony wrappers (over-abstraction trap). |
../../rules/R2-self-validating-types.md |
Constructor-only entry, validation ownership, trusting composed values, nil is not a value, no defensive checks in methods. |
../../rules/R3-storifying.md |
Plan orchestration functions as 3–5 named steps at one conceptual level; honest names for mutators. |
../../rules/R4-helper-placement.md |
WHERE each helper/type lands — the placement ladder (unexported → feature sub-package → shared domain package). |
../../rules/R5-vertical-slice.md |
Package structure and naming: feature slices with roles inside, flatcase domain vocabulary, migration template. |
../../rules/R6-test-only-interfaces.md |
Default dependencies to concrete types; an interface must be earned by a second production implementation or a grep-verified import cycle. |
../../rules/R7-test-placement.md |
The test plan per type: leaf types 100% unit coverage via public constructors; orchestrators integration-tested over real collaborators. |
../../rules/R8-no-globals.md |
Dependencies injected via constructors, cancellation passed down from callers, globals only at entry points. |
../../rules/R10-concurrency-safety.md |
Every planned goroutine gets an owner (stop + wait) and an exit path at construction time; shared state designed with its guard on one type — or designed away via handoff/confinement. |
../../rules/R11-conditional-dispatch.md |
How each kind/variant family dispatches: behavior-heavy or open set → interface chosen once at the boundary; single-behavior variance → strategy map; single-site closed enum → one exhaustive switch (named enum per R1). |
../../rules/R12-mutation-discipline.md |
Each type's mutation surface: constructors copy slice/map arguments; queries return copies or iterators, never internal references; no setters around validating constructors; query and modifier as separate methods. |
../../rules/R4-helper-placement.md)../../rules/R6-test-only-interfaces.md); otherwise depend on the concrete type<package_decomposition>)Feature: [Feature Name]
Core Domain Types (leaf):
Orchestrating Types:
Package Structure: [feature]/ ├── [type].go # each juicy type in its own file ├── service.go └── handler.go
Placement Decisions (R4):
Design Decisions:
Integration Points:
Next Steps:
</output_format>
<success_criteria>
Design phase is complete when ALL are true:
- [ ] Architecture pattern analyzed (vertical/horizontal/mixed) and user chose an option
- [ ] Core domain types identified, each with its validation rules and R1 score
- [ ] Placement decision recorded for every new type/helper (R4 ladder)
- [ ] Package structure follows R5 (slices, naming, downward imports)
- [ ] Design checklist answered satisfactorily (every box cites its rule)
- [ ] Design plan presented in the output format above
</success_criteria>