Skip to content

About

A production-ready, enterprise-grade REST API built with ASP.NET Core (.NET 10) following Clean Architecture, SOLID principles, CQRS pattern, and Domain-Driven Design (DDD).

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

.NET Enterprise API

.NET

A production-ready, enterprise-grade REST API built with ASP.NET Core (.NET 10) following Clean Architecture, SOLID principles, CQRS pattern, and Domain-Driven Design (DDD).

πŸ“– Table of Contents


🎯 Overview

This project serves as a comprehensive template for building enterprise-grade APIs with modern .NET development practices. It demonstrates how to build a secure, maintainable, and highly scalable REST API using industry-standard patterns and principles

πŸ—οΈ Architecture Patterns

  • Clean Architecture - Separation of concerns with clear boundaries
  • CQRS (Command Query Responsibility Segregation) - Separate read and write operations
  • MediatR - Mediator pattern for decoupled request/response handling
  • SOLID Principles - Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion
  • Domain-Driven Design - Rich domain models with domain events
  • Repository Pattern - Data access abstraction
  • Unit of Work Pattern - Transaction management
  • Result Pattern - Standardized response handling

πŸš€ Key Features

  • CQRS with MediatR - Commands and Queries separation
  • FluentValidation - Automatic request validation pipeline
  • Pipeline Behaviors - Logging, Performance monitoring, Validation
  • Domain Events - Event-driven architecture support
  • JWT Authentication - Secure token-based authentication
  • Role-Based Authorization - Fine-grained access control
  • Global Exception Handling - Centralized error management
  • Request Logging Middleware - Comprehensive request/response logging
  • Swagger/OpenAPI - Interactive API documentation
  • Entity Framework Core - Code-first database approach
  • Multi-Provider Data Access - Choose between Entity Framework, Dapper, or ADO.NET via config
  • Health Checks - Database and API health monitoring at /health
  • Rate Limiting - Fixed window, sliding window, and token bucket policies
  • API Versioning - Query string and header-based version control
  • OpenTelemetry - Distributed tracing and metrics instrumentation
  • Output Caching - Response caching with tag-based invalidation
  • Refresh Tokens - Secure token rotation for long-lived sessions
  • Pagination - Built-in pageNumber and pageSize query parameters

πŸ“¦ Technologies & Packages

  • ASP.NET Core Web API (.NET 10)
  • MediatR (12.4.1) - CQRS implementation
  • FluentValidation (11.11.0) - Request validation
  • Entity Framework Core (10.0.5) - Full ORM with change tracking & migrations
  • Dapper (2.1.72) - Lightweight micro-ORM with raw SQL
  • ADO.NET (Microsoft.Data.SqlClient) - Pure data access, no ORM
  • SQL Server
  • JWT Bearer Authentication + Refresh Tokens
  • BCrypt.Net - Secure password hashing
  • Mapster (7.4.0) - Object mapping
  • Swagger/OpenAPI - API documentation
  • OpenTelemetry (1.15.0) - Distributed tracing & metrics
  • Asp.Versioning (8.1.1) - API version management
  • AspNetCore.HealthChecks.SqlServer (9.0.0) - Database health monitoring

πŸ›οΈ Clean Architecture Structure

1. API Layer (DotnetEnterpriseApi.Api)

Responsibilities:

  • HTTP request/response handling
  • Controllers (thin, delegating to MediatR)
  • Middleware (Exception handling, Request logging)
  • Dependency injection configuration
  • Authentication & Authorization setup
  • Swagger configuration

Key Files:

  • Controllers/ - API endpoints using MediatR
  • Middleware/ - Custom middleware components
  • Program.cs - Application startup and DI configuration

2. Application Layer (DotnetEnterpriseApi.Application)

Responsibilities:

  • CQRS Commands and Queries
  • Command/Query Handlers
  • Validators (FluentValidation)
  • Pipeline Behaviors
  • Application interfaces
  • DTOs and Response models
  • Business logic orchestration

Structure:

