update readme

This commit is contained in:
Mathias Beaulieu-Duncan 2025-11-02 03:38:10 -05:00
parent 4824c0d31d
commit 6735261f21
Signed by: mathias
GPG Key ID: 1C16CF05BAF9162D

243
README.md
View File

@ -17,6 +17,7 @@ Our implementation of query and command responsibility segregation (CQRS).
| Svrnty.CQRS.DynamicQuery | [![NuGet](https://img.shields.io/nuget/v/Svrnty.CQRS.DynamicQuery.svg?style=flat-square&label=nuget)](https://www.nuget.org/packages/Svrnty.CQRS.DynamicQuery/) | ```dotnet add package Svrnty.CQRS.DynamicQuery ``` | | Svrnty.CQRS.DynamicQuery | [![NuGet](https://img.shields.io/nuget/v/Svrnty.CQRS.DynamicQuery.svg?style=flat-square&label=nuget)](https://www.nuget.org/packages/Svrnty.CQRS.DynamicQuery/) | ```dotnet add package Svrnty.CQRS.DynamicQuery ``` |
| Svrnty.CQRS.DynamicQuery.AspNetCore | [![NuGet](https://img.shields.io/nuget/v/Svrnty.CQRS.DynamicQuery.AspNetCore.svg?style=flat-square&label=nuget)](https://www.nuget.org/packages/Svrnty.CQRS.DynamicQuery.AspNetCore/) | ```dotnet add package Svrnty.CQRS.DynamicQuery.AspNetCore ``` | | Svrnty.CQRS.DynamicQuery.AspNetCore | [![NuGet](https://img.shields.io/nuget/v/Svrnty.CQRS.DynamicQuery.AspNetCore.svg?style=flat-square&label=nuget)](https://www.nuget.org/packages/Svrnty.CQRS.DynamicQuery.AspNetCore/) | ```dotnet add package Svrnty.CQRS.DynamicQuery.AspNetCore ``` |
| Svrnty.CQRS.Grpc | [![NuGet](https://img.shields.io/nuget/v/Svrnty.CQRS.Grpc.svg?style=flat-square&label=nuget)](https://www.nuget.org/packages/Svrnty.CQRS.Grpc/) | ```dotnet add package Svrnty.CQRS.Grpc ``` | | Svrnty.CQRS.Grpc | [![NuGet](https://img.shields.io/nuget/v/Svrnty.CQRS.Grpc.svg?style=flat-square&label=nuget)](https://www.nuget.org/packages/Svrnty.CQRS.Grpc/) | ```dotnet add package Svrnty.CQRS.Grpc ``` |
| Svrnty.CQRS.Grpc.Generators | [![NuGet](https://img.shields.io/nuget/v/Svrnty.CQRS.Grpc.Generators.svg?style=flat-square&label=nuget)](https://www.nuget.org/packages/Svrnty.CQRS.Grpc.Generators/) | ```dotnet add package Svrnty.CQRS.Grpc.Generators ``` |
> Abstractions Packages. > Abstractions Packages.
@ -24,10 +25,108 @@ Our implementation of query and command responsibility segregation (CQRS).
| ---------------------------- |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -----------------------------------------------------: | | ---------------------------- |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -----------------------------------------------------: |
| Svrnty.CQRS.Abstractions | [![NuGet](https://img.shields.io/nuget/v/Svrnty.CQRS.Abstractions.svg?style=flat-square&label=nuget)](https://www.nuget.org/packages/Svrnty.CQRS.Abstractions/) | ```dotnet add package Svrnty.CQRS.Abstractions ``` | | Svrnty.CQRS.Abstractions | [![NuGet](https://img.shields.io/nuget/v/Svrnty.CQRS.Abstractions.svg?style=flat-square&label=nuget)](https://www.nuget.org/packages/Svrnty.CQRS.Abstractions/) | ```dotnet add package Svrnty.CQRS.Abstractions ``` |
| Svrnty.CQRS.AspNetCore.Abstractions | [![NuGet](https://img.shields.io/nuget/v/Svrnty.CQRS.AspNetCore.Abstractions.svg?style=flat-square&label=nuget)](https://www.nuget.org/packages/Svrnty.CQRS.AspNetCore.Abstractions/) | ```dotnet add package Svrnty.CQRS.AspNetCore.Abstractions ``` | | Svrnty.CQRS.AspNetCore.Abstractions | [![NuGet](https://img.shields.io/nuget/v/Svrnty.CQRS.AspNetCore.Abstractions.svg?style=flat-square&label=nuget)](https://www.nuget.org/packages/Svrnty.CQRS.AspNetCore.Abstractions/) | ```dotnet add package Svrnty.CQRS.AspNetCore.Abstractions ``` |
| Svrnty.CQRS.DynamicQuery.Abstractions | [![NuGet](https://img.shields.io/nuget/v/Svrnty.CQRS.DynamicQuery.Abstractions.svg?style=flat-square&label=nuget)](https://www.nuget.org/packages/Svrnty.CQRS.DynamicQuery.Abstractions/) | ```dotnet add package Svrnty.CQRS.AspNetCore.Abstractions ``` | | Svrnty.CQRS.DynamicQuery.Abstractions | [![NuGet](https://img.shields.io/nuget/v/Svrnty.CQRS.DynamicQuery.Abstractions.svg?style=flat-square&label=nuget)](https://www.nuget.org/packages/Svrnty.CQRS.DynamicQuery.Abstractions/) | ```dotnet add package Svrnty.CQRS.DynamicQuery.Abstractions ``` |
| Svrnty.CQRS.Grpc.Abstractions | [![NuGet](https://img.shields.io/nuget/v/Svrnty.CQRS.Grpc.Abstractions.svg?style=flat-square&label=nuget)](https://www.nuget.org/packages/Svrnty.CQRS.Grpc.Abstractions/) | ```dotnet add package Svrnty.CQRS.Grpc.Abstractions ``` |
## Sample of startup code for Minimal API (Recommended) ## Sample of startup code for gRPC (Recommended)
```csharp
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.AddGrpc();
var app = builder.Build();
// Map auto-generated gRPC service implementations
app.MapGrpcService<CommandServiceImpl>();
app.MapGrpcService<QueryServiceImpl>();
// Enable gRPC reflection for tools like grpcurl
app.MapGrpcReflectionService();
app.Run();
```
### Important: gRPC Requirements
The gRPC implementation uses **Grpc.Tools** with `.proto` files and **source generators** for automatic service implementation:
#### 1. Install required packages:
```bash
dotnet add package Grpc.AspNetCore
dotnet add package Grpc.AspNetCore.Server.Reflection
dotnet add package Grpc.StatusProto # For Rich Error Model validation
```
#### 2. Add the source generator as an analyzer:
```bash
dotnet add package Svrnty.CQRS.Grpc.Generators
```
The source generator is automatically configured as an analyzer when installed via NuGet and will generate the gRPC service implementations at compile time.
#### 3. Define your proto files in `Protos/` directory:
```protobuf
syntax = "proto3";
import "google/protobuf/empty.proto";
service CommandService {
rpc AddUser(AddUserCommandRequest) returns (AddUserCommandResponse);
rpc RemoveUser(RemoveUserCommandRequest) returns (google.protobuf.Empty);
}
message AddUserCommandRequest {
string name = 1;
string email = 2;
int32 age = 3;
}
message AddUserCommandResponse {
int32 result = 1;
}
```
#### 4. Define your C# commands matching the proto structure:
```csharp
public record AddUserCommand
{
public required string Name { get; init; }
public required string Email { get; init; }
public int Age { get; init; }
}
public record RemoveUserCommand
{
public int UserId { get; init; }
}
```
**Notes:**
- The source generator automatically creates `CommandServiceImpl` and `QueryServiceImpl` implementations
- Property names in C# commands must match proto field names (case-insensitive)
- FluentValidation is automatically integrated with **Google Rich Error Model** for structured validation errors
- Validation errors return `google.rpc.Status` with `BadRequest` containing `FieldViolations`
- Use `record` types for commands/queries (immutable, value-based equality, more concise)
- No need for protobuf-net attributes
## Sample of startup code for Minimal API (Traditional HTTP)
For traditional HTTP/REST scenarios, you can use the Minimal API approach:
```csharp ```csharp
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@ -60,29 +159,15 @@ app.MapSvrntyQueries(); // Creates POST/GET /api/query/{queryName} endpoints
app.Run(); app.Run();
``` ```
## Sample of startup code for ASP.NET Core MVC (Legacy) **Notes:**
- FluentValidation is automatically integrated with **RFC 7807 Problem Details** for structured validation errors
- Use `record` types for commands/queries (immutable, value-based equality, more concise)
- Supports both POST and GET (for queries) endpoints
- Automatically generates Swagger/OpenAPI documentation
```csharp ## Sample enabling both gRPC and HTTP
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. You can enable both gRPC and traditional HTTP endpoints simultaneously, allowing clients to choose their preferred protocol:
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
```csharp ```csharp
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@ -97,53 +182,38 @@ AddQueries(builder.Services);
AddCommands(builder.Services); AddCommands(builder.Services);
// Add gRPC support // Add gRPC support
builder.Services.AddSvrntyCqrsGrpc(); builder.Services.AddGrpc();
// Add HTTP/REST support with Swagger
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build(); var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
// Map gRPC endpoints // Map gRPC endpoints
app.MapSvrntyGrpcCommands(); app.MapGrpcService<CommandServiceImpl>();
app.MapSvrntyGrpcQueries(); app.MapGrpcService<QueryServiceImpl>();
app.MapGrpcReflectionService();
// Map HTTP/REST endpoints
app.MapSvrntyCommands();
app.MapSvrntyQueries();
app.Run(); app.Run();
``` ```
### Important: protobuf-net Requirements for gRPC **Benefits:**
- Single codebase supports multiple protocols
To use gRPC, your commands and queries must be annotated with protobuf-net attributes: - gRPC for high-performance, low-latency scenarios (microservices, internal APIs)
- HTTP/REST for web browsers, legacy clients, and public APIs
```csharp - Same commands, queries, and validation logic for both protocols
using ProtoBuf; - Swagger UI available for HTTP endpoints, gRPC reflection for gRPC clients
[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. > Example how to add your queries and commands.
@ -165,25 +235,55 @@ private void AddQueries(IServiceCollection services)
# Fluent Validation # Fluent Validation
We use fluent validation in all of our projects, but we don't want it to be enforced. FluentValidation is optional but recommended for command and query validation. The `Svrnty.CQRS.FluentValidation` package provides extension methods to simplify validator registration.
If you install ```Svrnty.CQRS.FluentValidation``` you can use this way of registrating your commands. ## Without Svrnty.CQRS.FluentValidation
You need to register commands and validators separately:
```csharp ```csharp
public void ConfigureServices(IServiceCollection services) using Microsoft.Extensions.DependencyInjection;
using FluentValidation;
using Svrnty.CQRS;
private void AddCommands(IServiceCollection services)
{ {
// without Package. // Register command handler
services.AddCommand<EchoCommand, string, EchoCommandHandler>(); services.AddCommand<EchoCommand, string, EchoCommandHandler>();
// Manually register validator
services.AddTransient<IValidator<EchoCommand>, EchoCommandValidator>(); services.AddTransient<IValidator<EchoCommand>, EchoCommandValidator>();
} }
```
public void ConfigureServices(IServiceCollection services) ## With Svrnty.CQRS.FluentValidation (Recommended)
The package exposes extension method overloads that accept the validator as a generic parameter:
```bash
dotnet add package Svrnty.CQRS.FluentValidation
```
```csharp
using Microsoft.Extensions.DependencyInjection;
using Svrnty.CQRS.FluentValidation; // Extension methods for validator registration
private void AddCommands(IServiceCollection services)
{ {
// with Svrnty.CQRS.FluentValidation package. // Command without result - validator included in generics
services.AddCommand<EchoCommand, string, EchoCommandHandler, EchoCommandValidator>(); services.AddCommand<EchoCommand, string, EchoCommandHandler, EchoCommandValidator>();
// Command with result - validator as last generic parameter
services.AddCommand<CreatePersonCommand, CreatePersonCommandHandler, CreatePersonCommandValidator>();
} }
``` ```
**Benefits:**
- **Single line registration** - Handler and validator registered together
- **Type safety** - Compiler ensures validator matches command type
- **Less boilerplate** - No need for separate `AddTransient<IValidator<T>>()` calls
- **Cleaner code** - Clear intent that validation is part of command pipeline
# 2024-2025 Roadmap # 2024-2025 Roadmap
| Task | Description | Status | | Task | Description | Status |
@ -191,9 +291,6 @@ public void ConfigureServices(IServiceCollection services)
| Support .NET 8 | Ensure compatibility with .NET 8. | ✅ | | Support .NET 8 | Ensure compatibility with .NET 8. | ✅ |
| Support .NET 10 | Upgrade to .NET 10 with C# 14 language support. | ✅ | | Support .NET 10 | Upgrade to .NET 10 with C# 14 language support. | ✅ |
| Update FluentValidation | Upgrade FluentValidation to version 11.x for .NET 10 compatibility. | ✅ | | 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. | ✅ | | Add gRPC Support with source generators | Implement gRPC endpoints with source generators and Google Rich Error Model for validation. | ✅ |
| Create a new demo project as an example | Develop a new demo project to serve as an example for users. | ⬜️ | | Create a demo project (Svrnty.CQRS.Grpc.Sample) | Develop a comprehensive demo project showcasing gRPC and HTTP endpoints. | ✅ |
| 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. | ⬜️ | | 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. | ⬜️ |