PouryaKit.Cqrs
Commands and queries that run as Operations through one fixed pipeline.
Commands and queries that run as Operations through one fixed pipeline. You declare each Feature as a sealed record with exactly one handler and send it. The Kit runs it through logging, the optional authorization hook, optional validation and the handler, and a command's handler runs inside the Unit of Work, which commits once for the whole Operation.
Install
dotnet add package PouryaKit.Cqrs --prereleaseWhen to use it
- You are writing a Feature: a command that changes an aggregate, or a query that answers a read.
- A client may send a command again after a response it never received, and should get the first answer.
- A query returns a list that an Actor sorts, filters and pages.
- One command or query needs a rule of its own about who may run it.
It pairs with PouryaKit.Validation for input checks, PouryaKit.Modularity for a Module's Gateway, PouryaKit.Persistence.Postgres for the Unit of Work and PouryaKit.Web for endpoints.
Quick start
Register the Features in the Module's Composition Root:
module.AddPouryaKitCqrs();Declare the command and its one handler, as the Proving Sample does:
[Idempotent]
internal sealed record CreateProductCommand(Guid ProductId, Guid CategoryId, string Name)
: Command<Guid>;
internal sealed class CreateProductCommandHandler(
IRepository<Product, ProductId> products,
OperationContext context
) : CommandHandler<CreateProductCommand, Guid>
{
public override async Task<Result<Guid>> Handle(
CreateProductCommand command,
CancellationToken cancellationToken
)
{
var product = Product.Create(
new ProductId(command.ProductId),
new CategoryId(command.CategoryId),
command.Name,
context.Tenant!
);
if (product.IsFailure)
{
return product.Errors;
}
await products.AddAsync(product.Value, cancellationToken);
return product.Value.Id.Value;
}
}What you get
- One fixed pipeline for every command and query, with nothing to add per Feature.
- A
Resultfrom every handler, so an expected business failure is an Error and never an exception. - An
OperationContextthat says who and what caused the Operation, wherever it runs. [Idempotent]on a command, so a repeat of a request answers with the first success.- A Page declared on a query, sorted and filtered only by what the query allows.
- A Host that refuses its own composition mistakes when it is built, with a composition check that names each one.
A handler returns a Result and never saves. The Unit of Work owns saving, committing, transactions and the Outbox (Persistence and the Unit of Work).