{"id":21328418,"url":"https://github.com/wiredviews/xperience-community-cqrs","last_synced_at":"2026-04-01T21:50:43.005Z","repository":{"id":98317892,"uuid":"342651124","full_name":"wiredviews/xperience-community-cqrs","owner":"wiredviews","description":"A CQRS implementation influenced by jbogard/MediatR combined with vkhorikov/CSharpFunctionalExtensions Monads for Kentico Xperience applications.","archived":false,"fork":false,"pushed_at":"2022-10-21T04:47:44.000Z","size":237,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-03-27T13:44:26.162Z","etag":null,"topics":["aspnetcore","csharp","kentico","kentico-xperience","monad","xperience"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wiredviews.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2021-02-26T17:32:37.000Z","updated_at":"2022-10-01T03:03:49.000Z","dependencies_parsed_at":"2023-04-26T10:08:17.067Z","dependency_job_id":null,"html_url":"https://github.com/wiredviews/xperience-community-cqrs","commit_stats":null,"previous_names":["wiredviews/xperience-cqrs"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/wiredviews/xperience-community-cqrs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wiredviews%2Fxperience-community-cqrs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wiredviews%2Fxperience-community-cqrs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wiredviews%2Fxperience-community-cqrs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wiredviews%2Fxperience-community-cqrs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wiredviews","download_url":"https://codeload.github.com/wiredviews/xperience-community-cqrs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wiredviews%2Fxperience-community-cqrs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31046909,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-27T09:35:52.079Z","status":"ssl_error","status_checked_at":"2026-03-27T09:35:20.916Z","response_time":164,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aspnetcore","csharp","kentico","kentico-xperience","monad","xperience"],"created_at":"2024-11-21T21:28:52.435Z","updated_at":"2026-04-01T21:50:42.973Z","avatar_url":"https://github.com/wiredviews.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Xperience Community - CQRS\n\n[![NuGet Package](https://img.shields.io/nuget/v/XperienceCommunity.CQRS.Core.svg)](https://www.nuget.org/packages/XperienceCommunity.CQRS.Core)\n\n[![NuGet Package](https://img.shields.io/nuget/v/XperienceCommunity.CQRS.Data.svg)](https://www.nuget.org/packages/XperienceCommunity.CQRS.Data)\n\n[![NuGet Package](https://img.shields.io/nuget/v/XperienceCommunity.CQRS.Web.svg)](https://www.nuget.org/packages/XperienceCommunity.CQRS.Web)\n\nA CQRS implementation influenced by \u003chttps://github.com/jbogard/MediatR/\u003e\ncombined with \u003chttps://github.com/vkhorikov/CSharpFunctionalExtensions\u003e Maybe and Result monads for Kentico Xperience applications.\n\n## Dependencies\n\nThis package is compatible with ASP.NET Core 6+ and is designed to be used with Kentico Xperience 13.0\n\n\u003e Note: This library requires [Kentico Xperience 13: Refresh 1](https://docs.xperience.io/release-notes-xperience-13#ReleasenotesXperience13-Ref1)\n\u003e (Hotfix 13.0.16 and later).\n\n## How to Use?\n\n1. This library is separated into (3) NuGet packages.\n   Each package is associated with a different part of the [Onion Architecture](https://jeffreypalermo.com/2008/07/the-onion-architecture-part-1/).\n   You can install all of these into your web application project or isolate your Kentico Xperience types behind abstractions.\n\n   - `XperienceCommunity.CQRS.Core`\n\n     - Abstractions and implementations for the Domain, Persistence, and Presentation layers to implement.\n     - Typically the project consuming this package doesn't have any reference to Kentico Xperience packages or APIs.\n\n   - `XperienceCommunity.CQRS.Data`\n\n     - Decorators and base classes for data access implementations.\n     - The project consuming this package also uses Kentico Xperience data access APIs.\n\n   - `XperienceCommunity.CQRS.Web`\n     - Presentation layer cache and service collection registration utilities.\n     - The project consuming this package is where your presentation logic is located (Razor/View Components/Controllers).\n\n1. First, install the NuGet package(s) in your ASP.NET Core projects (see the example project under [/samples](https://github.com/wiredviews/xperience-community-cqrs/tree/main/samples))\n\n   ```bash\n   dotnet add package XperienceCommunity.CQRS.Core\n   dotnet add package XperienceCommunity.CQRS.Data\n   dotnet add package XperienceCommunity.CQRS.Web\n   ```\n\n1. Create a new implementation of `IQuery\u003cT\u003e`\n\n   ```csharp\n   public record HomePageQuery : IQuery\u003cHomePageQueryData\u003e;\n   public record HomePageQueryData(int DocumentID, string Title, Maybe\u003cstring\u003e BodyHTML);\n   ```\n\n1. Create a new implementation of `IQueryHandler\u003cTQuery, TResponse\u003e`\n\n   ```csharp\n   public class HomePageQueryHandler : CacheableQueryHandler\u003cHomePageQuery, HomePageQueryData\u003e\n   {\n       private readonly IPageRetriever retriever;\n\n       public HomePageQueryHandler(IPageRetriever retriever) =\u003e this.retriever = retriever;\n\n       public override Task\u003cResult\u003cHomePageQueryData\u003e\u003e Execute(HomePageQuery query, CancellationToken token) =\u003e\n           pageRetriever.RetrieveAsync\u003cHomePage\u003e(q =\u003e q.TopN(1), cancellationToken: token)\n               .TryFirst()\n               .ToResult($\"Could not find any [{HomePage.CLASS_NAME}]\")\n               .Map(homePage =\u003e\n               {\n                   var bodyHTML = string.IsNullOrWhiteSpace(p.Fields.BodyHTML)\n                       ? Maybe\u003cstring\u003e.None\n                       : p.Fields.BodyHTML;\n\n                   return new HomePageQueryData(homePage.DocumentID, homePage.Fields.Title, bodyHTML);\n               });\n\n       protected override ICacheDependencyKeysBuilder AddDependencyKeys(\n           HomePageQuery query,\n           HomePageQueryData response,\n           ICacheDependencyKeysBuilder builder) =\u003e\n           builder.Page(response.DocumentID);\n   }\n   ```\n\n   \u003e Note: To be identified by the library, all Query and Command handler classes must be named\n   \u003e with the suffix `QueryHandler` or `CommandHandler`.\n\n1. Register the library's dependencies with the service collection\n\n   ```csharp\n   public class Startup\n   {\n       public void ConfigureServices(IServiceCollection services)\n       {\n           services.AddXperienceCQRS(typeof(HomePageQueryHandler).Assembly);\n\n           // ...\n       }\n   }\n   ```\n\n1. (Optional) Configure cache settings through `appsettings.json`:\n\n   ```json\n   \"XperienceCommunity\": {\n       \"CQRS\": {\n           \"Caching\": {\n                \"Razor\": {\n                    \"CacheSlidingExpiration\": \"00:02:00\",\n                    \"CacheAbsoluteExpiration\": \"00:06:00\",\n                    \"IsEnabled\": true\n                },\n                \"Query\": {\n                    \"CacheItemDuration\": \"00:10:00\",\n                    \"IsEnabled\": true,\n                    \"IsSlidingExpiration\": true\n                }\n           }\n       }\n   }\n   ```\n\n   \u003e Note: If using `dotnet watch` for local development, it's recommended to disable Razor and Query caching because\n   \u003e .NET's hot reloading does not know about the memory caching and changes to code might not be\n   \u003e reflected due to cached information.\n\n1. (Optional) Configure cache settings through dependency injection:\n\n   ```csharp\n   public class Startup\n   {\n      public void ConfigureServices(IServiceCollection services)\n      {\n          services\n            .AddXperienceCQRS(typeof(HomePageQueryHandler).Assembly)\n            .Configure\u003cRazorCacheConfiguration\u003e(c =\u003e\n            {\n                c.IsEnabled = false;\n            })\n            .Configure\u003cQueryCacheConfiguration\u003e(c =\u003e\n            {\n                c.CacheItemDuration = TimeSpan.FromMinutes(20);\n            });\n      }\n   }\n   ```\n\n1. Use queries in your MVC Controllers, application services, or View Components\n\n   ```csharp\n   public class HomePageViewComponent : ViewComponent\n   {\n       private readonly IQueryDispatcher dispatcher;\n\n       public HomePageViewComponent(IQueryDispatcher dispatcher) =\u003e\n           this.dispatcher = dispatcher;\n\n       public Task\u003cIViewComponentResult\u003e Invoke() =\u003e\n           dispatcher.Dispatch(new HomePageQuery(), HttpContext.RequestAborted)\n               .ViewWithFallbackOnFailure(this, \"HomePage\", data =\u003e new HomePageViewModel(data));\n   }\n   ```\n\n   ```razor\n   @using Microsoft.AspNetCore.Html\n   @using XperienceCommunity.Sandbox.Web.Features.Home.Components\n   @model HomePageViewModel\n\n   \u003ch1\u003e@Model.Title\u003c/h1\u003e\n\n   @Model.BodyHTML.GetValueOrDefault(HtmlString.Empty)\n\n   @if (Model.ImagePath.TryGetValue(out string path))\n   {\n       \u003cimg src=\"@path\" alt=\"@Model.Title\" /\u003e\n   }\n   ```\n\n## How Does It Work?\n\nThis library's primary goal is to isolate data access into individual operations, with explicit and type-safe\ncache item names and dependencies. It models both content and operations with `Maybe` and `Result` monads.\n\nMost Kentico Xperience 13.0 sites focus on data retrieval for the ASP.NET Core application and this library focuses on supporting\nrobust data access. It also encourages data submission/modification operations\n(ex: commerce, external system integrations, user data management) to be separated from data retrieval.\n\n## Contributing\n\nTo build this project, you must have v6.0.300 or higher\nof the [.NET SDK](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) installed.\n\n## References\n\n### Kentico Xperience\n\n- [Kentico 12: Design Patterns Part 12 - Database Query Caching Patterns](https://dev.to/seangwright/kentico-12-design-patterns-part-12-database-query-caching-patterns-43hc)\n- [Kentico Xperience Design Patterns: MVC is Dead, Long Live PTVC](https://dev.to/seangwright/kentico-xperience-design-patterns-mvc-is-dead-long-live-ptvc-4635)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwiredviews%2Fxperience-community-cqrs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwiredviews%2Fxperience-community-cqrs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwiredviews%2Fxperience-community-cqrs/lists"}