Introduction

Enterprise applications are fundamentally different from small or demo projects. They must be secure, scalable, resilient, observable, and easy to evolve over years.

ASP.NET Core provides a solid runtime and framework, but real enterprise readiness comes from choosing the right NuGet packages and placing them correctly inside Clean Architecture.

This article explains the most important .NET NuGet packages, why they are used in real-world systems, and how together they cover nearly 90% of enterprise application requirements.

Typical Layers

Each NuGet package should live only in the layer where it belongs.

1. Entity Framework Core

Layer: Infrastructure
Category: ORM / Data Access

Entity Framework Core (EF Core) is the most widely used ORM in the .NET ecosystem. It allows developers to work with relational databases using strongly typed C# classes instead of raw SQL.

Why enterprises use EF Core

Enterprise best practice

EF Core significantly improves developer productivity while remaining performant when used correctly.

Example: DbContext (Infrastructure)

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options)
        : base(options) { }

    public DbSet<Product> Products => Set<Product>();
}

Entity (Domain)

public class Product
{
    public Guid Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public decimal Price { get; set; }
}

Repository Implementation

public class ProductRepository : IProductRepository
{
    private readonly AppDbContext _context;

    public ProductRepository(AppDbContext context)
    {
        _context = context;
    }

    public async Task AddAsync(Product product)
    {
        _context.Products.Add(product);
        await _context.SaveChangesAsync();
    }
}

2. Dapper

Layer: Infrastructure
Category: Micro ORM / Performance

Dapper is a lightweight, high-performance data access library that maps SQL results directly to objects.

Why enterprises use Dapper

Common enterprise pattern (Very Important)

EF Core for Commands (Writes)
Dapper for Queries (Reads)

This CQRS-based approach gives:

Example: Read Models (Infrastructure)

public class ProductReadRepository
{
    private readonly IDbConnection _connection;

    public ProductReadRepository(IDbConnection connection)
    {
        _connection = connection;
    }

    public async Task<IEnumerable<ProductDto>> GetAllAsync()
    {
        var sql = "SELECT Id, Name, Price FROM Products";
        return await _connection.QueryAsync<ProductDto>(sql);
    }
}

3. MediatR

Layer: Application
Category: Architecture / Decoupling

MediatR implements the Mediator pattern, enabling clean separation between controllers, business logic, and infrastructure.

What problems it solves

Enterprise usage

MediatR is the backbone of Clean Architecture + CQRS in modern ASP.NET Core systems.

Example: Command

public record CreateProductCommand(string Name, decimal Price) : IRequest<Guid>;

Handler

public class CreateProductHandler : IRequestHandler<CreateProductCommand, Guid>
{
    private readonly IProductRepository _repository;

    public CreateProductHandler(IProductRepository repository)
    {
        _repository = repository;
    }

    public async Task<Guid> Handle(CreateProductCommand request, CancellationToken cancellationToken)
    {
        var product = new Product
        {
            Id = Guid.NewGuid(),
            Name = request.Name,
            Price = request.Price
        };

        await _repository.AddAsync(product);
        return product.Id;
    }
}

Controller

[ApiController]
[Route("api/products")]
public class ProductsController : ControllerBase
{
    private readonly IMediator _mediator;

    public ProductsController(IMediator mediator)
    {
        _mediator = mediator;
    }

    [HttpPost]
    public async Task<IActionResult> Create(CreateProductCommand command)
    {
        var id = await _mediator.Send(command);
        return Ok(id);
    }
}

4. FluentValidation

Layer: Application
Category: Validation

FluentValidation provides a clean and expressive way to define validation rules outside controllers.

Why enterprises prefer it

Example

public class CreateProductCommandValidator : AbstractValidator<CreateProductCommand>
{
    public CreateProductCommandValidator()
    {
        RuleFor(x => x.Name)
            .NotEmpty()
            .MaximumLength(100);

        RuleFor(x => x.Price)
            .GreaterThan(0);
    }
}

5. Serilog

Layer: Infrastructure
Category: Logging & Observability

Serilog is a structured logging framework designed for production environments.

Why it matters

Example: Program.cs

Log.Logger = new LoggerConfiguration()
    .Enrich.FromLogContext()
    .WriteTo.Console()
    .WriteTo.File("logs/app-.log", rollingInterval: RollingInterval.Day)
    .CreateLogger();

builder.Host.UseSerilog();

Usage

_logger.LogInformation("Product {ProductId} created successfully", product.Id);

6. Polly

Layer: Infrastructure
Category: Resilience & Fault Tolerance

Polly provides resilience policies for handling transient faults.

Problems it solves

Common policies

Example: HTTP Resilience

var retryPolicy = Policy
    .Handle<HttpRequestException>()
    .WaitAndRetryAsync(3, retry => TimeSpan.FromSeconds(retry));

builder.Services.AddHttpClient<IExternalService, ExternalService>()
    .AddPolicyHandler(retryPolicy);

7. Swashbuckle.AspNetCore (Swagger)

Layer: Presentation
Category: API Documentation

Swagger automatically generates interactive API documentation.

Why enterprises require it

Example

builder.Services.AddSwaggerGen();
app.UseSwagger();
app.UseSwaggerUI();

8. System.Text.Json

Layer: Presentation / Shared
Category: Serialization

System.Text.Json is the default JSON serializer in modern .NET.

Why it is preferred

Example

builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
        options.JsonSerializerOptions.WriteIndented = true;
    });