Application/
β”œβ”€β”€ Common/
β”‚   β”œβ”€β”€ Behaviours/          # MediatR pipeline behaviors
β”‚   β”œβ”€β”€ Exceptions/          # Custom exceptions
β”‚   β”œβ”€β”€ Interfaces/          # Application contracts
β”‚   └── Models/              # Result pattern, common models
β”œβ”€β”€ Features/
β”‚   β”œβ”€β”€ Authentication/
β”‚   β”‚   β”œβ”€β”€ Commands/        # Register, Login commands
β”‚   β”‚   └── EventHandlers/   # Domain event handlers
β”‚   └── Tasks/
β”‚       β”œβ”€β”€ Commands/        # Create, Update, Delete commands
β”‚       β”œβ”€β”€ Queries/         # GetAll, GetById queries
β”‚       └── EventHandlers/   # Task-related event handlers
└── DependencyInjection.cs   # Application services registration

3. Domain Layer (DotnetEnterpriseApi.Domain)

Responsibilities:

  • Core business entities
  • Domain events
  • Business rules and invariants
  • Value objects
  • Domain interfaces

Structure:

Domain/
β”œβ”€β”€ Common/
β”‚   β”œβ”€β”€ BaseEntity.cs           # Base entity with domain events
β”‚   β”œβ”€β”€ BaseAuditableEntity.cs  # Auditable entity base
β”‚   └── IDomainEvent.cs         # Domain event interface
β”œβ”€β”€ Entities/
β”‚   β”œβ”€β”€ AppUser.cs
β”‚   └── TaskItem.cs
└── Events/
    β”œβ”€β”€ UserRegisteredEvent.cs
    └── TaskCreatedEvent.cs

4. Infrastructure Layer (DotnetEnterpriseApi.Infrastructure)

Responsibilities:

  • Database context implementation
  • Repository implementations (EF Core, Dapper, ADO.NET)
  • Unit of Work implementation
  • Data provider selection via configuration
  • Data persistence

Structure:

Infrastructure/
β”œβ”€β”€ Data/
β”‚   β”œβ”€β”€ AppDbContext.cs            # EF Core DbContext with domain events
β”‚   └── SqlConnectionFactory.cs    # IDbConnection factory (Dapper & ADO.NET)
β”œβ”€β”€ Persistence/
β”‚   β”œβ”€β”€ UnitOfWork.cs              # EF Core transaction management
β”‚   └── DapperUnitOfWork.cs        # Dapper/ADO transaction management
β”œβ”€β”€ Repositories/
β”‚   β”œβ”€β”€ EntityFramework/
β”‚   β”‚   β”œβ”€β”€ EfTaskRepository.cs    # EF Core implementation
β”‚   β”‚   └── EfUserRepository.cs
β”‚   β”œβ”€β”€ Dapper/
β”‚   β”‚   β”œβ”€β”€ DapperTaskRepository.cs # Dapper implementation
β”‚   β”‚   └── DapperUserRepository.cs
β”‚   └── Ado/
β”‚       β”œβ”€β”€ AdoTaskRepository.cs    # Pure ADO.NET implementation
β”‚       └── AdoUserRepository.cs
β”œβ”€β”€ Migrations/
└── DependencyInjection.cs         # Provider selection & DI registration

πŸ”„ Dependency Flow

API Layer β†’ Application Layer β†’ Domain Layer
Infrastructure Layer β†’ Application Layer + Domain Layer

Key Principle: Dependencies point inward. The Domain layer has no dependencies. The Application layer depends only on Domain. Infrastructure implements Application interfaces.


πŸ”Œ Data Provider Selection

This project supports three data access strategies. You choose which one to use by setting a single value in appsettings.json β€” no code changes required.

How to Switch

Edit DotnetEnterpriseApi.Api/appsettings.json:

{
  "DataProvider": "EntityFramework"
}
Value Description Best For
EntityFramework EF Core with DbContext, change tracking, LINQ, migrations Rapid development, complex queries, automatic schema management
Dapper Micro-ORM with raw SQL and automatic object mapping High-performance reads, fine-tuned SQL control
Ado Pure ADO.NET with SqlCommand/SqlDataReader, no ORM Maximum control, zero abstraction overhead

How It Works

appsettings.json ["DataProvider"]
        β”‚
        β–Ό
