A production-ready .NET 10 boilerplate project following Clean Architecture principles with MongoDB (Azure Cosmos DB) integration.
- Architecture
- Project Structure
- Technology Stack
- Getting Started
- Design Patterns
- Code Conventions
- MongoDB Integration
- Configuration
- API Design
- Testing
- Deployment
- Useful Commands
Boilerplate/
βββ API/ # Presentation Layer
βββ Application/ # Application Layer (Use Cases)
βββ Domain/ # Domain Layer (Business Logic)
βββ Infra/ # Infrastructure Layer (External Services)
Dependency Flow: API β Application β Domain β Infra
Key Principles:
- β Domain is independent (no external dependencies)
- β Application contains business use cases (CQRS with MediatR)
- β Infrastructure implements interfaces from Domain
- β API is thin (controllers delegate to Application layer)
API/
βββ Controllers/
β βββ BaseController.cs # Base controller with TryExecuteAsync pattern
β βββ SampleController.cs # CRUD endpoints
βββ DTOs/
β βββ Request/
β β βββ CreateSampleRequestDto.cs # No Id (generated by DB)
β β βββ GetSampleRequestDto.cs # With string Id
β β βββ UpdateSampleRequestDto.cs # With string Id
β β βββ Validators/ # FluentValidation validators
β βββ Response/
β βββ BaseResponseDto.cs # Generic response wrapper
β βββ *ResponseDto.cs # Specific response DTOs
βββ Exceptions/
β βββ BadRequestException.cs
β βββ DuplicateException.cs
β βββ FormValidationException.cs
βββ appsettings.json # Configuration (secrets in User Secrets)
Application/
βββ Commands/
β βββ CreateSampleCommand.cs
β βββ CreateSampleCommandHandler.cs
β βββ UpdateSampleCommand.cs
β βββ UpdateSampleCommandHandler.cs
βββ Queries/
β βββ SampleQuery.cs
β βββ SampleQueryHandler.cs
βββ Clients/
βββ ISampleClient.cs # Interface for external APIs
Domain/
βββ Models/
β βββ Sample.cs # Domain model (string Id for MongoDB)
βββ Entities/
β βββ BaseEntity.cs # MongoDB base (Id, CreatedAt, UpdatedAt)
β βββ SampleEntity.cs # MongoDB-specific entity
βββ Repositories/
β βββ ISampleRepository.cs # Repository interface
βββ MappingConfiguration/
β βββ SampleToSampleDto.cs # Mapster mappings
β βββ SampleEntityMapping.cs # Entity β Model mapping
βββ Enums/
βββ ErrorType.cs
Infra/
βββ Repositories/
β βββ SampleRepository.cs # MongoDB implementation
βββ Clients/
β βββ SampleClient.cs # External API client
βββ Settings/
β βββ MongoDbSettings.cs
β βββ SampleConfiguration.cs
βββ Extensions/
β βββ UrlExtensions.cs
β βββ JsonExtension.cs
βββ Infrastructure.cs # DI registration
| Component | Technology | Version |
|---|---|---|
| Framework | .NET | 10 |
| Language | C# | 14.0 |
| Database | Azure Cosmos DB | MongoDB API |
| Driver | MongoDB.Driver | Latest |
| Mediator | MediatR | Latest |
| Mapping | Mapster | Latest |
| Validation | FluentValidation | Latest |
| Logging | Serilog | Latest |
| Testing | xUnit + Moq | Latest |
- .NET 10 SDK
- Azure Cosmos DB account (MongoDB API) or local MongoDB
- Visual Studio 2022 / VS Code / Rider
git clone https://github.com/throwExceptions/thenorsound.git
cd thenorsound/Boilerplatedotnet restorecd API
dotnet user-secrets init
dotnet user-secrets set "MongoDbSettings:ConnectionString" "mongodb+srv://user:password@..."
dotnet user-secrets set "MongoDbSettings:DatabaseName" "boilerplate-mongodb"
dotnet user-secrets set "MongoDbSettings:SampleCollectionName" "Samples"dotnet run --project APIhttps://localhost:5001
Commands (Write operations):
CreateSampleCommandβCreateSampleCommandHandlerUpdateSampleCommandβUpdateSampleCommandHandler
Queries (Read operations):
SampleQueryβSampleQueryHandler
- Domain defines
ISampleRepositoryinterface - Infrastructure implements
SampleRepositorywith MongoDB logic - Application uses repository through DI
Using Primary Constructors (C# 12+):
public class SampleController(IMediator mediator, ILogger logger)
{
public async Task<IActionResult> Create(CreateSampleRequestDto request)
{
var command = new CreateSampleCommand(request);
var result = await mediator.Send(command);
return Ok(result);
}
}// β
Primary Constructors
public class SampleController(IMediator mediator, ILogger logger)
// β
File-scoped namespaces
namespace API.Controllers;
// β
Expression-bodied members
public DateTime? UpdatedAt { get => _updatedAt; set => _updatedAt = value; }
// β
Using statements inside namespace (StyleCop SA1200)
namespace API.Controllers;
using System;
using MediatR;| Type | Convention | Example |
|---|---|---|
| Classes | PascalCase | SampleController |
| Interfaces | IPascalCase | ISampleRepository |
| Methods | PascalCase | GetByIdAsync |
| Properties | PascalCase | ConnectionString |
| Private fields | _camelCase | _samples |
| Parameters | camelCase | request |
| Async methods | SuffixAsync | CreateAsync |
File Header (SA1633): Include license headers in all source files
// Copyright (c) ThenorSound. All rights reserved.
// Licensed under the MIT License.
namespace API.Controllers;public abstract class BaseEntity
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
[BsonElement("createdAt")]
public DateTime CreatedAt { get; set; }
[BsonElement("updatedAt")]
public DateTime? UpdatedAt { get; set; }
}- Type:
string - Format: 24 hexadecimal characters (e.g.,
507f1f77bcf86cd799439011) - Validation:
.Length(24).Matches("^[a-f0-9]{24}$")
Important Rules:
CreatedAtis set automatically when entity is first saved (lazy getter)UpdatedAtis set explicitly in Repository'sUpdateAsyncmethod- ObjectId is always a
stringin this architecture
Never commit secrets to Git!
cd API
dotnet user-secrets init
dotnet user-secrets set "MongoDbSettings:ConnectionString" "mongodb+srv://user:password@..."
dotnet user-secrets set "MongoDbSettings:DatabaseName" "boilerplate-mongodb"
dotnet user-secrets set "MongoDbSettings:SampleCollectionName" "Samples"In Azure App Service β Configuration β Application settings:
MongoDbSettings__ConnectionString = mongodb+srv://...
MongoDbSettings__DatabaseName = boilerplate-mongodb
MongoDbSettings__SampleCollectionName = Samples
__ instead of colon : for nested config.
- User Secrets (highest - local development)
- Environment Variables (Azure)
- appsettings.json (lowest - defaults)
| DTO Type | Id Field | Usage |
|---|---|---|
| CreateRequestDto | β No Id | Create new resource |
| GetRequestDto | β string Id | Retrieve resource |
| UpdateRequestDto | β string Id | Update resource |
| ResponseDto | β string Id | Return data |
public class SampleControllerTests
{
[Fact]
public async Task Create_WithValidRequest_ReturnsOk()
{
// Arrange
var mediatorMock = new Mock<IMediator>();
var controller = new SampleController(mediatorMock.Object, Mock.Of<ILogger>());
var request = new CreateSampleRequestDto { Name = "Test" };
// Act
var result = await controller.Create(request);
// Assert
Assert.IsType<OkObjectResult>(result);
}
}- API: MongoDB
- Tier: Free tier (for development)
- Location: North Europe
- Database:
boilerplate-mongodb(auto-created on first write) - Collection:
Samples(auto-created on first write)
- Enable Public Access
- Add your IP address
- Allow Azure services
- Cosmos DB β Connection strings
- Copy "Self (always this cluster)" connection string
az webapp create \
--resource-group rg-boilerplate \
--plan boilerplate-plan \
--name boilerplate-api \
--runtime "DOTNET|10.0"az webapp config appsettings set \
--name boilerplate-api \
--resource-group rg-boilerplate \
--settings \
MongoDbSettings__ConnectionString="mongodb+srv://..." \
MongoDbSettings__DatabaseName="boilerplate-mongodb" \
MongoDbSettings__SampleCollectionName="Samples"dotnet publish -c Release
az webapp deploy \
--resource-group rg-boilerplate \
--name boilerplate-api \
--src-path ./bin/Release/net10.0/publish.zipdotnet builddotnet run --project APIdotnet testdotnet watch --project APIdotnet user-secrets init --project API
dotnet user-secrets set "Key" "Value" --project API
dotnet user-secrets list --project API
dotnet user-secrets clear --project APIdotnet add [Project] package [PackageName]dotnet list package --outdated
dotnet add package [PackageName]Microsoft.AspNetCore.OpenApiSwashbuckle.AspNetCoreFluentValidationSerilog.AspNetCoreSerilog.Sinks.ConsoleSerilog.Sinks.File
MediatRMapster
MongoDB.BsonMapster
MongoDB.DriverMapsterMapsterMapper
public static IServiceCollection AddInfrastructure(
this IServiceCollection services,
IConfiguration configuration)
{
services.Configure<MongoDbSettings>(
configuration.GetSection(nameof(MongoDbSettings)));
services.AddSingleton<IMongoClient>(
new MongoClient(configuration["MongoDbSettings:ConnectionString"]));
services.AddScoped<ISampleRepository, SampleRepository>();
services.AddScoped<ISampleClient, SampleClient>();
return services;
}public static IServiceCollection AddMappings(this IServiceCollection services)
{
TypeAdapterConfig.GlobalSettings
.Scan(typeof(SampleToSampleDto).Assembly);
services.AddScoped<IMapper, ServiceMapper>();
return services;
}- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Follow StyleCop rules (SA*)
- All public members must have XML documentation
- Use primary constructors where possible
- Prefer
this.prefix for all class members - Use file-scoped namespaces
- Use expression-bodied members when appropriate
This project is licensed under the MIT License - see the LICENSE file for details.
- Clean Architecture by Robert C. Martin
- MediatR for CQRS implementation
- Mapster for high-performance object mapping
- FluentValidation for validation rules
- MongoDB.Driver for database access
- Serilog for structured logging
Project Link: https://github.com/throwExceptions/thenorsound
- Architecture: Follow Clean Architecture - Domain is the core, never reference infrastructure
- IDs: Always use
stringfor MongoDB ObjectIds (24 hex characters) - Timestamps:
CreatedAtuses lazy getter,UpdatedAtis set explicitly in Repository - DTOs: Create requests don't have Id, Get/Update requests do
- Secrets: Use User Secrets locally, Environment Variables in Azure
- Mapping: Use Mapster with
.Ignore()for auto-managed properties - Validation: FluentValidation for all request DTOs
- Testing: xUnit + Moq for unit tests
- Conventions: Follow StyleCop rules, use primary constructors
Built with β€οΈ using .NET 10 and Clean Architecture