This commit is contained in:
Mathias Beaulieu-Duncan 2025-11-04 16:45:54 -05:00
parent e19fad2baa
commit 2ee65b8dad
25 changed files with 175 additions and 85 deletions

View File

@ -31,7 +31,8 @@
"Bash(grpcurl:*)", "Bash(grpcurl:*)",
"Bash(lsof:*)", "Bash(lsof:*)",
"Bash(xargs kill -9)", "Bash(xargs kill -9)",
"Bash(dotnet run:*)" "Bash(dotnet run:*)",
"Bash(find:*)"
], ],
"deny": [], "deny": [],
"ask": [] "ask": []

2
.gitignore vendored
View File

@ -3,6 +3,8 @@
## ##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
.research/
# User-specific files # User-specific files
*.rsuser *.rsuser
*.suo *.suo

199
CLAUDE.md
View File

@ -6,28 +6,33 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
This is Svrnty.CQRS, a modern implementation of Command Query Responsibility Segregation (CQRS) for .NET 10. It was forked from PoweredSoft.CQRS and provides: This is Svrnty.CQRS, a modern implementation of Command Query Responsibility Segregation (CQRS) for .NET 10. It was forked from PoweredSoft.CQRS and provides:
- Automatic REST endpoint generation from command/query handlers - CQRS pattern implementation with command/query handlers exposed via HTTP or gRPC
- Automatic HTTP endpoint generation via Minimal API
- Automatic gRPC endpoint generation with source generators and Google Rich Error Model validation
- Dynamic query capabilities (filtering, sorting, grouping, aggregation) - Dynamic query capabilities (filtering, sorting, grouping, aggregation)
- ASP.NET Core MVC integration - FluentValidation support with RFC 7807 Problem Details (HTTP) and Google Rich Error Model (gRPC)
- FluentValidation support - AOT (Ahead-of-Time) compilation compatibility for core packages (where dependencies allow)
- AOT (Ahead-of-Time) compilation compatibility for core packages
## Solution Structure ## Solution Structure
The solution contains 9 projects organized by responsibility: The solution contains 11 projects organized by responsibility (10 packages + 1 sample project):
**Abstractions (interfaces and contracts only):** **Abstractions (interfaces and contracts only):**
- `Svrnty.CQRS.Abstractions` - Core interfaces (ICommandHandler, IQueryHandler, discovery contracts) - `Svrnty.CQRS.Abstractions` - Core interfaces (ICommandHandler, IQueryHandler, discovery contracts)
- `Svrnty.CQRS.AspNetCore.Abstractions` - ASP.NET Core attributes
- `Svrnty.CQRS.DynamicQuery.Abstractions` - Dynamic query interfaces (multi-targets netstandard2.1 and net10.0) - `Svrnty.CQRS.DynamicQuery.Abstractions` - Dynamic query interfaces (multi-targets netstandard2.1 and net10.0)
- `Svrnty.CQRS.Grpc.Abstractions` - gRPC-specific interfaces and contracts
**Implementation:** **Implementation:**
- `Svrnty.CQRS` - Core discovery and registration logic - `Svrnty.CQRS` - Core discovery and registration logic
- `Svrnty.CQRS.AspNetCore` - MVC controller generation (legacy/backward compatibility) - `Svrnty.CQRS.MinimalApi` - Minimal API endpoint mapping for commands/queries (recommended for HTTP)
- `Svrnty.CQRS.MinimalApi` - Minimal API endpoint mapping (recommended for new projects) - `Svrnty.CQRS.DynamicQuery` - PoweredSoft.DynamicQuery integration for advanced filtering
- `Svrnty.CQRS.DynamicQuery` - PoweredSoft.DynamicQuery integration - `Svrnty.CQRS.DynamicQuery.MinimalApi` - Minimal API endpoint mapping for dynamic queries
- `Svrnty.CQRS.DynamicQuery.AspNetCore` - Dynamic query controllers
- `Svrnty.CQRS.FluentValidation` - Validation integration helpers - `Svrnty.CQRS.FluentValidation` - Validation integration helpers
- `Svrnty.CQRS.Grpc` - gRPC service implementation support
- `Svrnty.CQRS.Grpc.Generators` - Source generator for .proto files and gRPC service implementations
**Sample Projects:**
- `Svrnty.Sample` - Comprehensive demo project showcasing both HTTP and gRPC endpoints
**Key Design Principle:** Abstractions projects contain ONLY interfaces/attributes with minimal dependencies. Implementation projects depend on abstractions. This allows consumers to reference abstractions without pulling in heavy implementation dependencies. **Key Design Principle:** Abstractions projects contain ONLY interfaces/attributes with minimal dependencies. Implementation projects depend on abstractions. This allows consumers to reference abstractions without pulling in heavy implementation dependencies.
@ -89,24 +94,77 @@ The framework uses a **metadata pattern** for runtime discovery:
- Query all registered metadata from DI container - Query all registered metadata from DI container
- Provide lookup methods: `GetCommand(string name)`, `GetCommands()`, etc. - Provide lookup methods: `GetCommand(string name)`, `GetCommands()`, etc.
3. ASP.NET Core feature providers use discovery to: 3. Endpoint mapping (HTTP and gRPC) uses discovery to:
- Enumerate all registered commands/queries - Enumerate all registered commands/queries
- Dynamically generate generic controller instances at startup - Dynamically generate endpoints at application startup
- Apply naming conventions (convert to lowerCamelCase) - Apply naming conventions (convert to lowerCamelCase)
- Generate gRPC service implementations via source generators
**Key Files:** **Key Files:**
- `Svrnty.CQRS.Abstractions/Discovery/` - Metadata interfaces - `Svrnty.CQRS.Abstractions/Discovery/` - Metadata interfaces
- `Svrnty.CQRS/Discovery/` - Discovery implementations - `Svrnty.CQRS/Discovery/` - Discovery implementations
- `Svrnty.CQRS.AspNetCore/Mvc/*FeatureProvider.cs` - Dynamic controller generation - `Svrnty.CQRS.MinimalApi/EndpointRouteBuilderExtensions.cs` - HTTP endpoint generation
- `Svrnty.CQRS.AspNetCore/Mvc/*Convention.cs` - Naming conventions - `Svrnty.CQRS.DynamicQuery.MinimalApi/EndpointRouteBuilderExtensions.cs` - Dynamic query endpoint generation
- `Svrnty.CQRS.Grpc.Generators/` - gRPC service generation via source generators
### ASP.NET Core Integration Options ### Integration Options
There are two options for integrating commands and queries with ASP.NET Core: There are two primary integration options for exposing commands and queries:
#### Option 1: Minimal API (Recommended) #### Option 1: gRPC (Recommended for performance-critical scenarios)
The **Svrnty.CQRS.MinimalApi** package provides modern Minimal API endpoints: The **Svrnty.CQRS.Grpc** package with **Svrnty.CQRS.Grpc.Generators** source generator provides high-performance gRPC endpoints:
**Registration:**
```csharp
var builder = WebApplication.CreateBuilder(args);
// Register CQRS services
builder.Services.AddSvrntyCQRS();
builder.Services.AddDefaultCommandDiscovery();
builder.Services.AddDefaultQueryDiscovery();
// Add your commands and queries
builder.Services.AddCommand<AddUserCommand, int, AddUserCommandHandler>();
builder.Services.AddCommand<RemoveUserCommand, RemoveUserCommandHandler>();
// 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();
```
**How It Works:**
1. Define `.proto` files in `Protos/` directory with your commands/queries as messages
2. Source generator automatically creates `CommandServiceImpl` and `QueryServiceImpl` implementations
3. Property names in C# commands must match proto field names (case-insensitive)
4. FluentValidation is automatically integrated with **Google Rich Error Model** for structured validation errors
5. Validation errors return `google.rpc.Status` with `BadRequest` containing `FieldViolations`
**Features:**
- High-performance binary protocol
- Automatic service implementation generation at compile time
- Google Rich Error Model for structured validation errors
- Full FluentValidation integration
- gRPC reflection support for development tools
- Suitable for microservices, internal APIs, and low-latency scenarios
**Key Files:**
- `Svrnty.CQRS.Grpc/` - Runtime support for gRPC services
- `Svrnty.CQRS.Grpc.Generators/` - Source generator for service implementations
#### Option 2: HTTP via Minimal API (Recommended for web/browser scenarios)
The **Svrnty.CQRS.MinimalApi** package provides HTTP endpoints for CQRS commands and queries:
**Registration:** **Registration:**
```csharp ```csharp
@ -121,6 +179,10 @@ builder.Services.AddDefaultQueryDiscovery();
builder.Services.AddCommand<CreatePersonCommand, CreatePersonCommandHandler>(); builder.Services.AddCommand<CreatePersonCommand, CreatePersonCommandHandler>();
builder.Services.AddQuery<PersonQuery, IQueryable<Person>, PersonQueryHandler>(); builder.Services.AddQuery<PersonQuery, IQueryable<Person>, PersonQueryHandler>();
// Add Swagger (optional)
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build(); var app = builder.Build();
// Map endpoints (this creates routes automatically) // Map endpoints (this creates routes automatically)
@ -133,7 +195,7 @@ app.Run();
**How It Works:** **How It Works:**
1. Extension methods iterate through `ICommandDiscovery` and `IQueryDiscovery` 1. Extension methods iterate through `ICommandDiscovery` and `IQueryDiscovery`
2. For each command/query, creates Minimal API endpoints using `MapPost()`/`MapGet()` 2. For each command/query, creates Minimal API endpoints using `MapPost()`/`MapGet()`
3. Applies same naming conventions as MVC (lowerCamelCase) 3. Applies naming conventions (lowerCamelCase)
4. Respects `[CommandControllerIgnore]` and `[QueryControllerIgnore]` attributes 4. Respects `[CommandControllerIgnore]` and `[QueryControllerIgnore]` attributes
5. Integrates with `ICommandAuthorizationService` and `IQueryAuthorizationService` 5. Integrates with `ICommandAuthorizationService` and `IQueryAuthorizationService`
6. Supports OpenAPI/Swagger documentation 6. Supports OpenAPI/Swagger documentation
@ -144,37 +206,53 @@ app.Run();
- Authorization via authorization services (returns 401/403 status codes) - Authorization via authorization services (returns 401/403 status codes)
- Customizable route prefixes: `MapSvrntyCommands("my-prefix")` - Customizable route prefixes: `MapSvrntyCommands("my-prefix")`
- Automatic OpenAPI tags: "Commands" and "Queries" - Automatic OpenAPI tags: "Commands" and "Queries"
- RFC 7807 Problem Details for validation errors
- Full Swagger/OpenAPI support
**Key Files:** **Key Files:**
- `Svrnty.CQRS.MinimalApi/EndpointRouteBuilderExtensions.cs` - Main implementation - `Svrnty.CQRS.MinimalApi/EndpointRouteBuilderExtensions.cs` - Main implementation
#### Option 2: MVC Controllers (Legacy) #### Option 3: Both gRPC and HTTP (Dual Protocol Support)
The AspNetCore packages use **dynamic controller generation** (maintained for backward compatibility): You can enable both protocols simultaneously, allowing clients to choose their preferred protocol:
1. **Feature Providers** (`CommandControllerFeatureProvider`, `QueryControllerFeatureProvider`):
- Called during MVC startup
- Discover handlers via `ICommandDiscovery`/`IQueryDiscovery`
- Create generic controller types: `CommandController<TCommand>`, `QueryController<TQuery, TResult>`
- Skip handlers marked with `[CommandControllerIgnore]` or `[QueryControllerIgnore]`
2. **Conventions** (`CommandControllerConvention`, `QueryControllerConvention`):
- Apply naming to generated controllers
- Default: Type name without suffix, converted to lowerCamelCase
- Custom: Use `[CommandName]` or `[QueryName]` attribute
3. **Controllers** expose automatic endpoints:
- Commands: `POST /api/command/{name}`
- Queries: `POST /api/query/{name}` or `GET /api/query/{name}`
**Registration Pattern:**
```csharp ```csharp
services var builder = WebApplication.CreateBuilder(args);
.AddControllers()
.AddSvrntyQueries() // Registers query feature provider // Register CQRS services
.AddSvrntyCommands(); // Registers command feature provider builder.Services.AddSvrntyCQRS();
builder.Services.AddDefaultCommandDiscovery();
builder.Services.AddDefaultQueryDiscovery();
// Add commands and queries
AddCommands(builder.Services);
AddQueries(builder.Services);
// Add both gRPC and HTTP support
builder.Services.AddGrpc();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Map both gRPC and HTTP endpoints
app.MapGrpcService<CommandServiceImpl>();
app.MapGrpcService<QueryServiceImpl>();
app.MapGrpcReflectionService();
app.MapSvrntyCommands();
app.MapSvrntyQueries();
app.Run();
``` ```
**Benefits:**
- Single codebase supports multiple protocols
- gRPC for high-performance, low-latency scenarios (microservices, internal APIs)
- HTTP 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
### Dynamic Query System ### Dynamic Query System
Dynamic queries provide OData-like filtering capabilities: Dynamic queries provide OData-like filtering capabilities:
@ -187,7 +265,7 @@ Dynamic queries provide OData-like filtering capabilities:
**Request Flow:** **Request Flow:**
1. HTTP request with filters/sorts/aggregates 1. HTTP request with filters/sorts/aggregates
2. DynamicQueryController receives request 2. Minimal API endpoint receives request
3. DynamicQueryHandler gets base queryable from IQueryableProvider 3. DynamicQueryHandler gets base queryable from IQueryableProvider
4. Applies alterations from all registered IAlterQueryableService instances 4. Applies alterations from all registered IAlterQueryableService instances
5. Builds PoweredSoft query criteria 5. Builds PoweredSoft query criteria
@ -195,14 +273,18 @@ Dynamic queries provide OData-like filtering capabilities:
**Registration Example:** **Registration Example:**
```csharp ```csharp
// Register dynamic query
services.AddDynamicQuery<Person, PersonDto>() services.AddDynamicQuery<Person, PersonDto>()
.AddDynamicQueryWithProvider<Person, PersonQueryableProvider>() .AddDynamicQueryWithProvider<Person, PersonQueryableProvider>()
.AddAlterQueryable<Person, PersonDto, SecurityFilter>(); .AddAlterQueryable<Person, PersonDto, SecurityFilter>();
// Map dynamic query endpoints
app.MapSvrntyDynamicQueries(); // Creates POST/GET /api/query/{queryName} endpoints
``` ```
**Key Files:** **Key Files:**
- `Svrnty.CQRS.DynamicQuery/DynamicQueryHandler.cs` - `Svrnty.CQRS.DynamicQuery/DynamicQueryHandler.cs` - Query execution logic
- `Svrnty.CQRS.DynamicQuery.AspNetCore/DynamicQuery.cs` - Request models - `Svrnty.CQRS.DynamicQuery.MinimalApi/EndpointRouteBuilderExtensions.cs` - HTTP endpoint mapping
## Package Configuration ## Package Configuration
@ -214,16 +296,28 @@ All projects target .NET 10.0 and use C# 14, sharing common configuration:
- **Symbols**: Portable debug symbols with source, published as `.snupkg` - **Symbols**: Portable debug symbols with source, published as `.snupkg`
- **NuGet metadata**: Icon, README, license (MIT), and repository URL included in packages - **NuGet metadata**: Icon, README, license (MIT), and repository URL included in packages
- **Authors**: David Lebee, Mathias Beaulieu-Duncan - **Authors**: David Lebee, Mathias Beaulieu-Duncan
- **Repository**: https://git.openharbor.io/Open-Harbor/dotnet-cqrs - **Repository**: https://git.openharbor.io/svrnty/dotnet-cqrs
### Package Dependencies ### Package Dependencies
**Core Dependencies:**
- **Microsoft.Extensions.DependencyInjection.Abstractions**: 10.0.0-rc.2.25502.107 (will update to stable when .NET 10 is released) - **Microsoft.Extensions.DependencyInjection.Abstractions**: 10.0.0-rc.2.25502.107 (will update to stable when .NET 10 is released)
- **FluentValidation**: 11.11.0 - **FluentValidation**: 11.11.0
- **Microsoft.CodeAnalysis.CSharp**: 4.13.0
- **PoweredSoft.DynamicQuery**: 3.0.1 - **PoweredSoft.DynamicQuery**: 3.0.1
- **Pluralize.NET**: 1.0.2 - **Pluralize.NET**: 1.0.2
**gRPC Dependencies (for Svrnty.CQRS.Grpc):**
- **Grpc.AspNetCore**: 2.68.0 or later
- **Grpc.AspNetCore.Server.Reflection**: 2.71.0 or later (optional, for reflection)
- **Grpc.StatusProto**: 2.71.0 or later (for Rich Error Model validation)
- **Grpc.Tools**: 2.76.0 or later (for .proto compilation)
**Source Generator Dependencies (for Svrnty.CQRS.Grpc.Generators):**
- **Microsoft.CodeAnalysis.CSharp**: 5.0.0-2.final
- **Microsoft.CodeAnalysis.Analyzers**: 3.11.0
- **Microsoft.Build.Utilities.Core**: 17.0.0
- Targets: netstandard2.0 (for Roslyn compatibility)
## Publishing ## Publishing
NuGet packages are published automatically via GitHub Actions when a release is created: NuGet packages are published automatically via GitHub Actions when a release is created:
@ -298,9 +392,12 @@ The codebase currently compiles without warnings on C# 14.
4. **Metadata Pattern**: When extending discovery, always create corresponding metadata classes (implement ICommandMeta/IQueryMeta). 4. **Metadata Pattern**: When extending discovery, always create corresponding metadata classes (implement ICommandMeta/IQueryMeta).
5. **Feature Provider Timing**: Controller generation happens during MVC startup. Discovery services must be registered before calling AddSvrntyCommands/Queries. 5. **Endpoint Mapping Timing**: Endpoints are mapped at application startup. Discovery services must be registered before calling `MapSvrntyCommands()`/`MapSvrntyQueries()` or mapping gRPC services.
6. **FluentValidation**: This framework only REGISTERS validators. Actual validation execution requires separate middleware/filters in consumer applications. 6. **FluentValidation Integration**:
- For HTTP: Validation happens automatically in the Minimal API pipeline. Errors return RFC 7807 Problem Details.
- For gRPC: Validation happens automatically via source-generated services. Errors return Google Rich Error Model with structured FieldViolations.
- The framework REGISTERS validators in DI; actual validation execution is handled by the endpoint implementations.
7. **DynamicQuery Interceptors**: Support up to 5 interceptors per query type. Interceptors modify PoweredSoft DynamicQuery behavior. 7. **DynamicQuery Interceptors**: Support up to 5 interceptors per query type. Interceptors modify PoweredSoft DynamicQuery behavior.
@ -309,6 +406,8 @@ The codebase currently compiles without warnings on C# 14.
- Handler interfaces: `Svrnty.CQRS.Abstractions/ICommandHandler.cs`, `IQueryHandler.cs` - Handler interfaces: `Svrnty.CQRS.Abstractions/ICommandHandler.cs`, `IQueryHandler.cs`
- Discovery implementations: `Svrnty.CQRS/Discovery/` - Discovery implementations: `Svrnty.CQRS/Discovery/`
- Service registration: `*/ServiceCollectionExtensions.cs` in each project - Service registration: `*/ServiceCollectionExtensions.cs` in each project
- Controller templates: `Svrnty.CQRS.AspNetCore/Mvc/*Controller.cs` - HTTP endpoint mapping: `Svrnty.CQRS.MinimalApi/EndpointRouteBuilderExtensions.cs`
- Dynamic query logic: `Svrnty.CQRS.DynamicQuery/DynamicQueryHandler.cs` - Dynamic query logic: `Svrnty.CQRS.DynamicQuery/DynamicQueryHandler.cs`
- Naming/routing: `Svrnty.CQRS.AspNetCore/Mvc/*Convention.cs` - Dynamic query endpoints: `Svrnty.CQRS.DynamicQuery.MinimalApi/EndpointRouteBuilderExtensions.cs`
- gRPC support: `Svrnty.CQRS.Grpc/` runtime, `Svrnty.CQRS.Grpc.Generators/` source generators
- Sample application: `Svrnty.Sample/` - demonstrates both HTTP and gRPC integration

