A powerful Command Query Responsibility Segregation (CQRS) framework built on the mediator pattern. This library provides core abstractions and implementations for building scalable, maintainable applications using CQRS principles.
- Clean CQRS Implementation - Separate read and write operations with clear command/query patterns
- Mediator Pattern - Built on Foundatio.Mediator for decoupled messaging
- Multiple Data Stores - Support for Entity Framework Core and MongoDB out of the box
- ASP.NET Core Integration - Minimal API endpoints for exposing commands and queries via HTTP
- Caching Support - Built-in caching abstractions for query optimization
- Dynamic Queries - Powerful filtering, sorting, and pagination with LINQ support
- Multi-Targeting - Supports .NET 8, .NET 9, and .NET 10
# Core package
dotnet add package Foundatio.CommandQuery
# Entity Framework integration
dotnet add package Foundatio.CommandQuery.EntityFramework
# MongoDB integration
dotnet add package Foundatio.CommandQuery.MongoDB
# ASP.NET Core endpoints
dotnet add package Foundatio.CommandQuery.Endpoints
# Dispatcher for Blazor
dotnet add package Foundatio.CommandQuery.DispatcherInstall-Package Foundatio.CommandQuery
Install-Package Foundatio.CommandQuery.EntityFramework
Install-Package Foundatio.CommandQuery.MongoDB
Install-Package Foundatio.CommandQuery.Endpoints
Install-Package Foundatio.CommandQuery.Dispatcher// Entity modelpublicclassTask:IHaveIdentifier<int>{publicintId{get;set;}publicstringTitle{get;set;}publicstringDescription{get;set;}publicDateTimeCreated{get;set;}publicstringCreatedBy{get;set;}}// Read modelpublicclassTaskReadModel:EntityReadModel<int>{publicstringTitle{get;set;}publicstringDescription{get;set;}}// Create modelpublicclassTaskCreateModel:EntityCreateModel<int>{publicstringTitle{get;set;}publicstringDescription{get;set;}}// Update modelpublicclassTaskUpdateModel:EntityUpdateModel{publicstringTitle{get;set;}publicstringDescription{get;set;}}usingFoundatio.CommandQuery.EntityFramework;publicclassTaskCommandHandler:EntityCommandHandler<AppDbContext,Task,int,TaskReadModel,TaskCreateModel,TaskUpdateModel>{publicTaskCommandHandler(AppDbContextcontext,IMappermapper):base(context,mapper){}}publicclassTaskQueryHandler:EntityQueryHandler<AppDbContext,Task,int,TaskReadModel>{publicTaskQueryHandler(AppDbContextcontext,IMappermapper):base(context,mapper){}}varbuilder=WebApplication.CreateBuilder(args);// Add mediator with assemblies containing handlersbuilder.Services.AddMediator(options =>options.AddAssembly(typeof(Program).Assembly));// Add your DbContextbuilder.Services.AddDbContext<AppDbContext>(options =>options.UseSqlServer(builder.Configuration.GetConnectionString("Default")));// Add endpoint routesbuilder.Services.AddEndpointRoutes();publicclassTaskEndpoint:EntityCommandEndpointBase<int,TaskListModel,TaskReadModel,TaskCreateModel,TaskUpdateModel>{publicTaskEndpoint(ILoggerFactoryloggerFactory):base(loggerFactory,"tasks","api/tasks"){}}varapp=builder.Build();// Map endpoint routesapp.MapEndpointRoutes();app.Run();This automatically creates the following endpoints:
GET /api/tasks/{id}- Get a task by IDGET /api/tasks- Query tasks with pagination and sortingPOST /api/tasks/query- Query tasks with advanced filteringGET /api/tasks/{id}/update- Get task update modelPOST /api/tasks- Create a new taskPUT /api/tasks/{id}- Update a taskDELETE /api/tasks/{id}- Delete a task
publicclassTaskService{privatereadonlyIMediator_mediator;publicTaskService(IMediatormediator){_mediator=mediator;}publicasyncTask<Result<TaskReadModel>>CreateTaskAsync(TaskCreateModelmodel,ClaimsPrincipaluser){varcommand=newCreateEntity<TaskCreateModel,TaskReadModel>(user,model);returnawait_mediator.InvokeAsync<Result<TaskReadModel>>(command);}publicasyncTask<Result<TaskReadModel>>GetTaskAsync(intid,ClaimsPrincipaluser){varquery=newGetEntity<int,TaskReadModel>(user,id);returnawait_mediator.InvokeAsync<Result<TaskReadModel>>(query);}publicasyncTask<Result<QueryResult<TaskReadModel>>>QueryTasksAsync(QueryDefinitionquery,ClaimsPrincipaluser){varcommand=newQueryEntities<TaskReadModel>(user,query);returnawait_mediator.InvokeAsync<Result<QueryResult<TaskReadModel>>>(command);}}// Query with filtering, sorting, and paginationvarqueryDefinition=newQueryDefinition{Filter=newQueryFilter{Logic=FilterLogic.And,Filters=newList<QueryFilter>{new(){Field="Status",Operator=FilterOperator.Equal,Value="Active"},new(){Field="Priority",Operator=FilterOperator.GreaterThan,Value=3}}},Sorts=newList<QuerySort>{new(){Name="Priority",Descending=true},new(){Name="Created",Descending=false}},Page=1,PageSize=20};varquery=newQueryEntities<TaskReadModel>(user,queryDefinition);varresult=await_mediator.InvokeAsync<Result<QueryResult<TaskReadModel>>>(query);usingFoundatio.CommandQuery.MongoDB;publicclassTaskCommandHandler:EntityCommandHandler<Task,int,TaskReadModel,TaskCreateModel,TaskUpdateModel>{publicTaskCommandHandler(IMongoDatabasedatabase,IMappermapper):base(database,mapper){}}publicclassTaskQueryHandler:EntityQueryHandler<Task,int,TaskReadModel>{publicTaskQueryHandler(IMongoDatabasedatabase,IMappermapper):base(database,mapper){}}publicrecordProcessTaskCommand:PrincipalCommand<CompleteModel>{publicProcessTaskCommand(ClaimsPrincipal?principal,stringaction):base(principal){Action=action;}publicstringAction{get;}}publicclassProcessTaskHandler:IRequestHandler<ProcessTaskCommand,CompleteModel>{publicasyncValueTask<CompleteModel>HandleAsync(ProcessTaskCommandrequest,CancellationTokencancellationToken=default){// Custom processing logicreturnCompleteModel.Success($"Processed: {request.Action}");}}Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.