Compare commits
2 Commits
7ef3e56759
...
3fa59306c2
| Author | SHA1 | Date | |
|---|---|---|---|
| 3fa59306c2 | |||
| 697b36900b |
17
CHANGELOG.md
Normal file
17
CHANGELOG.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- Initial project setup
|
||||
|
||||
### Changed
|
||||
|
||||
### Fixed
|
||||
|
||||
### Removed
|
||||
180
CLAUDE.md
180
CLAUDE.md
@ -1,155 +1,79 @@
|
||||
# dotnet-cqrs -- Development Guide
|
||||
# Development Guidelines
|
||||
|
||||
## Overview
|
||||
Universal development practices for all Svrnty/a-gent repositories.
|
||||
|
||||
Svrnty.CQRS — modern CQRS implementation for .NET 10. Forked from PoweredSoft.CQRS. Provides command/query handlers exposed via HTTP (Minimal API) or gRPC (source-generated), dynamic query capabilities, FluentValidation with RFC 7807 (HTTP) and Google Rich Error Model (gRPC).
|
||||
## Engineering Principles
|
||||
|
||||
## Tech Stack
|
||||
### KISS (Keep It Simple)
|
||||
- Prefer straightforward control flow over clever meta-programming
|
||||
- Keep error paths obvious and localized
|
||||
|
||||
- **Framework:** .NET 10 / C# 14
|
||||
- **Packages:** 10 NuGet packages + 1 sample project
|
||||
- **gRPC:** Grpc.AspNetCore 2.68+, source-generated service implementations
|
||||
- **Validation:** FluentValidation 11.11.0
|
||||
- **Dynamic queries:** PoweredSoft.DynamicQuery 3.0.1
|
||||
### YAGNI (You Aren't Gonna Need It)
|
||||
- Do not add features without concrete accepted use case
|
||||
- Do not introduce speculative abstractions
|
||||
|
||||
## Build & Test
|
||||
### DRY + Rule of Three
|
||||
- Duplicate small logic when it preserves clarity
|
||||
- Extract shared utilities only after 3+ repeated patterns
|
||||
|
||||
```bash
|
||||
# Restore dependencies
|
||||
dotnet restore
|
||||
### SRP + ISP (Single Responsibility + Interface Segregation)
|
||||
- Keep each module focused on one concern
|
||||
- Avoid "god modules" mixing multiple responsibilities
|
||||
|
||||
# Build entire solution
|
||||
dotnet build
|
||||
### Fail Fast + Explicit Errors
|
||||
- Prefer explicit errors for unsupported states
|
||||
- Document fallback behavior when intentional
|
||||
|
||||
# Build in Release mode
|
||||
dotnet build -c Release
|
||||
### Secure by Default
|
||||
- Deny-by-default for access boundaries
|
||||
- Never log secrets or sensitive payloads
|
||||
|
||||
# Create NuGet packages
|
||||
dotnet pack -c Release -o ./artifacts -p:Version=1.0.0
|
||||
```
|
||||
## Agent Workflow
|
||||
|
||||
No test projects currently. When adding tests: `tests/` directory, `.Tests` suffix.
|
||||
1. **Read before write** - Inspect existing code before editing
|
||||
2. **Define scope** - One concern per PR
|
||||
3. **Implement minimal patch** - Apply KISS/YAGNI explicitly
|
||||
4. **Validate** - Run lint, format, test before commit
|
||||
5. **Document impact** - Note behavior changes and risks
|
||||
|
||||
## Solution Structure
|
||||
## Branch Strategy
|
||||
|
||||
**Abstractions (interfaces only):**
|
||||
- `Svrnty.CQRS.Abstractions` — Core interfaces (ICommandHandler, IQueryHandler)
|
||||
- `Svrnty.CQRS.DynamicQuery.Abstractions` — Dynamic query interfaces (multi-targets netstandard2.1 + net10.0)
|
||||
- `Svrnty.CQRS.Grpc.Abstractions` — gRPC-specific contracts
|
||||
- All repos use `JP` branch for active development
|
||||
- Always verify current branch before committing
|
||||
|
||||
**Implementation:**
|
||||
- `Svrnty.CQRS` — Core discovery and registration
|
||||
- `Svrnty.CQRS.MinimalApi` — HTTP endpoint mapping
|
||||
- `Svrnty.CQRS.DynamicQuery` — PoweredSoft.DynamicQuery integration
|
||||
- `Svrnty.CQRS.DynamicQuery.MinimalApi` — Dynamic query HTTP endpoints
|
||||
- `Svrnty.CQRS.FluentValidation` — Validation helpers
|
||||
- `Svrnty.CQRS.Grpc` — gRPC service support
|
||||
- `Svrnty.CQRS.Grpc.Generators` — Source generator for .proto + gRPC service implementations
|
||||
|
||||
**Sample:** `Svrnty.Sample` — HTTP + gRPC demo
|
||||
|
||||
## Architecture
|
||||
|
||||
### Metadata-Driven Discovery
|
||||
|
||||
1. `services.AddCommand<TCommand, THandler>()` registers handler + creates `ICommandMeta`
|
||||
2. `ICommandDiscovery` / `IQueryDiscovery` query all registered metadata from DI
|
||||
3. Endpoint mapping (HTTP/gRPC) uses discovery to dynamically generate endpoints at startup
|
||||
|
||||
### Integration Options
|
||||
|
||||
- **gRPC** (recommended for performance): Source-generated `CommandServiceImpl` / `QueryServiceImpl`
|
||||
- **HTTP** (recommended for web): `MapSvrntyCommands()` / `MapSvrntyQueries()` via Minimal API
|
||||
- **Both**: Dual protocol support in single codebase
|
||||
|
||||
### Dynamic Query System
|
||||
|
||||
- `IDynamicQuery<TSource, TDestination>` with filters, sorts, groups, aggregates
|
||||
- `IQueryableProvider<TSource>` provides base IQueryable
|
||||
- `IAlterQueryableService<TSource, TDestination>` for middleware (security filters)
|
||||
- Up to 5 interceptors per query type
|
||||
|
||||
## Conventions
|
||||
|
||||
- Abstractions contain ONLY interfaces/attributes with minimal dependencies
|
||||
- Handler naming: strip "Command"/"Query" suffix, convert to lowerCamelCase (e.g. `CreatePersonCommand` → `createPerson`)
|
||||
- All handlers are async with CancellationToken support
|
||||
- Endpoint mapping happens at startup — discovery must be registered first
|
||||
- FluentValidation: HTTP returns RFC 7807, gRPC returns Google Rich Error Model
|
||||
|
||||
## Key Files
|
||||
|
||||
- Handler interfaces: `Svrnty.CQRS.Abstractions/ICommandHandler.cs`, `IQueryHandler.cs`
|
||||
- Discovery: `Svrnty.CQRS/Discovery/`
|
||||
- Registration: `*/ServiceCollectionExtensions.cs`
|
||||
- HTTP endpoints: `Svrnty.CQRS.MinimalApi/EndpointRouteBuilderExtensions.cs`
|
||||
- gRPC generators: `Svrnty.CQRS.Grpc.Generators/`
|
||||
|
||||
## Publishing
|
||||
|
||||
NuGet packages via GitHub Actions on release: `.github/workflows/publish-nugets.yml`
|
||||
|
||||
## What NOT to Do
|
||||
|
||||
- Do NOT modify abstractions projects to add implementation dependencies
|
||||
- Do NOT skip CancellationToken in handler signatures
|
||||
- Do NOT call `MapSvrntyCommands()` before registering discovery services
|
||||
- Do NOT add heavy dependencies to abstractions packages
|
||||
|
||||
## Critical Rules
|
||||
|
||||
### Commit Authorship
|
||||
## Commit Rules
|
||||
|
||||
All AI-authored commits MUST use:
|
||||
```
|
||||
Co-Authored-By: Svrnty Inc. <eng@svrnty.com>
|
||||
```
|
||||
|
||||
NEVER use:
|
||||
- Third-party AI tool names (e.g. "Claude") in any Co-Authored-By
|
||||
- Third-party AI company names (e.g. "Anthropic") in any Co-Authored-By
|
||||
- Third-party AI tool names in commit messages
|
||||
NEVER use third-party AI tool/company names in commits.
|
||||
|
||||
### Documentation References
|
||||
## PR Discipline
|
||||
|
||||
- Do NOT reference third-party AI tool names as product names in any documentation
|
||||
- Use "agent runtime", "agent CLI", or "Svrnty runtime" instead
|
||||
- CLAUDE.md files keep their filename (industry standard) but content must not
|
||||
mention third-party AI tool names as products
|
||||
- Clear, scoped commit messages
|
||||
- Small PRs preferred
|
||||
- Never commit personal/sensitive data
|
||||
- Reference related repos for cross-repo changes
|
||||
|
||||
### Branch Strategy
|
||||
## Validation
|
||||
|
||||
- All repos use the `JP` branch for active development
|
||||
- Always check which branch you're on before committing
|
||||
Run before every commit:
|
||||
- Format check (language-specific)
|
||||
- Lint check (language-specific)
|
||||
- Test suite
|
||||
|
||||
### Cross-Repo Changes
|
||||
## Anti-Patterns
|
||||
|
||||
- If a change spans multiple repos, commit to each repo separately
|
||||
- Reference the related repo in commit messages when relevant
|
||||
- Run tests in each affected repo before committing
|
||||
- Do NOT add heavy dependencies for minor convenience
|
||||
- Do NOT mix formatting-only with functional changes
|
||||
- Do NOT modify unrelated modules "while here"
|
||||
- Do NOT bypass failing checks without explanation
|
||||
|
||||
## Documentation Triad
|
||||
## Cross-Repo Changes
|
||||
|
||||
This repo maintains three canonical files. Keep them in sync.
|
||||
|
||||
### PROJECT.md — What & Why
|
||||
- User perspective. No code, no architecture.
|
||||
- Update when: features added/removed, scope changes, user-facing behaviour changes.
|
||||
|
||||
### GRAPH.md — Architecture
|
||||
- Unicode cascade diagrams. Entire file IS the diagram.
|
||||
- Update when: modules added/removed, data flow changes, API surface changes.
|
||||
|
||||
### CLAUDE.md — How to Work Here
|
||||
- Agent development guide for this repository.
|
||||
- Update when: build commands change, conventions change, new patterns established.
|
||||
|
||||
## GRAPH.md Maintenance
|
||||
|
||||
This repo contains a `GRAPH.md` file with unicode cascade diagrams showing the
|
||||
architecture. **You MUST update GRAPH.md whenever you**:
|
||||
- Add, remove, or rename modules, directories, or major components
|
||||
- Change data flow, dependencies, or integration points
|
||||
- Modify the public API surface or protocol contracts
|
||||
|
||||
Keep the diagrams accurate. They are read by non-technical stakeholders.
|
||||
When changes span multiple repos:
|
||||
1. Commit to each repo separately
|
||||
2. Reference related repo in commit message
|
||||
3. Run tests in each affected repo before commit
|
||||
|
||||
52
CONTRIBUTING.md
Normal file
52
CONTRIBUTING.md
Normal file
@ -0,0 +1,52 @@
|
||||
# Contributing
|
||||
|
||||
Thank you for your interest in contributing to this project.
|
||||
|
||||
## Development Guidelines
|
||||
|
||||
See [CLAUDE.md](./CLAUDE.md) for development practices, engineering principles, and coding standards.
|
||||
|
||||
## How to Contribute
|
||||
|
||||
1. **Fork & Clone**
|
||||
```bash
|
||||
git clone <your-fork-url>
|
||||
cd <project>
|
||||
git checkout JP
|
||||
```
|
||||
|
||||
2. **Create a Branch**
|
||||
```bash
|
||||
git checkout -b feature/your-feature-name
|
||||
```
|
||||
|
||||
3. **Make Changes**
|
||||
- Follow the guidelines in CLAUDE.md
|
||||
- Keep changes focused and minimal
|
||||
- Write tests if applicable
|
||||
|
||||
4. **Validate**
|
||||
- Run format checks
|
||||
- Run lint checks
|
||||
- Run test suite
|
||||
|
||||
5. **Commit**
|
||||
```bash
|
||||
git commit -m "feat: your change description"
|
||||
```
|
||||
|
||||
AI-authored commits must include:
|
||||
```
|
||||
Co-Authored-By: Svrnty Inc. <eng@svrnty.com>
|
||||
```
|
||||
|
||||
6. **Push & Create PR**
|
||||
```bash
|
||||
git push origin feature/your-feature-name
|
||||
```
|
||||
- Open a PR against the `JP` branch
|
||||
- Provide clear description of changes
|
||||
|
||||
## Questions?
|
||||
|
||||
Open an issue for questions or discussions.
|
||||
57
GRAPH.md
57
GRAPH.md
@ -1,57 +0,0 @@
|
||||
╔══════════════════════════════════════════════════════════╗
|
||||
║ dotnet-cqrs Architecture ║
|
||||
╚══════════════════════════════════════════════════════════╝
|
||||
|
||||
PACKAGE LAYERS
|
||||
══════════════
|
||||
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Abstractions (Interfaces) │
|
||||
│ ICommandHandler<T> IQueryHandler<T,R> │
|
||||
│ ICommandDiscovery IQueryDiscovery │
|
||||
│ IDynamicQuery<S,D> IQueryableProvider<S> │
|
||||
└─────────────────────────┬───────────────────────────┘
|
||||
│ implements
|
||||
┌─────────────────────────▼───────────────────────────┐
|
||||
│ Core Implementation │
|
||||
│ CQRS (discovery + dispatch) │
|
||||
│ DynamicQuery (OData-like filter/sort/group) │
|
||||
│ FluentValidation (validators → RFC 7807 errors) │
|
||||
└─────────────────────────┬───────────────────────────┘
|
||||
│ exposes via
|
||||
┌─────────────────────────▼───────────────────────────┐
|
||||
│ Protocol / Transport │
|
||||
│ MinimalApi ─── HTTP endpoints (auto-mapped) │
|
||||
│ Grpc ───────── gRPC endpoints (source-generated) │
|
||||
│ Grpc.Generators ── compile-time codegen │
|
||||
└─────────────────────────┬───────────────────────────┘
|
||||
│ optional
|
||||
┌─────────────────────────▼───────────────────────────┐
|
||||
│ Extended Features │
|
||||
│ Events + Events.RabbitMQ ── event sourcing │
|
||||
│ Sagas + Sagas.RabbitMQ ─── long-running txns │
|
||||
│ DynamicQuery.EF ────────── Entity Framework │
|
||||
│ Notifications ──────────── real-time push │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
|
||||
|
||||
METADATA-DRIVEN ENDPOINT FLOW
|
||||
══════════════════════════════
|
||||
|
||||
Developer writes handler:
|
||||
ICommandHandler<CreatePersonCommand, PersonResult>
|
||||
│
|
||||
▼
|
||||
DI registration (AddSvrntyCqrs)
|
||||
│
|
||||
▼
|
||||
Discovery auto-enumerates all handlers
|
||||
│
|
||||
▼
|
||||
┌────┴────────────────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
HTTP: MapSvrntyCommands() gRPC: Source Generator
|
||||
POST /api/command/ CommandServiceImpl
|
||||
createPerson QueryServiceImpl
|
||||
(compile-time generated)
|
||||
42
PROJECT.md
42
PROJECT.md
@ -1,42 +0,0 @@
|
||||
# dotnet-cqrs
|
||||
|
||||
> Modern CQRS implementation for .NET 10 with HTTP and gRPC endpoint generation.
|
||||
|
||||
## Purpose
|
||||
|
||||
Svrnty.CQRS provides a framework for implementing the Command Query Responsibility Segregation pattern in .NET applications. It automatically generates HTTP endpoints (via Minimal API) and gRPC service implementations (via source generators) from registered command and query handlers.
|
||||
|
||||
## What It Does
|
||||
|
||||
- **Automatic HTTP endpoints** — registers handlers, generates REST endpoints at startup
|
||||
- **Automatic gRPC services** — source-generated service implementations from .proto files
|
||||
- **Dynamic queries** — OData-like filtering, sorting, grouping, and aggregation
|
||||
- **Validation** — FluentValidation with RFC 7807 (HTTP) and Google Rich Error Model (gRPC)
|
||||
- **Dual protocol** — same handlers serve both HTTP and gRPC simultaneously
|
||||
|
||||
## How It Works
|
||||
|
||||
1. Define a command/query as a C# class
|
||||
2. Implement a handler (`ICommandHandler<T>` or `IQueryHandler<T, R>`)
|
||||
3. Register with DI: `services.AddCommand<T, Handler>()`
|
||||
4. Framework creates metadata and discovery services at startup
|
||||
5. `MapSvrntyCommands()` generates HTTP endpoints; `MapGrpcService<>()` maps gRPC services
|
||||
|
||||
## Key Concepts
|
||||
|
||||
| Term | Meaning |
|
||||
|------|---------|
|
||||
| Command | An action that changes state (always POST) |
|
||||
| Query | A request for data (POST or GET) |
|
||||
| Discovery | Runtime service that enumerates all registered commands/queries |
|
||||
| Source Generator | Compile-time code generation for gRPC service implementations |
|
||||
| Dynamic Query | OData-like filtering with IQueryable providers and interceptors |
|
||||
|
||||
## Current Status
|
||||
|
||||
| Phase | Status | Description |
|
||||
|-------|--------|-------------|
|
||||
| 0 | Done | Core CQRS, Minimal API, FluentValidation |
|
||||
| 1 | Done | gRPC support, source generators |
|
||||
| 2 | Done | Dynamic queries, dual protocol |
|
||||
| 3 | Planned | Test suite, AOT compatibility |
|
||||
290
README.md
290
README.md
@ -1,282 +1,66 @@
|
||||
> This project was originally initiated by [Powered Software Inc.](https://poweredsoft.com/) and was forked from the [PoweredSoft.CQRS](https://github.com/PoweredSoft/CQRS) Repository
|
||||
# Svrnty.CQRS
|
||||
|
||||
# CQRS
|
||||
|
||||
Our implementation of query and command responsibility segregation (CQRS).
|
||||
> Modern CQRS framework for .NET with gRPC source generation and HTTP Minimal API support.
|
||||
|
||||
## Where This Fits
|
||||
|
||||
This is a backend framework of the [Svrnty Agent System](../README.md).
|
||||
|
||||
**Layer**: Framework
|
||||
**Layer**: libs
|
||||
**Depends on**: Nothing (standalone .NET framework)
|
||||
**Depended on by**: a-gent-app (backend services), flutter_cqrs_datasource (client)
|
||||
**Git**: [git.openharbor.io/svrnty/dotnet-cqrs](https://git.openharbor.io/svrnty/dotnet-cqrs)
|
||||
**Depended on by**: a-gent-app (backend services), flutter-cqrs-datasource (client)
|
||||
**Git**: git.openharbor.io/svrnty/dotnet-cqrs.git
|
||||
|
||||
## Getting Started
|
||||
## Tech Stack
|
||||
|
||||
> Install nuget package to your awesome project.
|
||||
- **Language**: C# 14 / .NET 10
|
||||
- **Framework**: ASP.NET Core Minimal API, gRPC
|
||||
- **Key Dependencies**: FluentValidation 11.x, Grpc.AspNetCore, PoweredSoft.DynamicQuery
|
||||
|
||||
| Package Name | NuGet | NuGet Install |
|
||||
|-----------------------------------------| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |-----------------------------------------------------------------------:|
|
||||
| Svrnty.CQRS | [](https://www.nuget.org/packages/Svrnty.CQRS/) | ```dotnet add package Svrnty.CQRS ``` |
|
||||
| Svrnty.CQRS.MinimalApi | [](https://www.nuget.org/packages/Svrnty.CQRS.MinimalApi/) | ```dotnet add package Svrnty.CQRS.MinimalApi ``` |
|
||||
| Svrnty.CQRS.FluentValidation | [](https://www.nuget.org/packages/Svrnty.CQRS.FluentValidation/) | ```dotnet add package Svrnty.CQRS.FluentValidation ``` |
|
||||
| Svrnty.CQRS.DynamicQuery | [](https://www.nuget.org/packages/Svrnty.CQRS.DynamicQuery/) | ```dotnet add package Svrnty.CQRS.DynamicQuery ``` |
|
||||
| Svrnty.CQRS.DynamicQuery.MinimalApi | [](https://www.nuget.org/packages/Svrnty.CQRS.DynamicQuery.MinimalApi/) | ```dotnet add package Svrnty.CQRS.DynamicQuery.MinimalApi ``` |
|
||||
| Svrnty.CQRS.Grpc | [](https://www.nuget.org/packages/Svrnty.CQRS.Grpc/) | ```dotnet add package Svrnty.CQRS.Grpc ``` |
|
||||
| Svrnty.CQRS.Grpc.Generators | [](https://www.nuget.org/packages/Svrnty.CQRS.Grpc.Generators/) | ```dotnet add package Svrnty.CQRS.Grpc.Generators ``` |
|
||||
|
||||
> Abstractions Packages.
|
||||
|
||||
| Package Name | NuGet | NuGet Install |
|
||||
| ---------------------------- |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -----------------------------------------------------: |
|
||||
| Svrnty.CQRS.Abstractions | [](https://www.nuget.org/packages/Svrnty.CQRS.Abstractions/) | ```dotnet add package Svrnty.CQRS.Abstractions ``` |
|
||||
| Svrnty.CQRS.DynamicQuery.Abstractions | [](https://www.nuget.org/packages/Svrnty.CQRS.DynamicQuery.Abstractions/) | ```dotnet add package Svrnty.CQRS.DynamicQuery.Abstractions ``` |
|
||||
| Svrnty.CQRS.Grpc.Abstractions | [](https://www.nuget.org/packages/Svrnty.CQRS.Grpc.Abstractions/) | ```dotnet add package Svrnty.CQRS.Grpc.Abstractions ``` |
|
||||
|
||||
|
||||
## Sample of startup code for gRPC (Recommended)
|
||||
|
||||
```csharp
|
||||
using Svrnty.CQRS;
|
||||
using Svrnty.CQRS.FluentValidation;
|
||||
using Svrnty.CQRS.Grpc;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Register your commands with validators
|
||||
builder.Services.AddCommand<AddUserCommand, int, AddUserCommandHandler, AddUserCommandValidator>();
|
||||
builder.Services.AddCommand<RemoveUserCommand, RemoveUserCommandHandler>();
|
||||
|
||||
// Register your queries
|
||||
builder.Services.AddQuery<FetchUserQuery, User, FetchUserQueryHandler>();
|
||||
|
||||
// Configure CQRS with gRPC support
|
||||
builder.Services.AddSvrntyCqrs(cqrs =>
|
||||
{
|
||||
// Enable gRPC endpoints with reflection
|
||||
cqrs.AddGrpc(grpc =>
|
||||
{
|
||||
grpc.EnableReflection();
|
||||
});
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Map all configured CQRS endpoints
|
||||
app.UseSvrntyCqrs();
|
||||
|
||||
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:
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
dotnet add package Grpc.AspNetCore
|
||||
dotnet add package Grpc.AspNetCore.Server.Reflection
|
||||
dotnet add package Grpc.StatusProto # For Rich Error Model validation
|
||||
# Build
|
||||
dotnet build
|
||||
|
||||
# Run
|
||||
dotnet run --project Svrnty.Sample
|
||||
|
||||
# Test
|
||||
dotnet test
|
||||
```
|
||||
|
||||
#### 2. Add the source generator as an analyzer:
|
||||
## Architecture
|
||||
|
||||
```bash
|
||||
dotnet add package Svrnty.CQRS.Grpc.Generators
|
||||
```
|
||||
10 NuGet packages organized by concern:
|
||||
|
||||
The source generator is automatically configured as an analyzer when installed via NuGet and will generate both the `.proto` files and gRPC service implementations at compile time.
|
||||
- **Abstractions**: Core interfaces (ICommandHandler, IQueryHandler)
|
||||
- **Core**: Discovery, registration, handler execution
|
||||
- **MinimalApi**: HTTP endpoint mapping with RFC 7807 validation
|
||||
- **Grpc**: gRPC service support with Google Rich Error Model
|
||||
- **Grpc.Generators**: Source generator for .proto files and service implementations
|
||||
- **DynamicQuery**: PoweredSoft integration for filtering, sorting, paging
|
||||
- **FluentValidation**: Validator registration helpers
|
||||
|
||||
#### 3. Define your C# commands and queries:
|
||||
## Configuration
|
||||
|
||||
```csharp
|
||||
public record AddUserCommand
|
||||
{
|
||||
public required string Name { get; init; }
|
||||
public required string Email { get; init; }
|
||||
public int Age { get; init; }
|
||||
}
|
||||
// Register handlers
|
||||
builder.Services.AddCommand<CreateUserCommand, int, CreateUserCommandHandler>();
|
||||
builder.Services.AddQuery<GetUserQuery, User, GetUserQueryHandler>();
|
||||
|
||||
public record RemoveUserCommand
|
||||
{
|
||||
public int UserId { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
- The source generator automatically creates:
|
||||
- `.proto` files in the `Protos/` directory from your C# commands and queries
|
||||
- `CommandServiceImpl` and `QueryServiceImpl` implementations
|
||||
- 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 - just define your C# types
|
||||
|
||||
## Sample of startup code for Minimal API (HTTP)
|
||||
|
||||
For HTTP scenarios (web browsers, public APIs), you can use the Minimal API approach:
|
||||
|
||||
```csharp
|
||||
using Svrnty.CQRS;
|
||||
using Svrnty.CQRS.FluentValidation;
|
||||
using Svrnty.CQRS.MinimalApi;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Register your commands with validators
|
||||
builder.Services.AddCommand<CreatePersonCommand, CreatePersonCommandHandler, CreatePersonCommandValidator>();
|
||||
builder.Services.AddCommand<EchoCommand, string, EchoCommandHandler, EchoCommandValidator>();
|
||||
|
||||
// Register your queries
|
||||
builder.Services.AddQuery<PersonQuery, IQueryable<Person>, PersonQueryHandler>();
|
||||
|
||||
// Configure CQRS with Minimal API support
|
||||
// Configure CQRS with gRPC + HTTP
|
||||
builder.Services.AddSvrntyCqrs(cqrs =>
|
||||
{
|
||||
// Enable Minimal API endpoints
|
||||
cqrs.AddGrpc(grpc => grpc.EnableReflection());
|
||||
cqrs.AddMinimalApi();
|
||||
});
|
||||
|
||||
// Add Swagger (optional)
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
|
||||
// Map all configured CQRS endpoints (automatically creates POST /api/command/* and POST/GET /api/query/*)
|
||||
app.UseSvrntyCqrs();
|
||||
|
||||
app.Run();
|
||||
```
|
||||
|
||||
**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
|
||||
## Contributing
|
||||
|
||||
## Sample enabling both gRPC and HTTP
|
||||
See [CLAUDE.md](./CLAUDE.md) for development guidelines.
|
||||
|
||||
You can enable both gRPC and traditional HTTP endpoints simultaneously, allowing clients to choose their preferred protocol:
|
||||
## License
|
||||
|
||||
```csharp
|
||||
using Svrnty.CQRS;
|
||||
using Svrnty.CQRS.FluentValidation;
|
||||
using Svrnty.CQRS.Grpc;
|
||||
using Svrnty.CQRS.MinimalApi;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Register your commands with validators
|
||||
builder.Services.AddCommand<AddUserCommand, int, AddUserCommandHandler, AddUserCommandValidator>();
|
||||
builder.Services.AddCommand<RemoveUserCommand, RemoveUserCommandHandler>();
|
||||
|
||||
// Register your queries
|
||||
builder.Services.AddQuery<FetchUserQuery, User, FetchUserQueryHandler>();
|
||||
|
||||
// Configure CQRS with both gRPC and Minimal API support
|
||||
builder.Services.AddSvrntyCqrs(cqrs =>
|
||||
{
|
||||
// Enable gRPC endpoints with reflection
|
||||
cqrs.AddGrpc(grpc =>
|
||||
{
|
||||
grpc.EnableReflection();
|
||||
});
|
||||
|
||||
// Enable Minimal API endpoints
|
||||
cqrs.AddMinimalApi();
|
||||
});
|
||||
|
||||
// Add HTTP support with Swagger
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
|
||||
// Map all configured CQRS endpoints (both gRPC and HTTP)
|
||||
app.UseSvrntyCqrs();
|
||||
|
||||
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
|
||||
|
||||
# Fluent Validation
|
||||
|
||||
FluentValidation is optional but recommended for command and query validation. The `Svrnty.CQRS.FluentValidation` package provides extension methods to simplify validator registration.
|
||||
|
||||
## 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 Svrnty.CQRS.FluentValidation; // Extension methods for validator registration
|
||||
|
||||
// Command with result - validator as last generic parameter
|
||||
builder.Services.AddCommand<EchoCommand, string, EchoCommandHandler, EchoCommandValidator>();
|
||||
|
||||
// Command without result - validator included in generics
|
||||
builder.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
|
||||
|
||||
## Without Svrnty.CQRS.FluentValidation
|
||||
|
||||
If you prefer not to use the FluentValidation package, you need to register commands and validators separately:
|
||||
|
||||
```csharp
|
||||
using FluentValidation;
|
||||
using Svrnty.CQRS;
|
||||
|
||||
// Register command handler
|
||||
builder.Services.AddCommand<EchoCommand, string, EchoCommandHandler>();
|
||||
|
||||
// Manually register validator
|
||||
builder.Services.AddTransient<IValidator<EchoCommand>, EchoCommandValidator>();
|
||||
```
|
||||
|
||||
# 2024-2025 Roadmap
|
||||
|
||||
| Task | Description | Status |
|
||||
|----------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------|--------|
|
||||
| Support .NET 10 | .NET 10 with C# 14 language support. | ✅ |
|
||||
| Update FluentValidation | Upgrade FluentValidation to version 11.x for .NET 10 compatibility. | ✅ |
|
||||
| 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. | ⬜️ |
|
||||
|
||||
# 2026 Roadmap
|
||||
|
||||
| Task | Description | Status |
|
||||
|----------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------|--------|
|
||||
| gRPC Compression Support | Smart message compression with automatic threshold detection and per-handler control. | ⬜️ |
|
||||
| gRPC Metadata & Authorization Support | Expose ServerCallContext to handlers and integrate authorization services for gRPC endpoints. | ⬜️ |
|
||||
MIT OR Apache-2.0
|
||||
|
||||
122
SAGAS_ROADMAP.md
122
SAGAS_ROADMAP.md
@ -1,122 +0,0 @@
|
||||
# Saga Orchestration Roadmap
|
||||
|
||||
## Completed (Phase 1)
|
||||
|
||||
- [x] `Svrnty.CQRS.Sagas.Abstractions` - Core interfaces and contracts
|
||||
- [x] `Svrnty.CQRS.Sagas` - Orchestration engine with fluent builder API
|
||||
- [x] `Svrnty.CQRS.Sagas.RabbitMQ` - RabbitMQ message transport
|
||||
|
||||
---
|
||||
|
||||
## Phase 1d: Testing & Sample
|
||||
|
||||
### Unit Tests
|
||||
- [ ] `SagaBuilder` step configuration tests
|
||||
- [ ] `SagaOrchestrator` execution flow tests
|
||||
- [ ] `SagaOrchestrator` compensation flow tests
|
||||
- [ ] `InMemorySagaStateStore` persistence tests
|
||||
- [ ] `RabbitMqSagaMessageBus` serialization tests
|
||||
|
||||
### Integration Tests
|
||||
- [ ] End-to-end saga execution with RabbitMQ
|
||||
- [ ] Multi-step saga with compensation scenario
|
||||
- [ ] Concurrent saga execution tests
|
||||
- [ ] Connection recovery tests
|
||||
|
||||
### Sample Implementation
|
||||
- [ ] `OrderProcessingSaga` example in WarehouseManagement
|
||||
- ReserveInventory step
|
||||
- ProcessPayment step
|
||||
- CreateShipment step
|
||||
- Full compensation flow
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Persistence
|
||||
|
||||
### Svrnty.CQRS.Sagas.EntityFramework
|
||||
- [ ] `EfCoreSagaStateStore` implementation
|
||||
- [ ] `SagaState` entity configuration
|
||||
- [ ] Migration support
|
||||
- [ ] PostgreSQL/SQL Server compatibility
|
||||
- [ ] Optimistic concurrency handling
|
||||
|
||||
### Configuration
|
||||
```csharp
|
||||
cqrs.AddSagas()
|
||||
.UseEntityFramework<AppDbContext>();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Reliability
|
||||
|
||||
### Saga Timeout Service
|
||||
- [ ] `SagaTimeoutHostedService` - background service for stalled sagas
|
||||
- [ ] Configurable timeout per saga type
|
||||
- [ ] Automatic compensation trigger on timeout
|
||||
- [ ] Dead letter handling for failed compensations
|
||||
|
||||
### Retry Policies
|
||||
- [ ] Exponential backoff support
|
||||
- [ ] Circuit breaker integration
|
||||
- [ ] Polly integration option
|
||||
|
||||
### Idempotency
|
||||
- [ ] Message deduplication
|
||||
- [ ] Idempotent step execution
|
||||
- [ ] Inbox/Outbox pattern support
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Observability
|
||||
|
||||
### OpenTelemetry Integration
|
||||
- [ ] Distributed tracing for saga execution
|
||||
- [ ] Span per saga step
|
||||
- [ ] Correlation ID propagation
|
||||
- [ ] Metrics (saga duration, success/failure rates)
|
||||
|
||||
### Saga Dashboard (Optional)
|
||||
- [ ] Web UI for saga monitoring
|
||||
- [ ] Real-time saga status
|
||||
- [ ] Manual compensation trigger
|
||||
- [ ] Saga history and audit log
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Flutter Integration
|
||||
|
||||
### gRPC Streaming for Saga Status
|
||||
- [ ] `ISagaStatusStream` service
|
||||
- [ ] Real-time saga progress updates
|
||||
- [ ] Step completion notifications
|
||||
- [ ] Error/compensation notifications
|
||||
|
||||
### Flutter Client
|
||||
- [ ] Dart client for saga status streaming
|
||||
- [ ] Saga progress widget components
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Alternative Transports
|
||||
|
||||
### Svrnty.CQRS.Sagas.AzureServiceBus
|
||||
- [ ] Azure Service Bus message transport
|
||||
- [ ] Topic/Subscription topology
|
||||
- [ ] Dead letter queue handling
|
||||
|
||||
### Svrnty.CQRS.Sagas.Kafka
|
||||
- [ ] Kafka message transport
|
||||
- [ ] Consumer group management
|
||||
- [ ] Partition key strategies
|
||||
|
||||
---
|
||||
|
||||
## Future Considerations
|
||||
|
||||
- **Event Sourcing**: Saga state as event stream
|
||||
- **Saga Versioning**: Handle saga definition changes gracefully
|
||||
- **Saga Composition**: Nested/child sagas
|
||||
- **Saga Scheduling**: Delayed saga start
|
||||
- **Multi-tenancy**: Tenant-aware saga execution
|
||||
53
SECURITY.md
Normal file
53
SECURITY.md
Normal file
@ -0,0 +1,53 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you discover a security vulnerability, please report it responsibly.
|
||||
|
||||
**Do NOT open a public issue.**
|
||||
|
||||
### How to Report
|
||||
|
||||
Email: **security@svrnty.com**
|
||||
|
||||
Include:
|
||||
- Description of the vulnerability
|
||||
- Steps to reproduce
|
||||
- Potential impact
|
||||
- Any suggested fixes (optional)
|
||||
|
||||
### Response Timeline
|
||||
|
||||
- **Acknowledgment**: Within 48 hours
|
||||
- **Initial Assessment**: Within 7 days
|
||||
- **Resolution Target**: Within 30 days (depending on severity)
|
||||
|
||||
### What to Expect
|
||||
|
||||
1. We will acknowledge receipt of your report
|
||||
2. We will investigate and validate the issue
|
||||
3. We will work on a fix and coordinate disclosure
|
||||
4. We will credit you (if desired) when the fix is released
|
||||
|
||||
### Scope
|
||||
|
||||
This policy applies to:
|
||||
- Code in this repository
|
||||
- Dependencies we control
|
||||
- Infrastructure we operate
|
||||
|
||||
### Out of Scope
|
||||
|
||||
- Third-party services or dependencies
|
||||
- Social engineering attacks
|
||||
- Physical security
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Security updates are provided for the latest release only.
|
||||
|
||||
| Version | Supported |
|
||||
|---------|-----------|
|
||||
| Latest | Yes |
|
||||
| Older | No |
|
||||
|
||||
Loading…
Reference in New Issue
Block a user