View File

@ -24,7 +24,6 @@ Our implementation of query and command responsibility segregation (CQRS).
| Package Name | NuGet | NuGet Install | | Package Name | NuGet | NuGet Install |
| ---------------------------- |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -----------------------------------------------------: | | ---------------------------- |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -----------------------------------------------------: |
| 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.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.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 ``` | | 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 ``` |
@ -124,9 +123,9 @@ public record RemoveUserCommand
- Use `record` types for commands/queries (immutable, value-based equality, more concise) - Use `record` types for commands/queries (immutable, value-based equality, more concise)
- No need for protobuf-net attributes - No need for protobuf-net attributes
## Sample of startup code for Minimal API (Traditional HTTP) ## Sample of startup code for Minimal API (HTTP)
For traditional HTTP/REST scenarios, you can use the Minimal API approach: For HTTP scenarios (web browsers, public APIs), you can use the Minimal API approach:
```csharp ```csharp
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@ -184,7 +183,7 @@ AddCommands(builder.Services);
// Add gRPC support // Add gRPC support
builder.Services.AddGrpc(); builder.Services.AddGrpc();
// Add HTTP/REST support with Swagger // Add HTTP support with Swagger
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(); builder.Services.AddSwaggerGen();
@ -201,7 +200,7 @@ app.MapGrpcService<CommandServiceImpl>();
app.MapGrpcService<QueryServiceImpl>(); app.MapGrpcService<QueryServiceImpl>();
app.MapGrpcReflectionService(); app.MapGrpcReflectionService();
// Map HTTP/REST endpoints // Map HTTP endpoints
app.MapSvrntyCommands(); app.MapSvrntyCommands();
app.MapSvrntyQueries(); app.MapSvrntyQueries();
@ -211,7 +210,7 @@ app.Run();
**Benefits:** **Benefits:**
- Single codebase supports multiple protocols - Single codebase supports multiple protocols
- gRPC for high-performance, low-latency scenarios (microservices, internal APIs) - gRPC for high-performance, low-latency scenarios (microservices, internal APIs)
- HTTP/REST for web browsers, legacy clients, and public APIs - HTTP for web browsers, legacy clients, and public APIs
- Same commands, queries, and validation logic for both protocols - Same commands, queries, and validation logic for both protocols
- Swagger UI available for HTTP endpoints, gRPC reflection for gRPC clients - Swagger UI available for HTTP endpoints, gRPC reflection for gRPC clients

