dotnet-cqrs/README.md

11 KiB
Raw Blame History

This project was originally initiated by Powered Software Inc. and was forked from the PoweredSoft.CQRS Repository

CQRS

Our implementation of query and command responsibility segregation (CQRS).

Getting Started

Install nuget package to your awesome project.

Package Name NuGet NuGet Install
Svrnty.CQRS NuGet dotnet add package Svrnty.CQRS
Svrnty.CQRS.MinimalApi NuGet dotnet add package Svrnty.CQRS.MinimalApi
Svrnty.CQRS.AspNetCore NuGet dotnet add package Svrnty.CQRS.AspNetCore
Svrnty.CQRS.FluentValidation NuGet dotnet add package Svrnty.CQRS.FluentValidation
Svrnty.CQRS.DynamicQuery NuGet dotnet add package Svrnty.CQRS.DynamicQuery
Svrnty.CQRS.DynamicQuery.AspNetCore NuGet dotnet add package Svrnty.CQRS.DynamicQuery.AspNetCore
Svrnty.CQRS.Grpc NuGet dotnet add package Svrnty.CQRS.Grpc

Abstractions Packages.

Package Name NuGet NuGet Install
Svrnty.CQRS.Abstractions NuGet dotnet add package Svrnty.CQRS.Abstractions
Svrnty.CQRS.AspNetCore.Abstractions NuGet dotnet add package Svrnty.CQRS.AspNetCore.Abstractions
Svrnty.CQRS.DynamicQuery.Abstractions NuGet dotnet add package Svrnty.CQRS.AspNetCore.Abstractions
var builder = WebApplication.CreateBuilder(args);

// Register CQRS core services
builder.Services.AddSvrntyCQRS();
builder.Services.AddDefaultCommandDiscovery();
builder.Services.AddDefaultQueryDiscovery();

// Add your commands and queries
AddQueries(builder.Services);
AddCommands(builder.Services);

// Add Swagger (optional)
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

// Map CQRS endpoints - automatically creates routes for all commands and queries
app.MapSvrntyCommands();  // Creates POST /api/command/{commandName} endpoints
app.MapSvrntyQueries();   // Creates POST/GET /api/query/{queryName} endpoints

app.Run();

Sample of startup code for ASP.NET Core MVC (Legacy)

public void ConfigureServices(IServiceCollection services)
{
    // make sure to add your queries and commands before configuring MvcBuilder with .AddSvrntyCommands and .AddSvrntyQueries
    AddQueries(services);
    AddCommands(services);

    // adds the non related to aspnet core features.
    services.AddSvrntyCQRS();

    services
        .AddControllers()
        .AddSvrntyQueries() // adds queries to aspnetcore mvc.(you can make it configurable to load balance only commands on a instance)
        .AddSvrntyCommands() // adds commands to aspnetcore mvc. (you can make it configurable to load balance only commands on a instance)
        .AddFluentValidation();

    services.AddSwaggerGen();
}

Sample of startup code for gRPC

var builder = WebApplication.CreateBuilder(args);

// Register CQRS core services
builder.Services.AddSvrntyCQRS();
builder.Services.AddDefaultCommandDiscovery();
builder.Services.AddDefaultQueryDiscovery();

// Add your commands and queries
AddQueries(builder.Services);
AddCommands(builder.Services);

// Add gRPC support
builder.Services.AddSvrntyCqrsGrpc();

var app = builder.Build();

// Map gRPC endpoints
app.MapSvrntyGrpcCommands();
app.MapSvrntyGrpcQueries();

app.Run();

Important: protobuf-net Requirements for gRPC

To use gRPC, your commands and queries must be annotated with protobuf-net attributes:

using ProtoBuf;

[ProtoContract]
public class CreatePersonCommand
{
    [ProtoMember(1)]
    public string FirstName { get; set; } = string.Empty;

    [ProtoMember(2)]
    public string LastName { get; set; } = string.Empty;

    [ProtoMember(3)]
    public int Age { get; set; }
}

[ProtoContract]
public class Person
{
    [ProtoMember(1)]
    public int Id { get; set; }

    [ProtoMember(2)]
    public string FullName { get; set; } = string.Empty;
}

Notes:

  • Add [ProtoContract] to each command/query/result class
  • Add [ProtoMember(n)] to each property with sequential numbers starting from 1
  • These attributes don't interfere with JSON serialization or FluentValidation
  • You can use both HTTP REST (MinimalApi/MVC) and gRPC simultaneously

Example how to add your queries and commands.

private void AddCommands(IServiceCollection services)
{
    services.AddCommand<CreatePersonCommand, CreatePersonCommandHandler>();
    services.AddTransient<IValidator<CreatePersonCommand>, CreatePersonCommandValidator>();

    services.AddCommand<EchoCommand, string, EchoCommandHandler>();
    services.AddTransient<IValidator<EchoCommand>, EchoCommandValidator>();
}

private void AddQueries(IServiceCollection services)
{
    services.AddQuery<PersonQuery, IQueryable<Person>, PersonQueryHandler>();
}

Fluent Validation

We use fluent validation in all of our projects, but we don't want it to be enforced.

If you install Svrnty.CQRS.FluentValidation you can use this way of registrating your commands.

public void ConfigureServices(IServiceCollection services) 
{
    // without Package.
    services.AddCommand<EchoCommand, string, EchoCommandHandler>();
    services.AddTransient<IValidator<EchoCommand>, EchoCommandValidator>();
}

public void ConfigureServices(IServiceCollection services) 
{
    // with Svrnty.CQRS.FluentValidation package.
    services.AddCommand<EchoCommand, string, EchoCommandHandler, EchoCommandValidator>();
}

2024-2025 Roadmap

Task Description Status
Support .NET 8 Ensure compatibility with .NET 8.
Support .NET 10 Upgrade to .NET 10 with C# 14 language support.
Update FluentValidation Upgrade FluentValidation to version 11.x for .NET 10 compatibility.
Add gRPC Support with protobuf-net Implement gRPC endpoints with binary protobuf serialization for high-performance scenarios.
Create a new demo project as an example Develop a new demo project to serve as an example for users.
New Independent Module for MVC Develop a standalone module, independent of MVC, to enhance framework flexibility.
Implement .NET Native Compilation (AOT) Enable full Ahead-of-Time (AOT) compilation support (blocked by third-party dependencies).
Create a website for the Framework Develop a website to host comprehensive documentation for the framework.
Re-add support for GraphQL Re-integrate support for GraphQL, exploring lightweight solutions.