| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
A modular monolith reference architecture for .NET teams that want explicit boundaries today and a credible path toward service extraction later, without starting with microservice overhead.
ModularVerticalSlice.NET is both a reference architecture and a starter kit. It does not just show a folder structure. It encodes concrete architectural choices, their trade-offs, and the guardrails that keep those choices stable as the codebase grows.
It is aimed at teams that need:
In concrete terms, that means safer refactors through executable boundary rules, durable messaging without introducing a separate broker up front, and a lower extraction cost because collaboration contracts stay explicit instead of leaking through a shared application core.
The sample domain is event ticket booking: Catalog owns events and ticket inventory, Bookings owns reservation and booking workflows, and Payments shows downstream processing boundaries.
This project starts from classic Vertical Slice Architecture: each use case owns its request path end to end instead of being split across horizontal technical layers.
It then adds two constraints that classic VSA often leaves implicit:
It also keeps the useful parts of Clean Architecture: explicit boundaries, dependency discipline, testability, resilience, and a composition root. What it avoids is the extra ceremony that appears when repository layers, service layers, and abstractions are introduced before the codebase has earned them.
Entity behavior stays on the persisted entity by default. A separate Domain/ folder exists only when a rule, policy, or value object does not belong cleanly to one persisted entity, or when a cross-entity concept deserves its own home.
In practice this is a modular monolith with explicit boundaries, local transactions, and message-based coordination across modules.
This repository is a reference architecture and starter kit, not a reusable framework or NuGet product. Payments and downstream delivery flows are demonstration-grade application slices, useful for showing architecture and reliability patterns rather than claiming production-ready integrations out of the box.
Three common starting points:
src/
ModularVerticalSlice.Application/
Modules/
Bookings/
Features/
Persistence/
Messages/
Catalog/
Contracts/
Payments/
Delivery/
BookingConfirmation/
Shared/
ModularVerticalSlice.Persistence/
ModularVerticalSlice.SharedKernel/
ModularVerticalSlice.WebApi/
tests/
ModularVerticalSlice.UnitTests/
ModularVerticalSlice.IntegrationTests/
ModularVerticalSlice.ArchitectureTests/
The repository provides a disposable PostgreSQL instance through docker-compose.yml:
docker compose up -d postgresDefault local credentials:
The design-time DbContext factory requires an explicit connection string when running EF Core commands:
dotnet tool restore
$env:ConnectionStrings__Database = "Host=localhost;Port=5432;Database=modularverticalslice;Username=postgres;Password=postgres"
dotnet ef database update --project .\src\ModularVerticalSlice.Persistencedotnet run --project .\src\ModularVerticalSlice.WebApiIn Development the API listens on:
The Development profile also enables a fake authenticated user from appsettings.Development.json. That means booking endpoints can be exercised locally without wiring a real identity provider.
This repository exposes OpenAPI JSON in Development, but it does not include a Swagger UI page.
If you want to prove the host is running and then exercise one realistic flow, use this sequence.
curl.exe http://localhost:5211/health/live
curl.exe http://localhost:5211/health/ready/health/live is process-only. /health/ready depends on PostgreSQL connectivity.
curl.exe -X POST "http://localhost:5211/api/v1/events/" `
-H "Content-Type: application/json" `
-d "{\"title\":\"Architecture Demo\",\"date\":\"2030-06-01T20:00:00Z\",\"ticketPrice\":25.0,\"availableTickets\":100}"This returns an EventReadModel payload that includes the generated event id.
In Development, fake authentication is already enabled and provides the bookings.write scope required by the booking endpoint.
curl.exe -X POST "http://localhost:5211/api/v1/bookings/" `
-H "Content-Type: application/json" `
-d "{\"eventId\":\"<event-id>\",\"quantity\":2,\"clientRequestId\":\"<new-guid>\"}"This returns the new booking id.
The booking query endpoints use the same Development fake user and scopes:
curl.exe http://localhost:5211/api/v1/events/
curl.exe http://localhost:5211/api/v1/bookings/
curl.exe http://localhost:5211/api/v1/bookings/<booking-id>The public PowerShell script is the main verification contract:
./scripts/verify.ps1It restores dependencies, builds the solution, and runs unit, architecture, and integration tests.
If PostgreSQL is not already running, the script can start the repository's disposable database container and wait for it:
./scripts/verify.ps1 -StartDatabaseNotes:
Hosted CI is optional automation over the same public contract. Any CI workflow should invoke this script so failures stay locally reproducible.
The repository is meant to demonstrate reliability patterns as concrete runtime capabilities, not just as architectural slogans.
| Need | Capability | Why it matters |
|---|---|---|
| durable publish on commit | transactional outbox | prevents lost messages when application state commits |
| safe message receipt | inbox / durable handling | reduces duplicate or partial downstream processing |
| transient fault recovery | retry policies | absorbs temporary infrastructure failures |
| downstream failure containment | circuit breaker | avoids hammering a failing dependency |
| workflow coordination | sagas | makes long-running, multi-step flows explicit |
| delayed follow-up work | scheduled messages | supports timeout and reminder behavior |
| failure inspection | dead-letter queue | preserves failed work for diagnosis and recovery |
Structural boundaries are enforced by ModularVerticalSlice.ArchitectureTests using NetArchTest.
Rules include:
These rules are meant to fail builds when violated. They are not advisory.
Wolverine is both mediator and message bus. Outbox, inbox, retry, circuit breaker, sagas, scheduled messages, and dead-letter queues come from one library instead of being assembled from separate mediator and transport layers.
Queries live in handlers or slice-specific extension methods. Each module declares a DbContextSlice interface exposing only the DbSet<T> or IQueryable<T> it needs. Generic repository abstractions such as GetById and GetAll are intentionally excluded.
Business failures such as NotFound, Conflict, and Validation are modeled as return values, not control-flow exceptions. HTTP mapping is explicit.
This project does not require a separate broker to get outbox, inbox, retry, and dead-letter behavior. Wolverine plus PostgreSQL is the default durable transport. RabbitMQ, Kafka, or Azure Service Bus become relevant only when throughput or topology needs justify them.
ModularVerticalSlice.SharedKernel contains only Result<T>, Error, and ErrorType. A shared kernel that grows without discipline becomes the next monolith.
DbContextSlice adapts the bounded-DbContext idea to a modular monolith without paying the infrastructure cost of separate DbContexts for every module.
The important distinction is that many slices share one AppDbContext instance. That means:
A handler can only see the tables declared in its slice. Cross-module reads must therefore be explicit. In this solution, GetBookingDetails and GetCustomerBookings use IBookingCatalogReadDbContextSlice to compose Booking and Catalog data in one query without falling back to a global DbContext dependency.
For the full rationale, see ADR-0026.
The host exposes a small operational surface that matches the actual runtime.
Because Wolverine durable messaging shares the same PostgreSQL dependency, the readiness signal is meaningful for the running application.
Design decisions with rationale are recorded in docs/adr/.
This repository is released under the MIT License.
ModularVerticalSlice is actively maintained by Antonio Angiò. Contributions are welcome through pull requests.
| Back | FazBrowse Home | New Git URL |