DependencyInjection.cs (Infrastructure)
        β”‚
        β”œβ”€β”€ "EntityFramework"
        β”‚       β”œβ”€β”€ AppDbContext (EF Core)
        β”‚       β”œβ”€β”€ UnitOfWork (EF transactions)
        β”‚       β”œβ”€β”€ EfTaskRepository
        β”‚       └── EfUserRepository
        β”‚
        β”œβ”€β”€ "Dapper"
        β”‚       β”œβ”€β”€ SqlConnectionFactory (IDbConnection)
        β”‚       β”œβ”€β”€ DapperUnitOfWork
        β”‚       β”œβ”€β”€ DapperTaskRepository
        β”‚       └── DapperUserRepository
        β”‚
        └── "Ado"
                β”œβ”€β”€ SqlConnectionFactory (IDbConnection)
                β”œβ”€β”€ DapperUnitOfWork
                β”œβ”€β”€ AdoTaskRepository
                └── AdoUserRepository

All three providers implement the same interfaces (ITaskRepository, IUserRepository, IUnitOfWork), so the Application and API layers remain unchanged regardless of which provider is active.

Request Flow (Same for All Providers)

HTTP Request
  β”‚
  β–Ό
Controller (TasksController / AuthController)
  β”‚
  β–Ό
MediatR.Send(Command or Query)
  β”‚
  β–Ό
Handler (uses ITaskRepository / IUserRepository)
  β”‚
  β–Ό
Repository Implementation (EF / Dapper / ADO β€” selected at startup)
  β”‚
  β–Ό
SQL Server Database
  β”‚
  β–Ό
Result<T> flows back up through the same chain
  β”‚
  β–Ό
HTTP Response (JSON)

Provider Comparison

Feature EntityFramework Dapper ADO.NET
Change Tracking Yes No No
LINQ Support Yes No No
Raw SQL Optional Yes Yes
Object Mapping Automatic Automatic Manual
Migrations Yes No No
Performance Good Excellent Excellent
Code Verbosity Low Medium High
Learning Curve Medium Low Low

Example: Same Operation, Three Implementations

Get Task by ID

Entity Framework:

return await _context.Tasks.FindAsync(id);

Dapper:

const string sql = "SELECT Id, Title, Description, IsCompleted, CreatedDate FROM Tasks WHERE Id = @Id";
using var connection = _connectionFactory.CreateConnection();
return await connection.QueryFirstOrDefaultAsync<TaskItem>(sql, new { Id = id });

ADO.NET:

const string sql = "SELECT Id, Title, Description, IsCompleted, CreatedDate FROM Tasks WHERE Id = @Id";
using var connection = (SqlConnection)_connectionFactory.CreateConnection();
await connection.OpenAsync();
using var command = new SqlCommand(sql, connection);
command.Parameters.AddWithValue("@Id", id);
using var reader = await command.ExecuteReaderAsync();
return await reader.ReadAsync() ? MapTaskItem(reader) : null;

🏒 Enterprise Features

All features below are built-in and ready to use out of the box. No extra setup required.

Health Checks

Endpoint: GET /health

Returns JSON with API and database health status:

{
  "status": "Healthy",
  "checks": [
    {
      "name": "sqlserver",
      "status": "Healthy",
      "duration": "184ms"
    }
  ],
  "totalDuration": "188ms"
}

Rate Limiting

Protects your API from abuse. Three built-in policies:

Policy Type Limit Window
fixed Fixed Window 100 requests 1 minute
sliding Sliding Window 50 requests 1 minute
token Token Bucket 100 tokens, 10/10s refill Continuous

A global limiter (200 req/min per user/IP) is applied to all endpoints automatically. When the limit is exceeded, the API returns 429 Too Many Requests.

API Versioning

Supports two methods simultaneously:

# Query string
GET /api/tasks?api-version=1.0

# Header
GET /api/tasks
X-Api-Version: 1.0

Response headers include api-supported-versions to tell clients which versions are available.

OpenTelemetry Observability

Built-in distributed tracing and metrics:

  • Traces - Every HTTP request is traced with span context
  • Metrics - ASP.NET Core and HTTP client metrics collected automatically
  • Console Exporter - Outputs to console by default (replace with Jaeger, Zipkin, OTLP, or Application Insights for production)

