vsa
Vertical Slice Architecture guardian, two modes. PLACE mode: decide where a new class, event, DTO, or module belongs under VSA rules, or design a new module/slice map from scratch. AUDIT mode: scan fo
Files1
View on GitHubSKILL.mdskills/vsa/SKILL.md
GitHubVSA — Slice Placement & Isolation
The architecture in one breath: group by feature slice, not file type; slices never import each other; modules talk only through database state and events; the scheduler is the only orchestrator.
Default mode is place; the words "audit", "scan", "violations", "leak" select audit.
The Rules (both modes enforce these)
- Slice = one user story. Every class serving that story — controller, service, job, listener — lives in the same folder. Namespace:
App\{Module}\{Slice}\{ClassName}. - No type folders.
Actions/,Jobs/,Listeners/,Http/Controllers/are banned; the moment one appears, slicing has failed. - No cross-slice imports. A slice never
uses or dispatches a class from another slice. - Shared within a module → module root. A class, DTO, or enum used by 2+ slices of the same module moves to
app/{Module}/— never defined in one slice and imported by another. - Events are the seams.
- Slice must trigger a sibling slice → fire a module-internal event at the module root; the receiving slice registers a listener. Never a direct call or job dispatch across the boundary.
- Event consumed by 2+ modules →
App\Shared\Events\. - An event never lives inside a slice folder.
- Modules never import each other's classes. Cross-module effects flow through database state (status fields another module's scheduler picks up) or shared events. Models used by many modules live in
App\Shared\. - The scheduler orchestrates. Module A writes state; the scheduler detects it and triggers Module B. No module calls another.
Mode PLACE
Given a new class, feature, or whole module:
- Name the user story. One story → one slice; two stories → two slices, even if they share a screen.
- Walk the decision ladder: used by one slice → in the slice · by 2+ slices, one module → module root · by 2+ modules →
Shared/· event → rule 5 placement. - For a new module, design the map with aggregate-root thinking before folders: trigger → state written → who consumes it. Deliver a table (slice → story → classes) plus the module's status-field contract (values written, consumed by whom).
- Verify against reality: read the existing tree first; place new code the way its neighbors are placed. If existing structure already violates the rules, flag it — do not imitate a violation.
The rules are stack-agnostic — folder-per-slice with event seams works in any framework; only the namespace idiom changes.
Mode AUDIT
Sweep the module (or whole app) for each violation class, with grep evidence:
- Cross-slice
use/dispatch statements. - Cross-module class imports (anything not via
Shared/or events). - Direct cross-module job dispatches or method calls.
- Events defined inside slice folders.
- DTOs/enums defined in one slice, imported by another.
- Type folders reappearing.
- Orchestration outside the scheduler (module A directly triggering module B).
Output — per violation
file:line → rule broken → the concrete move that fixes it ("move ParsedRowDto to app/Ingestion/, update 3 imports"; "replace the direct dispatch with a CsvUploadParsedEvent at module root + listener in the receiving slice"). Ranked: cross-module coupling first (breaks the architecture), cosmetic placement last. End with the isolation verdict per module: clean / leaking.
Audit is read-only; apply moves only on request, each move followed by a green full suite.