View File

@ -4,10 +4,11 @@
<IsAotCompatible>true</IsAotCompatible> <IsAotCompatible>true</IsAotCompatible>
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors> <Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
<LangVersion>14</LangVersion> <LangVersion>14</LangVersion>
<Nullable>enable</Nullable>
<Company>Svrnty</Company> <Company>Svrnty</Company>
<PackageIcon>icon.png</PackageIcon> <PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/svrnty/dotnet-cqrs</RepositoryUrl> <RepositoryUrl>https://git.openharbor.io/svrnty/dotnet-cqrs</RepositoryUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>

View File

@ -7,7 +7,7 @@
<Company>Svrnty</Company> <Company>Svrnty</Company>
<PackageIcon>icon.png</PackageIcon> <PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/svrnty/dotnet-cqrs</RepositoryUrl> <RepositoryUrl>https://git.openharbor.io/svrnty/dotnet-cqrs</RepositoryUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>

View File

@ -1,4 +1,3 @@
#nullable enable
using System; using System;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;

View File

@ -3,11 +3,12 @@
<TargetFramework>net10.0</TargetFramework> <TargetFramework>net10.0</TargetFramework>
<IsAotCompatible>false</IsAotCompatible> <IsAotCompatible>false</IsAotCompatible>
<LangVersion>14</LangVersion> <LangVersion>14</LangVersion>
<Nullable>enable</Nullable>
<Company>Svrnty</Company> <Company>Svrnty</Company>
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors> <Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
<PackageIcon>icon.png</PackageIcon> <PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/svrnty/dotnet-cqrs</RepositoryUrl> <RepositoryUrl>https://git.openharbor.io/svrnty/dotnet-cqrs</RepositoryUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>