To switch to a production exporter, replace AddConsoleExporter() in Program.cs with your preferred exporter package.

Output Caching

Response caching reduces database load:

Policy TTL Description
Default 30 seconds Applied to all endpoints
tasks 60 seconds Tag-based, applied to task listing

Cache is automatically invalidated when data changes.

Refresh Tokens

Login now returns both a JWT access token and a refresh token:

{
  "token": "eyJhbG...",
  "refreshToken": "FyuFem...",
  "userName": "john",
  "email": "john@example.com",
  "role": "User"
}

Use POST /api/auth/refresh-token with the refresh token to get new tokens without re-entering credentials. Refresh tokens expire in 7 days and are single-use (revoked after each rotation).

Pagination

All list endpoints support pagination via query parameters:

GET /api/tasks?pageNumber=1&pageSize=10
Parameter Default Description
pageNumber 1 Page number (1-based)
pageSize 10 Items per page

🎯 SOLID Principles Implementation

Single Responsibility Principle (SRP)

  • Each handler handles one command/query
  • Separate validators for each request
  • Dedicated event handlers for domain events

Open/Closed Principle (OCP)

  • Pipeline behaviors extend functionality without modifying existing code
  • Base classes allow extension through inheritance

Liskov Substitution Principle (LSP)

  • Interface-based design allows substitution
  • Repository pattern enables different implementations

Interface Segregation Principle (ISP)

  • Small, focused interfaces (IApplicationDbContext, IUnitOfWork)
  • Clients depend only on interfaces they use

Dependency Inversion Principle (DIP)

  • High-level modules depend on abstractions
  • Dependency injection throughout
  • Infrastructure implements Application interfaces


πŸ“‹ CQRS Pattern Implementation

Commands (Write Operations)

Commands modify state and return a Result object:

Example: Register User Command

// Command
public class RegisterCommand : IRequest<Result<RegisterResponse>>
{
    public string UserName { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
}

// Handler
public class RegisterCommandHandler : IRequestHandler<RegisterCommand, Result<RegisterResponse>>
{
    // Implementation with validation, business logic, and persistence
}

// Validator
public class RegisterCommandValidator : AbstractValidator<RegisterCommand>
{
    // FluentValidation rules
}

Queries (Read Operations)

Queries retrieve data without modifying state:

Example: Get All Tasks Query

// Query
public class GetAllTasksQuery : IRequest<Result<List<TaskResponse>>>
{
}

// Handler
public class GetAllTasksQueryHandler : IRequestHandler<GetAllTasksQuery, Result<List<TaskResponse>>>
{
    // Implementation to retrieve and return data
}

Pipeline Behaviors

Automatic cross-cutting concerns applied to all requests:

  1. ValidationBehaviour - Validates requests using FluentValidation
  2. LoggingBehaviour - Logs request execution
  3. PerformanceBehaviour - Monitors long-running requests (>500ms)

πŸ” Authentication Flow

  1. User registers with email and password
  2. Password is securely hashed using BCrypt
  3. User logs in using credentials
  4. API generates a JWT token with claims
  5. Client sends JWT token in Authorization header
  6. Protected endpoints validate the token
  7. Role-based authorization controls access

Example Authorization Header:

Authorization: Bearer {your_jwt_token}

🌐 API Endpoints

Authentication Endpoints

POST /api/auth/register

  • Register a new user
  • Request body: { "userName": "string", "email": "string", "password": "string" }
  • Response: Result<RegisterResponse>

POST /api/auth/login

  • Login and receive JWT token
  • Request body: { "email": "string", "password": "string" }
  • Response: Result<LoginResponse> with JWT + Refresh Token

POST /api/auth/refresh-token

  • Exchange a refresh token for new JWT + refresh token pair
  • Request body: { "token": "string" }
  • Response: Result<LoginResponse> with new tokens

Health & Monitoring Endpoints

GET /health

  • Returns API and database health status (no auth required)

Task Management Endpoints (Requires Authentication)

GET /api/tasks?pageNumber=1&pageSize=10

