From 6735261f2168d1554adb7bb21ba10c6524963606 Mon Sep 17 00:00:00 2001 From: Mathias Beaulieu-Duncan Date: Sun, 2 Nov 2025 03:38:10 -0500 Subject: [PATCH] update readme --- README.md | 245 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 171 insertions(+), 74 deletions(-) diff --git a/README.md b/README.md index f542246..4a0e385 100644 --- a/README.md +++ b/README.md @@ -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.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.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. @@ -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.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(); +app.MapGrpcService(); + +// 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 var builder = WebApplication.CreateBuilder(args); @@ -60,29 +159,15 @@ app.MapSvrntyQueries(); // Creates POST/GET /api/query/{queryName} endpoints 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 -public void ConfigureServices(IServiceCollection services) -{ - // make sure to add your queries and commands before configuring MvcBuilder with .AddSvrntyCommands and .AddSvrntyQueries - AddQueries(services); - AddCommands(services); +## Sample enabling both gRPC and HTTP - // 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 +You can enable both gRPC and traditional HTTP endpoints simultaneously, allowing clients to choose their preferred protocol: ```csharp var builder = WebApplication.CreateBuilder(args); @@ -97,53 +182,38 @@ AddQueries(builder.Services); AddCommands(builder.Services); // 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(); +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + // Map gRPC endpoints -app.MapSvrntyGrpcCommands(); -app.MapSvrntyGrpcQueries(); +app.MapGrpcService(); +app.MapGrpcService(); +app.MapGrpcReflectionService(); + +// Map HTTP/REST endpoints +app.MapSvrntyCommands(); +app.MapSvrntyQueries(); app.Run(); ``` -### Important: protobuf-net Requirements for gRPC - -To use gRPC, your commands and queries must be annotated with protobuf-net attributes: - -```csharp -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 +**Benefits:** +- Single codebase supports multiple protocols +- gRPC for high-performance, low-latency scenarios (microservices, internal APIs) +- HTTP/REST for web browsers, legacy clients, and public APIs +- Same commands, queries, and validation logic for both protocols +- Swagger UI available for HTTP endpoints, gRPC reflection for gRPC clients > Example how to add your queries and commands. @@ -165,25 +235,55 @@ private void AddQueries(IServiceCollection services) # 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 -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(); + + // Manually register validator services.AddTransient, 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(); + + // Command with result - validator as last generic parameter + services.AddCommand(); } ``` +**Benefits:** +- **Single line registration** - Handler and validator registered together +- **Type safety** - Compiler ensures validator matches command type +- **Less boilerplate** - No need for separate `AddTransient>()` calls +- **Cleaner code** - Clear intent that validation is part of command pipeline + # 2024-2025 Roadmap | Task | Description | Status | @@ -191,9 +291,6 @@ public void ConfigureServices(IServiceCollection services) | 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. | ⬜️ | \ No newline at end of file +| Add gRPC Support with source generators | Implement gRPC endpoints with source generators and Google Rich Error Model for validation. | ✅ | +| Create a demo project (Svrnty.CQRS.Grpc.Sample) | Develop a comprehensive demo project showcasing gRPC and HTTP endpoints. | ✅ | +| Create a website for the Framework | Develop a website to host comprehensive documentation for the framework. | ⬜️ | \ No newline at end of file