View File

@ -3,10 +3,11 @@
<TargetFramework>net10.0</TargetFramework> <TargetFramework>net10.0</TargetFramework>
<IsAotCompatible>true</IsAotCompatible> <IsAotCompatible>true</IsAotCompatible>
<LangVersion>14</LangVersion> <LangVersion>14</LangVersion>
<Nullable>enable</Nullable>
<Company>Svrnty</Company> <Company>Svrnty</Company>
<PackageIcon>icon.png</PackageIcon> <PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/svrnty/dotnet-cqrs</RepositoryUrl> <RepositoryUrl>https://git.openharbor.io/svrnty/dotnet-cqrs</RepositoryUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>

View File

@ -1,5 +1,3 @@
#nullable enable
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using FluentValidation; using FluentValidation;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;

View File

@ -3,10 +3,11 @@
<TargetFramework>net10.0</TargetFramework> <TargetFramework>net10.0</TargetFramework>
<IsAotCompatible>true</IsAotCompatible> <IsAotCompatible>true</IsAotCompatible>
<LangVersion>14</LangVersion> <LangVersion>14</LangVersion>
<Nullable>enable</Nullable>
<Company>Svrnty</Company> <Company>Svrnty</Company>
<PackageIcon>icon.png</PackageIcon> <PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/svrnty/dotnet-cqrs</RepositoryUrl> <RepositoryUrl>https://git.openharbor.io/svrnty/dotnet-cqrs</RepositoryUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>