  • Get all tasks with pagination
  • Response: Result<List<TaskResponse>>

GET /api/tasks/{id}

  • Get task by ID
  • Response: Result<TaskResponse>

POST /api/tasks

  • Create new task
  • Request body: { "title": "string", "description": "string" }
  • Response: Result<TaskResponse>

PUT /api/tasks/{id}

  • Update task
  • Request body: { "title": "string", "description": "string", "isCompleted": boolean }
  • Response: Result<TaskResponse>

DELETE /api/tasks/{id} (Admin only)

  • Delete task
  • Response: Result


οΏ½ Getting Started

Prerequisites

Installation

  1. Clone the repository

    git clone https://github.com/aadityamp01/dotnet-enterprise-api.git
    cd dotnet-enterprise-api
  2. Update database connection string

    Edit DotnetEnterpriseApi.Api/appsettings.json:

    "ConnectionStrings": {
      "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=DotNetEnterpriseApiDb;Trusted_Connection=True;TrustServerCertificate=True;"
    }
  3. Update JWT settings

    Edit DotnetEnterpriseApi.Api/appsettings.json:

    "Jwt": {
      "Key": "YourSuperSecretKeyHere_MinimumLength32Characters!",
      "Issuer": "DotNetEnterpriseAPI",
      "Audience": "DotNetEnterpriseAPIUsers"
    }
  4. Apply database migrations

    cd DotnetEnterpriseApi.Api
    dotnet ef database update
  5. Run the application

    dotnet run
  6. Access Swagger UI

    Navigate to: https://localhost:5001/swagger


πŸ“š API Documentation

Authentication Endpoints

Register User

POST /api/auth/register
Content-Type: application/json

{
  "userName": "john_doe",
  "email": "john@example.com",
  "password": "SecurePass123"
}

Response:

{
  "isSuccess": true,
  "message": "User registered successfully",
  "data": {
    "id": 1,
    "userName": "john_doe",
    "email": "john@example.com"
  },
  "errors": []
}

Login

POST /api/auth/login
Content-Type: application/json

{
  "email": "john@example.com",
  "password": "SecurePass123"
}

Response:

{
  "isSuccess": true,
  "message": "Login successful",
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "userName": "john_doe",
    "email": "john@example.com",
    "role": "User"
  },
  "errors": []
}

Task Management Endpoints

All task endpoints require authentication. Include the JWT token in the Authorization header:

Authorization: Bearer {your_jwt_token}

Get All Tasks

GET /api/tasks
Authorization: Bearer {token}

Get Task by ID

GET /api/tasks/{id}
Authorization: Bearer {token}

Create Task

POST /api/tasks
Authorization: Bearer {token}
Content-Type: application/json

{
  "title": "Complete project documentation",
  "description": "Write comprehensive README and API docs"
}

Update Task

PUT /api/tasks/{id}
Authorization: Bearer {token}
Content-Type: application/json

{
  "title": "Updated title",
  "description": "Updated description",
  "isCompleted": true
}

Delete Task (Admin Only)

DELETE /api/tasks/{id}
Authorization: Bearer {token}

πŸ”’ Security

  • JWT Authentication - Secure token-based authentication
  • Password Hashing - BCrypt with salt for password security
  • Role-Based Authorization - Fine-grained access control
  • Input Validation - FluentValidation prevents injection attacks
  • HTTPS - Enforced encrypted communication

πŸ§ͺ Testing

# Run unit tests
dotnet test

# Run with coverage
dotnet test /p:CollectCoverage=true

πŸ“ˆ Performance

  • Pipeline Behaviors - Automatic performance monitoring
  • Async/Await - Non-blocking I/O operations
  • CQRS - Optimized read and write operations
  • Logging - Performance tracking for requests >500ms

πŸ™ Acknowledgments

  • Clean Architecture by Robert C. Martin
  • CQRS Pattern
  • Domain-Driven Design by Eric Evans
  • ASP.NET Core Team

Built with ❀️ using Clean Architecture principles

About

A production-ready, enterprise-grade REST API built with ASP.NET Core (.NET 10) following Clean Architecture, SOLID principles, CQRS pattern, and Domain-Driven Design (DDD).

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages