MartinDrozdik.DDD.Web 0.7.0

dotnet add package MartinDrozdik.DDD.Web --version 0.7.0
                    
NuGet\Install-Package MartinDrozdik.DDD.Web -Version 0.7.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="MartinDrozdik.DDD.Web" Version="0.7.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="MartinDrozdik.DDD.Web" Version="0.7.0" />
                    
Directory.Packages.props
<PackageReference Include="MartinDrozdik.DDD.Web" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add MartinDrozdik.DDD.Web --version 0.7.0
                    
#r "nuget: MartinDrozdik.DDD.Web, 0.7.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package MartinDrozdik.DDD.Web@0.7.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=MartinDrozdik.DDD.Web&version=0.7.0
                    
Install as a Cake Addin
#tool nuget:?package=MartinDrozdik.DDD.Web&version=0.7.0
                    
Install as a Cake Tool

DDD for ASP.NET Core - Web Plumbing That Doesn't Suck

NuGet NuGet Downloads Build & Test .NET License

Opinionated web infrastructure for .NET based on MartinDrozdik.DDD. Includes error handling, logging, telemetry, health checks, and other setup you'll need anyway. Check the demo.

Installation

dotnet add package MartinDrozdik.DDD.Web

Philosophy

Same as the core DDD library.

This package provides basic scaffolding for ASP.NET apps while staying out of your way when you need to do your thing. Everything is optional, but it's tested together.

iT jUsT wOrKs

Quick Start

Bare minimum setup - one liner to get logging, error handling, OpenAPI, health checks, telemetry, and HTTP resilience:

var builder = WebApplication.CreateBuilder(args);
builder.AddAppServices(); // the magic one liner

// ...

var app = builder.Build();
app.UseAppMiddlewares(); // the other magic one liner

// ...

await app.RunAsync();

Done. You've got a production-ready baseline. Now go build your actual features.

You want to use Apire? We got you fam! The OTEL works with Apire out of the box.

All-in-One Setup

The AddAppServices() extension registers:

  • Logging - Structured logging that actually helps you debug but doesn't leak sensitive info in production
  • Error Handling - Converts your DDD exceptions to proper HTTP RFC7807 error responses
  • OpenAPI - Auto-generated API docs (because manually writing swagger is hell)
  • Health Checks - basic /health, /health/live and /health/ready endpoints
  • OpenTelemetry - Metrics, traces, and logs (exports to OTLP when configured via OTEL environment variables)
  • HTTP Resilience - Default policies for HTTP clients
  • Static file path provider - Because you probably need to serve some static files at some point

Don't want all of it? Use the individual extensions instead. I won't judge.

Or just turn them off via settings:

var options = MartinDrozdik.DDD.Web.WebApplicationOptions.Default with
{
    UseStaticFilePathProvider = false,
};
builder.AddAppServices(options);

Modules

What goodies do you want to use? Just call the appropriate extension method:

Options

Leverages IOptions<T> with FluentValidation for configuration validation and automatic binding.

Configured to fail fast if your configuration is invalid. No more "wOrKs On My MaChInE".

public class InvoiceOptions : IValidatedAppOptions<InvoiceOptions>
{
    public static string Section => "App:Invoice"; // Simple binding from config (appsettings etc.)

    // FluentValidation validator, because attributes are for suckers
    public static AbstractValidator<InvoiceOptions> Validator { get; } = new OptionsValidator();

    // Your actual options properties:
    public required int StartingId { get; init; }
    public required string DefaultName { get; init; }
    
    private class OptionsValidator : AbstractValidator<InvoiceOptions>
    {
        public OptionsValidator()
        {
            RuleFor(e => e.StartingId).GreaterThanOrEqualTo(0);
            RuleFor(e => e.DefaultName).NotNull().NotEmpty();
        }
    }
}

// Register it:
builder.Services.AddValidatedAppOptions<InvoiceOptions>();

For simpler options without validation:

public class SimpleOptions : IAppOptions
{
    public static string Section => "App:Simple";
    public required string Value { get; init; }
}

builder.Services.AddAppOptions<SimpleOptions>();

EEE (Ezy Error Ehndling)

Automatic conversion of DDD exceptions to HTTP responses:

  • BusinessRuleValidationException → 400 Bad Request
  • ValidationException (FluentValidation) → 400 Bad Request
  • BusinessRuleException → 500 Internal Server Error (with business details)
  • Anything else → 500 Internal Server Error

In development, you get detailed info. In production, you get clean and safe error messages. The middleware handles it:

builder.Services.AddAppErrorHandling();
//...
app.UseExceptionHandler();

Your domain layer throws exceptions. The middleware translates them. You don't touch HTTP in your business logic.

Database Setup

There is 99 % chance you are using EF core with relational database.

We got you, just use the extension method:

// appsettings.json
{
  "App": {
    "Database": {
      "ConnectionString": "Data Source=app.db"
    }
  }
}
// With DatabaseOptions from config:
builder.AddAppDbContext<YourDbContext>((options, dbBuilder) =>
{
    dbBuilder.UseSqlite(options.ConnectionString);
    // Use SQL Server, PostgreSQL, MySQL, whatever you like.
});

// Or manual configuration:
builder.AddAppDbContext<YourDbContext>(dbBuilder =>
{
    dbBuilder.UseSqlServer(connectionString);
});
  • In development: sensitive data logging and detailed errors.
  • In production: none of that.

Ensure your database exists:

await app.EnsureCreatedDatabaseAsync<YourDbContext>();

Or for you migration folks:

await app.MigrateDatabaseAsync<YourDbContext>();

Health Checks

The most basic liveness and readiness probes. Because Kubernetes will ask:

builder.AddAppHealthChecks();

// Or add custom checks:
builder.AddAppHealthChecks(checks =>
{
    // Add more checks as needed
});

app.MapAppHealthChecks(); // Registers /health/live and /health/ready

Timeouts are configured. Todd says "it just works".

Mediator

Automatic request/response logging for your CQRS handlers:

builder.Services.AddMediator(config =>
{
    var integration = new LoggingPipelineIntegrator()
        .Merge<ValidationPipelineIntegrator>();
    
    config.WithCommand<CreateInvoiceCommand, InvoiceId, CreateInvoiceCommandHandler>(integration);
});

Every command and query gets logged with info.

Telemetry (OpenTelemetry)

Traces, metrics, and logs for ASP.NET Core and HTTP clients:

builder.AddAppOpenTelemetry();

Configure export via environment variables:

OTEL_EXPORTER_OTLP_ENDPOINT=http://your-collector:4317
OTEL_SERVICE_NAME=your-service
OTEL_SERVICE_VERSION=1.0.0

Check the docs with full list of OTEL environment variables.

Health check requests are filtered out of traces because who cares about those.

Reverse Proxy Support

One liner for handling proxied requests (nginx, YARP etc.):

app.IsBehindProxy();

Handles X-Forwarded-* headers.

HTTP Client Resilience

builder.Services.AddHttpClientResilience();

Because the network is unreliable and you know it.

Static File Path Provider

How much time did you spend solving an issue solved by clearing browser cache? Yeah, me too. This path modification with query string versioning is the most basic solution to this problem.

IStaticFilePathProvider provider; // inject it where you need it
provider.PathTo("file.js");

Returns a version depending on the environment:

  • Development: returns "file.js?version={unix-timestamp}" to bust cache on every request.
  • Production: returns "file.js?version=1.2.3" depending on your appsettings. Bust cache when you deploy a new version, but not on every request.
// appsettings.json
{
  "App": {
    "StaticFileVersioning": {
        "Version": "1.2.3"
    }
  }
}

Demo App

Check out the demo project for examples.

It's structured for demo purposes, I would recommend structuring with vertical slices in a real app, but it shows all the features in one place.

It's a simple ASP.NET Core app with a few endpoints, using the mediator for commands and queries, and demonstrating the error handling and telemetry in action. Check it out for examples of how to use the library in a real app...

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on MartinDrozdik.DDD.Web:

Package Downloads
MartinDrozdik.DDD.Testing

A library to support testing of websites using the MartinDrozdik.DDD packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.7.0 264 5/16/2026
0.6.0 142 5/2/2026
0.5.3 173 4/19/2026
0.5.2 118 4/18/2026
0.5.1 128 4/10/2026
0.5.0 144 4/6/2026
0.4.4 122 4/6/2026
0.4.2 113 4/6/2026
0.4.1 134 4/6/2026
0.1.4 159 3/21/2026
0.1.3 175 2/14/2026
0.1.2 108 2/13/2026
0.1.1 113 2/13/2026
0.1.0 116 2/9/2026