View File

@ -8,7 +8,7 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<PackageIcon>icon.png</PackageIcon> <PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/svrnty/dotnet-cqrs</RepositoryUrl> <RepositoryUrl>https://git.openharbor.io/svrnty/dotnet-cqrs</RepositoryUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>

View File

@ -16,7 +16,7 @@
<Company>Svrnty</Company> <Company>Svrnty</Company>
<PackageIcon>icon.png</PackageIcon> <PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/svrnty/dotnet-cqrs</RepositoryUrl> <RepositoryUrl>https://git.openharbor.io/svrnty/dotnet-cqrs</RepositoryUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>

View File

@ -1,4 +1,3 @@
#nullable enable
using System; using System;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;

View File

@ -1,5 +1,3 @@
#nullable enable
namespace Svrnty.CQRS.Grpc; namespace Svrnty.CQRS.Grpc;
/// <summary> /// <summary>

View File

@ -3,12 +3,12 @@
<TargetFramework>net10.0</TargetFramework> <TargetFramework>net10.0</TargetFramework>
<IsAotCompatible>false</IsAotCompatible> <IsAotCompatible>false</IsAotCompatible>
<LangVersion>14</LangVersion> <LangVersion>14</LangVersion>
<Nullable>enable</Nullable>
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors> <Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
<Nullable>enable</Nullable>
<Company>Svrnty</Company> <Company>Svrnty</Company>
<PackageIcon>icon.png</PackageIcon> <PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/svrnty/dotnet-cqrs</RepositoryUrl> <RepositoryUrl>https://git.openharbor.io/svrnty/dotnet-cqrs</RepositoryUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>

