A comprehensive .NET cloud-native workshop demonstrating modern distributed application patterns using .NET Aspire 13 and a modular monolith architecture.
This project showcases a complete e-learning platform built with cloud-native principles, featuring:
- Modular Monolith Architecture - Domain-driven design with clear bounded contexts
- .NET Aspire Orchestration - Streamlined local development with automatic infrastructure provisioning
- PostgreSQL + Entity Framework Core - Primary relational database with code-first migrations
- Distributed Caching - Redis for performance optimization
- Message-Driven Communication - RabbitMQ for event-driven patterns
- Cloud-Ready Design - Built for containerization and cloud deployment
├── Dometrain.Aspire.AppHost # Aspire orchestration host
├── Dometrain.Aspire.ServiceDefaults # Shared service configuration
└── Dometrain.Monolith.Api # Main API (Modular Monolith)
├── Identity/ # Authentication & authorization
├── Students/ # Student management domain
├── Courses/ # Course catalog domain
├── ShoppingCarts/ # Shopping cart domain
├── Orders/ # Order processing domain
├── Enrollments/ # Course enrollment domain
├── Database/ # EF Core DbContext, configurations, migrations
├── ErrorHandling/ # Centralized exception handling
└── OpenApi/ # Swagger configuration
- .NET 10 - Latest .NET runtime and SDK
- .NET Aspire 13 - Cloud-native orchestration and tooling
- ASP.NET Core Minimal APIs - Lightweight endpoint definitions
- PostgreSQL 17 - Primary relational database (all domains)
- Entity Framework Core - ORM with code-first migrations
- Redis - Distributed caching layer
- RabbitMQ - Message broker for async communication
- FluentValidation - Request validation framework
- OpenTelemetry - Observability (metrics, traces, logs)
- Swashbuckle - OpenAPI/Swagger documentation
All infrastructure is automatically managed by Aspire:
| Component | Purpose | Port | UI |
|---|---|---|---|
| PostgreSQL | All transactional data | 5433 | - |
| Redis | Caching | - | RedisInsight UI |
| RabbitMQ | Message broker | - | Management Plugin UI |
- .NET 10 SDK
- Docker Desktop
- IDE: Visual Studio 2025, VS Code, or Rider
git clone <repository-url>
cd cloud-native-workshop-ndccph-2025dotnet build CloudNativeWorkshop.slnThe recommended way to run the application is through the Aspire AppHost, which automatically starts all infrastructure:
dotnet run --project src/Dometrain.Aspire.AppHostThis will:
- Start PostgreSQL container and create the
dometraindatabase - Start Redis with RedisInsight
- Launch RabbitMQ with Management Plugin
- Start the main API
- Apply EF Core migrations automatically
- Open the Aspire Dashboard
- API: http://localhost:5000
- Swagger UI: http://localhost:5000/swagger
- Aspire Dashboard: Automatically opens in browser
On the first run, the application will:
- Create the PostgreSQL
dometraindatabase - Initialize all database tables
- Create a default admin user:
- Email:
admin@dometrain.com - ID:
005d25b1-bfc8-4391-b349-6cec00d1416c
- Email:
Each domain module follows a consistent subfolder structure:
{Domain}/
├── Models/
│ └── {Domain}.cs # Domain entity models
├── Interfaces/
│ ├── I{Domain}Repository.cs # Repository interface
│ └── I{Domain}Service.cs # Service interface
├── Services/
│ └── {Domain}Service.cs # Business logic implementation
├── Repositories/
│ ├── {Domain}Repository.cs # EF Core data access
│ └── Cached{Domain}Repository.cs # Optional caching decorator
├── Api/
│ ├── {Domain}Endpoints.cs # API endpoint handlers
│ └── {Domain}EndpointExtensions.cs # Route registration
├── Mappers/
│ └── {Domain}Mapper.cs # DTO-to-entity mapping
├── Validators/
│ └── {Domain}Validator.cs # FluentValidation rules
├── Requests/
│ └── {Domain}Requests.cs # Request DTOs
└── {MiscFiles}.cs # Config, extensions at root
Folders are only created when needed. Example - Students module:
Students/
├── Models/Student.cs
├── Interfaces/IStudentRepository.cs, IStudentService.cs
├── Services/StudentService.cs
├── Repositories/StudentRepository.cs
├── Api/StudentEndpoints.cs, StudentEndpointExtensions.cs
├── Mappers/StudentMapper.cs
├── Validators/StudentValidator.cs
└── Requests/StudentRequests.cs
- Minimal APIs - Lightweight endpoint definitions without controllers
- Repository Pattern - Abstraction over data access
- Decorator Pattern - Caching repositories wrap base repositories
- Dependency Injection - Singleton lifetime for most services
- Request/Response DTOs - Clear API contracts
The API uses JWT Bearer token authentication:
# Login to get token
POST /auth/login
{
"email": "admin@dometrain.com",
"password": "YourPassword"
}- Admin - Requires JWT with
is_admin: trueclaim - ApiAdmin - Uses API key from configuration
Use the included Insomnia collection for testing:
# Import the collection
insomnia-latest.yamlThe collection includes:
- Authentication flows
- Student management
- Course operations
- Shopping cart interactions
- Order processing
- Enrollment management
# Run API directly (requires infrastructure running)
dotnet run --project src/Dometrain.Monolith.Api
# Run with Docker Compose (PostgreSQL only)
docker compose up dbThe application uses Entity Framework Core code-first migrations:
# Create a new migration
cd src/Dometrain.Monolith.Api
dotnet ef migrations add MigrationName --output-dir Database/Migrations
# Apply migrations manually (also runs on startup)
dotnet ef database update- Migrations are applied automatically on startup via
context.Database.MigrateAsync() - EF Core configurations are in
Database/Configurations/ - No manual migration steps required for development
- Create domain folder under
Dometrain.Monolith.Api/with subfolders - Add
Models/,Interfaces/,Services/,Repositories/,Api/subfolders - Implement entity models, repository, service, and endpoints
- Add EF Core configuration in
Database/Configurations/ - Register DbSet in
Database/DometrainDbContext.cs - Register services and endpoints in
Program.cs - Add validation rules in
Validators/ - Create EF Core migration for schema changes
Located in src/Dometrain.Aspire.AppHost/appsettings.json:
- Infrastructure connection strings (auto-generated)
- Service discovery configuration
- Telemetry settings
Located in src/Dometrain.Monolith.Api/appsettings.json:
{
"Identity": {
"Key": "your-secret-key",
"Issuer": "Dometrain",
"Audience": "Dometrain.Api",
"Lifetime": "08:00:00",
"AdminApiKey": "your-admin-key"
}
}The application includes comprehensive observability via OpenTelemetry:
- Request counts and duration
- Database query performance
- Cache hit/miss ratios
- Message processing metrics
- Distributed tracing across services
- Database query tracing
- HTTP request tracing
- Structured logging with correlation IDs
- Integration with Aspire Dashboard
# Prometheus metrics
http://localhost:9090
# Grafana dashboards
http://localhost:3000
# Aspire Dashboard (built-in)
# Opens automatically on startupIf the database fails to connect:
# Clean up existing containers and volumes
docker stop $(docker ps -q --filter name=main-db) 2>/dev/null || true
docker rm $(docker ps -aq --filter name=main-db) 2>/dev/null || true
docker volume rm $(docker volume ls -q --filter name=main-db) 2>/dev/null || true
# Restart Aspire
dotnet run --project src/Dometrain.Aspire.AppHostIf you encounter build errors:
# Clean and rebuild
dotnet clean
dotnet build --no-incrementalIf ports are already in use, update them in AppHost.cs:
var postgres = builder.AddPostgres("main-db", port: 5433); // Change port hereBuild the API as a container:
docker build -t dometrain-api -f src/Dometrain.Monolith.Api/Dockerfile .Pre-generated Kubernetes manifests are available in:
src/Dometrain.Aspire.AppHost/aspirate-output/
Deploy to Kubernetes:
kubectl apply -k src/Dometrain.Aspire.AppHost/aspirate-output/- Modular Monolith - Domain separation without microservices complexity
- Repository Pattern - Data access abstraction
- Decorator Pattern - Caching layer implementation
- Dependency Injection - Loose coupling and testability
- Health Checks - Application health monitoring
- Configuration Management - External configuration
- Service Discovery - Aspire-managed service resolution
- Distributed Caching - Redis-based caching
- Event-Driven Architecture - RabbitMQ messaging
- Repository Pattern - EF Core data access with IDbContextFactory
- CQRS-lite - Read/write separation in repositories
- Decorator Pattern - Caching repositories wrap base repositories
The application implements a two-tier caching approach:
- L1 Cache - In-memory application cache
- L2 Cache - Distributed Redis cache
Cached entities:
- Courses (reduces database load)
- Shopping carts (improves response time)
- Connection pooling - Managed by Npgsql
- IDbContextFactory - Singleton repositories with short-lived DbContext per operation
- Indexed lookups - On email, slug fields via EF Core configurations
- JWT with symmetric key signing
- Configurable token lifetime
- Password hashing via ASP.NET Core Identity
- Claim-based authorization
- API key authentication for admin operations
- Role-based access control
- Secrets in environment variables (production)
- HTTPS enforcement (production)
- Input validation via FluentValidation
- SQL injection prevention via parameterized queries
This project is for educational purposes as part of the NDC Copenhagen 2025 Cloud-Native Workshop.
- .NET Aspire Documentation
- Minimal APIs Guide
- Cloud-Native Patterns
- Entity Framework Core Documentation
Workshop: Cloud-Native Development with .NET Aspire Event: NDC Copenhagen 2025 Framework: .NET 10 | Aspire 13