A comprehensive .NET 9 microservices template implementing Domain-Driven Design (DDD) patterns with modern cloud-native technologies. This template serves as a production-ready starting point for building distributed systems and is designed to become a dotnet template package in the future.
This project is being developed as a future dotnet template that developers can use to quickly scaffold robust microservices architectures. The template will provide:
- Pre-configured microservices with DDD patterns
- Production-ready infrastructure components
- Modern observability and messaging patterns
- Comprehensive testing strategies
- Database schema management with Liquibase
This template implements a microservices architecture using Domain-Driven Design principles with the following core patterns:
Each microservice follows a consistent layered architecture:
DomainName/
โโโ src/
โ โโโ DomainName.Api/ # REST/gRPC endpoints and controllers
โ โโโ DomainName.AppHost/ # .NET Aspire orchestration host
โ โโโ DomainName.BackOffice/ # Background jobs and event handlers
โ โโโ DomainName.BackOffice.Orleans/ # Orleans grains (optional)
โ โโโ DomainName.Contracts/ # Integration events and shared models
โ โโโ DomainName/ # Core domain logic (commands, queries, entities)
โโโ test/
โ โโโ DomainName.Tests/ # Integration and architecture tests
โโโ infra/
โโโ DomainName.Database/ # Liquibase database migrations
- ๐งพ Billing Service - Manages cashiers, invoices, and payment processing
- ๐ Accounting Service - Handles ledgers, business day operations, and financial records
- ๐ง Platform/Operations - Shared infrastructure, extensions, and service defaults
- .NET 9 - Latest framework with C# 13
- ASP.NET Core - Web API and gRPC services
- Minimal APIs - Lightweight HTTP APIs
- WolverineFx - CQRS/Event Sourcing framework
- gRPC - High-performance inter-service communication
- Apache Kafka - Distributed streaming platform
- Protocol Buffers - Efficient serialization
- PostgreSQL - Primary database engine
- Dapper - Lightweight ORM with custom source generators
- Liquibase - Database schema versioning and migration
- .NET Aspire - Cloud-native application orchestration
- Microsoft Orleans - Virtual actor model (for stateful services)
- Service Discovery - Automatic service registration and discovery
- OpenTelemetry - Distributed tracing and metrics
- Serilog - Structured logging
- Health Checks - Service health monitoring
- Orleans Dashboard - Real-time insights into Orleans grains
- xUnit v3 - Unit and integration testing framework
- Testcontainers - Integration testing with real dependencies
- NetArchTest - Architecture compliance testing
- NSubstitute - Mocking framework
- Shouldly - Fluent assertions
- SonarAnalyzer - Static code analysis
- FluentValidation - Input validation with automatic registration
- Docker & Docker Compose - Containerization
- Azure Storage - Cloud storage integration
- HTTP Files - API testing and documentation
Choose your preferred development approach:
The fastest way to get started is using .NET Aspire orchestration for a specific domain.
- .NET 9 SDK
- PostgreSQL running on
localhost:5432
# Clone and run Billing services
git clone <repository-url>
cd templates
cd Billing/src/Billing.AppHost
dotnet run# Clone and run Accounting services
git clone <repository-url>
cd templates
cd Accounting/src/Accounting.AppHost
dotnet runThat's it! ๐ Aspire automatically:
- โ Sets up databases with Liquibase
- โ Starts all domain services (API, BackOffice, Orleans)
- โ Configures service discovery
- โ Provides observability dashboard
Access Points:
- Billing Aspire Dashboard: http://localhost:18100
- Accounting Aspire Dashboard: http://localhost:18120
- Operations Aspire Dashboard: http://localhost:18140
- Domain APIs: Available through respective Aspire dashboards
The system uses a structured port allocation optimized for macOS compatibility:
- Billing: 8100-8119 (20 ports)
- Accounting: 8120-8139 (20 ports)
- Operations: 8140-8159 (20 ports)
XX00: Aspire Resource Service (HTTP)
XX01: Main API (HTTP)
XX02: Main API (gRPC)
XX03: BackOffice (HTTP)
XX04: Orleans (HTTP)
XX10: Aspire Resource Service (HTTPS)
XX11: Main API (HTTPS)
XX13: BackOffice (HTTPS)
XX14: Orleans (HTTPS)
XX19: Documentation (last port of range)
- HTTP: Service base + 10,000 (e.g., 8100 โ 18100)
- HTTPS: Service base + 10,010 (e.g., 8100 โ 18110)
- Billing Service: http://localhost:8101 (API), http://localhost:8119 (Docs)
- Accounting Service: http://localhost:8121 (API), http://localhost:8139 (Docs - reserved)
- Operations Service: http://localhost:8159 (Docs - reserved)
- Shared Services: PostgreSQL (5432), OTLP (4317/4318)
For developers who want full control over the setup process.
- .NET 9 SDK
- PostgreSQL running on
localhost:5432 - Liquibase CLI (for database setup)
- Docker (optional)
git clone <repository-url>
cd templates
dotnet build Operations.slnxSet up databases manually using Liquibase:
Important: The following commands assume your running PosgresSQL DB running on port 5432, has the username
postgresand passwordpassword@, adjust if needed, by providing the CLI params--passwordor--username
# Billing Database
cd Billing/infra/Billing.Database/
liquibase update --defaults-file liquibase.setup.properties
liquibase update --defaults-file liquibase.servicebus.properties
liquibase update
# Accounting Database
cd ../../../Accounting/infra/Accounting.Database/
liquibase update --defaults-file liquibase.setup.properties
liquibase update --defaults-file liquibase.servicebus.properties
liquibase updateFor detailed database instructions, see DATABASE_SETUP.md.
Option A: Individual Services
# Terminal 1 - Billing API
dotnet run --project Billing/src/Billing.Api
# Terminal 2 - Billing BackOffice
dotnet run --project Billing/src/Billing.BackOffice
# Terminal 3 - Accounting API
dotnet run --project Accounting/src/Accounting.Api
# Terminal 4 - Accounting BackOffice
dotnet run --project Accounting/src/Accounting.BackOfficeOption B: Docker Compose
docker compose up --buildOption C: Individual Docker Builds
# Build individual service images
docker build -t billing-api -f Billing/src/Billing.Api/Dockerfile .
docker build -t billing-backoffice -f Billing/src/Billing.BackOffice/Dockerfile .
docker build -t accounting-api -f Accounting/src/Accounting.Api/Dockerfile .
docker build -t accounting-backoffice -f Accounting/src/Accounting.BackOffice/Dockerfile .
# Run individual containers
docker run -p 8101:8080 billing-api
docker run -p 8121:8080 accounting-api- Billing API: http://localhost:8101/scalar
- Accounting API: http://localhost:8121/scalar
- Health Checks: http://localhost:8101/health, http://localhost:8121/health
Test the APIs with the included HTTP files:
# Install REST Client extension in VS Code, then open:
# .http/Billing.Api.http
# .http/Accounting.Api.httpOr use curl:
# Create a cashier
curl -X POST http://localhost:8101/cashiers \
-H "Content-Type: application/json" \
-d '{"name": "Test Cashier", "currencyCode": "USD"}'
# Create a ledger
curl -X POST http://localhost:8121/ledgers \
-H "Content-Type: application/json" \
-d '{"name": "Test Ledger", "type": "Asset"}'# Build entire solution
dotnet build Operations.slnx
# Run all tests
dotnet test
# Run specific service tests
dotnet test Billing/test/Billing.Tests
dotnet test Accounting/test/Accounting.Tests# Build all service images
docker build -t billing-api -f Billing/src/Billing.Api/Dockerfile .
docker build -t billing-backoffice -f Billing/src/Billing.BackOffice/Dockerfile .
docker build -t accounting-api -f Accounting/src/Accounting.Api/Dockerfile .
docker build -t accounting-backoffice -f Accounting/src/Accounting.BackOffice/Dockerfile .
# Run with Docker Compose (full stack)
docker-compose up --build
# Run individual containers
docker run -p 8101:8080 --name billing-api billing-api
docker run -p 8103:8080 --name billing-backoffice billing-backoffice
docker run -p 8121:8080 --name accounting-api accounting-api
docker run -p 8123:8080 --name accounting-backoffice accounting-backoffice# Check migration status
liquibase status
# View migration history
liquibase history
# Rollback last migration
liquibase rollback-count 1The template includes powerful source generators for reducing boilerplate:
Automatically generates Dapper command handlers and parameter mappers:
[DbCommand(sp: "create_cashier", nonQuery: true)]
public partial record CreateCashierCommand(string Name, decimal Balance) : ICommand<int>;
// Generates: DB access methods that executes the stored procedure create_cashier and return the affected records (nonQuery)For detailed documentation, see Platform/src/Operations.Extensions.SourceGenerators/README.md.
Command and Query separation with automatic handler discovery:
// Command
public record CreateLedgerCommand(string Name, LedgerType Type) : ICommand<Guid>;
// Query
public record GetLedgerQuery(Guid Id) : IQuery<Ledger>;
// Handlers auto-discovered and registered
public class CreateLedgerHandler
{
public async Task<Guid> Handle(CreateLedgerCommand command, CancellationToken ct)
{
// Implementation
}
}Type-safe inter-service communication with Protocol Buffers:
service Ledgers {
rpc GetLedger(GetLedgerRequest) returns (GetLedgerResponse);
rpc CreateLedger(CreateLedgerRequest) returns (CreateLedgerResponse);
}Multiple testing layers with real infrastructure:
[Fact]
public async Task CreateCashier_ShouldReturnSuccess()
{
// Integration test with Testcontainers PostgreSQL
await using var app = new BillingApiWebAppFactory();
var client = app.CreateClient();
var response = await client.PostAsJsonAsync("/cashiers", new CreateCashierRequest(...));
response.StatusCode.ShouldBe(HttpStatusCode.Created);
}Production-ready monitoring and tracing:
- Distributed Tracing - OpenTelemetry with automatic instrumentation
- Structured Logging - Serilog with correlation IDs
- Health Checks - Endpoint monitoring with custom checks
- Metrics - Custom metrics with OpenTelemetry
/
โโโ ๐ Operations.slnx # Solution file with organized folders
โโโ ๐ณ compose.yaml # Docker Compose services
โโโ ๐ฆ Directory.Packages.props # Centralized NuGet package management
โโโ ๐๏ธ Directory.Build.props # Shared MSBuild properties
โ
โโโ ๐งพ Billing/ # Billing microservice
โ โโโ docs/ # Project documentation, including code docs
โ โโโ src/ # Source code
โ โโโ test/ # Tests
โ โโโ web/ # UI project
โ โโโ infra/ # Infrastructure (database)
โ
โโโ ๐ Accounting/ # Accounting microservice
โ โโโ src/ # Source code
โ โโโ test/ # Tests
โ โโโ infra/ # Infrastructure (database)
โ
โโโ ๐ง Platform/ # Shared platform components
โโโ src/
โ โโโ Operations.Extensions/ # Custom extensions and utilities
โ โโโ Operations.Extensions.Abstractions/ # Interfaces and attributes
โ โโโ Operations.Extensions.SourceGenerators/ # Code generators
โ โโโ Operations.ServiceDefaults/ # Shared service configuration
โ โโโ Operations.ServiceDefaults.Api/ # API-specific defaults
โ โโโ Operations.AppHost/ # Aspire orchestration
โโโ test/ # Platform tests
The template uses centralized configuration management:
- Directory.Build.props - Shared MSBuild properties and package references
- Directory.Packages.props - Centralized package version management
- Operations.ruleset - Code analysis rules
- .editorconfig - Code style enforcement
Services support multiple configuration sources:
- appsettings.json - Base configuration
- appsettings.Development.json - Development overrides
- Environment Variables - Container/cloud deployment
- User Secrets - Local development secrets
Configure source generators globally via MSBuild properties:
<PropertyGroup>
<!-- If using postgres -->
<DbCommandDefaultParamCase>SnakeCase</DbCommandDefaultParamCase>
</PropertyGroup>- Unit Tests - Fast, isolated component tests
- Integration Tests - Service-level tests with real dependencies
- Architecture Tests - Enforce architectural constraints
- Testcontainers - Real PostgreSQL instances for integration tests
- WebApplicationFactory - In-memory test servers
- Custom Test Fixtures - Shared test infrastructure
- Test Data Builders - Consistent test data creation
All services include optimized Dockerfiles:
FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine AS base
# Multi-stage builds for optimal image sizeLiquibase handles database schema evolution:
- Version Control - All schema changes tracked in Git
- Rollback Support - Safe rollback to previous versions
- Environment Promotion - Consistent schema across environments
When this becomes a dotnet template, it will support:
dotnet new microservice-template \
--name "Billing" \
--database "pgsql" \
--messaging "kafka" \- Service Selection - Choose which services to include
- Technology Choices - Alternative databases, messaging systems
- Authentication - JWT, OAuth2, Azure AD integration
This template is designed to incorporate best practices and patterns. Contributions are welcome for:
- New microservice patterns
- Additional source generators
- Enhanced testing strategies
- Cloud deployment templates
- Documentation improvements
- CLAUDE.md - AI assistant development guidance
- DATABASE_SETUP.md - Detailed database setup instructions
- Source Generator Documentation - DbCommand generator guide