View File

@ -1,4 +1,3 @@
#nullable enable
using System; using System;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;

View File

@ -1,5 +1,3 @@
#nullable enable
namespace Svrnty.CQRS.MinimalApi; namespace Svrnty.CQRS.MinimalApi;
/// <summary> /// <summary>

View File

@ -8,7 +8,7 @@
<Company>Svrnty</Company> <Company>Svrnty</Company>
<PackageIcon>icon.png</PackageIcon> <PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/svrnty/dotnet-cqrs</RepositoryUrl> <RepositoryUrl>https://git.openharbor.io/svrnty/dotnet-cqrs</RepositoryUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>

View File

@ -1,5 +1,3 @@
#nullable enable
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Svrnty.CQRS.Configuration; using Svrnty.CQRS.Configuration;

View File

@ -1,5 +1,3 @@
#nullable enable
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Svrnty.CQRS.Abstractions; using Svrnty.CQRS.Abstractions;
using Svrnty.CQRS.Discovery; using Svrnty.CQRS.Discovery;

View File

@ -1,4 +1,3 @@
#nullable enable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;

View File

@ -1,5 +1,3 @@
#nullable enable
using System; using System;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;

View File

@ -4,10 +4,11 @@
<IsAotCompatible>true</IsAotCompatible> <IsAotCompatible>true</IsAotCompatible>
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors> <Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
<LangVersion>14</LangVersion> <LangVersion>14</LangVersion>
<Nullable>enable</Nullable>
<Company>Svrnty</Company> <Company>Svrnty</Company>
<PackageIcon>icon.png</PackageIcon> <PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/svrnty/dotnet-cqrs</RepositoryUrl> <RepositoryUrl>https://git.openharbor.io/svrnty/dotnet-cqrs</RepositoryUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>

View File

@ -13,7 +13,7 @@ builder.WebHost.ConfigureKestrel(options =>
{ {
// Port 6000: HTTP/2 for gRPC // Port 6000: HTTP/2 for gRPC
options.ListenLocalhost(6000, o => o.Protocols = HttpProtocols.Http2); options.ListenLocalhost(6000, o => o.Protocols = HttpProtocols.Http2);
// Port 6001: HTTP/1.1 for REST API // Port 6001: HTTP/1.1 for HTTP API
options.ListenLocalhost(6001, o => o.Protocols = HttpProtocols.Http1); options.ListenLocalhost(6001, o => o.Protocols = HttpProtocols.Http1);
}); });