Vertical Slice Architecture
Quick Reference: The 5 Rules
- One feature = one directory containing handler, request/response types, validation, and tests
- One entry point per feature — a setup/registration function that receives the router and dependencies. Name varies by convention (
Setup, RegisterRoute, Map); the role is the invariant, not the name.
- Minimize coupling between slices, maximize coupling within a slice
- No premature abstractions — no shared repository/service layers until genuine duplication emerges across multiple slices
- Test each feature primarily through its entry point, verifying outcomes (DB state, API calls, response). Platform/adapter tests are also encouraged.
Project Structure
{project}/
features/ # or internal/features/ (Go), Features/ (.NET)
{domain}/ # orders/, users/, kvs/
{operation}/ # create/, list/, delete/
handler # Single entry point + orchestration
request/response # DTOs
validator # Input validation (optional)
test # Co-located integration test
internal/ # Feature-private helpers (optional)
platform/ # or Infrastructure/ — shared cross-cutting concerns
middleware/ # Auth, error handling, idempotency
database/ # Connection pooling, circuit breakers
observability/ # Metrics, tracing, structured logging
opqueue/ # Operation queues, outbox patterns (if needed)
main # Composition root wires features + infrastructure
Workflow: Adding a New Feature
- Create directory:
features/{domain}/{operation}/
- Define the handler with a single exported setup function
- Define request/response types (inline for simple cases)
- Add validation logic
- Register in the composition root (
main)
- Write integration test that calls the setup function, sends request, verifies outcomes
Workflow: Adding Cross-Cutting Concerns
Place in platform/ (not inside a feature). Examples:
- Auth middleware, error handling, request logging
- Database connection pooling, circuit breakers
- Idempotency middleware, operation queues, event notifications
- Observability (metrics, tracing, structured logging)
Workflow: Extracting Shared Logic
Only extract when genuine duplication emerges across multiple slices (use judgment — the "3+ slices" heuristic is guidance, not a hard rule):
- Duplicate business rule: extract to a domain entity/value object
- Duplicate data access pattern: extract to a shared repository (only for that specific pattern)
- Duplicate HTTP helper: extract to
platform/httpx/
Key Decisions by Language
Detect the project's language/framework and consult the appropriate reference:
- Patterns per language: See references/patterns-by-language.md for Go, .NET, Java, TypeScript, Python
- Testing per language: See references/testing.md for testcontainers, mock verification, integration test patterns
- Core principles: See references/principles.md for detailed rules, anti-patterns, and shared domain model guidance
Single Entry Point Contract
Every feature exposes one primary setup/registration function. Internal types stay private. The entry point name is conventional — the invariant is: one public function per feature that wires the slice to the framework.
- Go — convention
Setup or RegisterRoute; signature func Setup(r gin.IRoutes, repo Repository); DI via explicit params.
- .NET — convention
Map (static); signature static void Map(IEndpointRouteBuilder app); DI container resolves deps in handler.
- Java/Kotlin —
@RestController class discovered by component scan; Spring DI (constructor injection).
- TypeScript — convention
setup; signature function setup(router: Router, db: Database): void; DI via explicit params.
- Python — convention
setup; signature def setup(router: APIRouter, db: Database) -> None; DI via explicit params or Depends().
Exceptions: Versioned APIs may have SetupV1/SetupV2 wrappers sharing internal handler wiring. Frameworks with auto-discovery (Spring, NestJS) use the controller/module class itself as the entry point.
Testing
See references/testing.md for full testing strategy per language, including feature integration tests, platform/adapter tests, mock verification patterns, and test naming conventions.
1---2name: vertical-slice-architecture3description: Enforce Vertical Slice Architecture (VSA) when building applications in any language (Go, .NET/C#, Java, Kotlin, TypeScript, Python, etc.) and any type (web API, mobile backend, CLI, event-driven). Organize code by feature/use-case instead of technical layers. Each feature is a self-contained vertical slice with a single entry point that receives the router/framework handle and its dependencies. Use when the user says "vertical slice architecture", "VSA", "organize by feature", "feature-based architecture", "slice architecture", or when building a new app or feature and the project already follows VSA conventions. Also use when reviewing or refactoring code to align with VSA principles.4---56# Vertical Slice Architecture78## Quick Reference: The 5 Rules9101. **One feature = one directory** containing handler, request/response types, validation, and tests112. **One entry point per feature** — a setup/registration function that receives the router and dependencies. Name varies by convention (`Setup`, `RegisterRoute`, `Map`); the role is the invariant, not the name.123. **Minimize coupling between slices, maximize coupling within a slice**134. **No premature abstractions** — no shared repository/service layers until genuine duplication emerges across multiple slices145. **Test each feature primarily through its entry point**, verifying outcomes (DB state, API calls, response). Platform/adapter tests are also encouraged.1516## Project Structure1718```19{project}/20 features/ # or internal/features/ (Go), Features/ (.NET)21 {domain}/ # orders/, users/, kvs/22 {operation}/ # create/, list/, delete/23 handler # Single entry point + orchestration24 request/response # DTOs25 validator # Input validation (optional)26 test # Co-located integration test27 internal/ # Feature-private helpers (optional)28 platform/ # or Infrastructure/ — shared cross-cutting concerns29 middleware/ # Auth, error handling, idempotency30 database/ # Connection pooling, circuit breakers31 observability/ # Metrics, tracing, structured logging32 opqueue/ # Operation queues, outbox patterns (if needed)33 main # Composition root wires features + infrastructure34```3536## Workflow: Adding a New Feature37381. Create directory: `features/{domain}/{operation}/`392. Define the handler with a single exported setup function403. Define request/response types (inline for simple cases)414. Add validation logic425. Register in the composition root (`main`)436. Write integration test that calls the setup function, sends request, verifies outcomes4445## Workflow: Adding Cross-Cutting Concerns4647Place in `platform/` (not inside a feature). Examples:48- Auth middleware, error handling, request logging49- Database connection pooling, circuit breakers50- Idempotency middleware, operation queues, event notifications51- Observability (metrics, tracing, structured logging)5253## Workflow: Extracting Shared Logic5455Only extract when genuine duplication emerges across multiple slices (use judgment — the "3+ slices" heuristic is guidance, not a hard rule):56- Duplicate business rule: extract to a domain entity/value object57- Duplicate data access pattern: extract to a shared repository (only for that specific pattern)58- Duplicate HTTP helper: extract to `platform/httpx/`5960## Key Decisions by Language6162Detect the project's language/framework and consult the appropriate reference:63- **Patterns per language**: See [references/patterns-by-language.md](references/patterns-by-language.md) for Go, .NET, Java, TypeScript, Python64- **Testing per language**: See [references/testing.md](references/testing.md) for testcontainers, mock verification, integration test patterns65- **Core principles**: See [references/principles.md](references/principles.md) for detailed rules, anti-patterns, and shared domain model guidance6667## Single Entry Point Contract6869Every feature exposes one primary setup/registration function. Internal types stay private. The entry point name is conventional — the invariant is: **one public function per feature that wires the slice to the framework**.7071- **Go** — convention `Setup` or `RegisterRoute`; signature `func Setup(r gin.IRoutes, repo Repository)`; DI via explicit params.72- **.NET** — convention `Map` (static); signature `static void Map(IEndpointRouteBuilder app)`; DI container resolves deps in handler.73- **Java/Kotlin** — `@RestController` class discovered by component scan; Spring DI (constructor injection).74- **TypeScript** — convention `setup`; signature `function setup(router: Router, db: Database): void`; DI via explicit params.75- **Python** — convention `setup`; signature `def setup(router: APIRouter, db: Database) -> None`; DI via explicit params or `Depends()`.7677**Exceptions**: Versioned APIs may have `SetupV1`/`SetupV2` wrappers sharing internal handler wiring. Frameworks with auto-discovery (Spring, NestJS) use the controller/module class itself as the entry point.7879## Testing8081See [references/testing.md](references/testing.md) for full testing strategy per language, including feature integration tests, platform/adapter tests, mock verification patterns, and test naming conventions.