Imported from Aurelian1974/LocalTax (
.claude/skills/architecture-migration/SKILL.md). Install upstream withnpx skills add Aurelian1974/LocalTax --skill architecture-migration. Copyright stays with the author.
Architecture Migration
Laws
- No big bang. Every step ships, builds, passes tests, and is revertible on its own.
- Characterize before changing. Lock current behavior with tests (integration-level) before moving code.
- Migrate by use case, not by layer. One endpoint/use case at a time moves to the target recipe.
- New code goes to the target immediately; old code moves when touched or by planned batch.
- Track progress in the profile: module
recipe: layered-legacy+notes: "migrating to <recipe>; N/M use cases done"and an ADR.
M1 — Layered (Controller/Service/Repository) → Vertical slices
- Pick the most-changed use case (git log frequency). Write characterization tests at HTTP level.
- Create
Features/{Feature}/{UseCase}/with endpoint + handler that calls the existing service (strangler facade). - Route traffic to the new endpoint (same route; remove the controller action).
- Inline the service method's logic into the handler; delete it from the service when unused.
- Replace generic repository calls with direct Dapper usage (pure-slices) or an aggregate repository (domain-model).
- Repeat. When a service/repository becomes empty, delete it. Architecture test for the module switches from "legacy allowed" to recipe rules when the last use case moves.
M2 — Anemic model / transaction scripts → Domain model
- Inventory rules scattered in services/handlers/SPs per entity (grep for status checks, validations, calculations).
- Choose the aggregate boundary (skill
ddd-tacticalprocedure). - Encapsulate: make setters private one property at a time; the compiler lists every place that writes → move each write into an intent method.
- Move the rules into those methods; handlers become load → call → save.
- Add aggregate unit tests for each moved rule; delete duplicated checks.
- SP-embedded rules: keep SP as-is behind characterization tests, move the rule to the aggregate, reduce SP to persistence or remove it; never have the rule in both places after the step ends.
M3 — Monolith → Modular monolith
- Discover seams: namespaces, change coupling (files changed together), table clusters by write access, team ownership.
- Draft modules + ownership table (which module writes each table). Conflicts = boundary decisions for the architect.
- Create module projects + Contracts; move code without behavior change (namespace moves only).
- Replace direct cross-module calls with contracts/events, one dependency at a time; add architecture test in "report-only" mode, then enforce per module.
- Split schemas: move tables to module schemas with synonyms/views for transition; remove cross-schema FKs; remove synonyms at contract phase.
M4 — Extract a module into a service
Precondition checklist in modular-monolith §Extraction path. If any item is false, fix that first.
Checkpoint template (per step, in the plan)
STEP: <n> — <use case / component>
BEFORE: <characterization tests: names, green>
CHANGE: <moves and rewrites>
AFTER: <tests green; architecture tests status>
ROLLBACK: <git revert of this step is sufficient | additional DB step>
PROGRESS: <k/N use cases on target recipe>
