diff --git a/README.md b/README.md index 7adbc76..b55a21d 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ These samples aren't designed to teach how to model complex domains using actors 1. [Akka.NET Cluster.Sharding with Akka.Persistence.SqlServer and Razor Pages](https://github.com/petabridge/akkadotnet-code-samples/tree/master/src/clustering/sharding-sqlserver) 2. [Akka.Streams.Amqp.RabbitMQ with Akka.Cluster.Sharding - Reliable Delivery + Backpressure Support](https://github.com/petabridge/akkadotnet-code-samples/tree/master/src/reliability/rabbitmq-backpressure) +3. [Event-Sourcing and CQRS with Akka.Peristence and Akka.Peristence.Query](https://github.com/petabridge/akkadotnet-code-samples/tree/master/src/cqrs/cqrs-sqlserver) ## Contributing @@ -50,8 +51,7 @@ You are free to modify and use these diagrams in your own derivative works as lo [Petabridge](https://petabridge.com/) is a company dedicated to making it easier for .NET developers to build distributed applications. -Petabridge provides Akka.NET consulting and training, including advanced training in [Akka.Remote](https://petabridge.com/training/akka-remoting/), [Akka.Cluster](https://petabridge.com/training/akka-clustering/), and [Akka.NET Design Patterns](https://petabridge.com/training/akka-design-patterns/)! +Petabridge provides [Akka.NET support, consulting, and training](https://petabridge.com/services/support/). --- -Copyright 2015 - 2022 Petabridge, LLC - +Copyright 2015 - 2024 Petabridge, LLC diff --git a/src/clustering/sharding-sqlserver/README.md b/src/clustering/sharding-sqlserver/README.md index 593a49d..234e9e7 100644 --- a/src/clustering/sharding-sqlserver/README.md +++ b/src/clustering/sharding-sqlserver/README.md @@ -6,9 +6,9 @@ The goal of this sample is to demonstrate how to host Akka.Cluster.Sharding with This solution is built with: -- .NET 6 minimal APIs; +- Minimal APIs; - C# `record` types; -- ASP.NET Core 6; +- ASP.NET Core; - Google.Protobuf for message and state schema; - Akka.NET v1.5 w/ Akka.Cluster; - Akka.Persistence.SqlServer; and diff --git a/src/cqrs/cqrs-sqlserver/README.md b/src/cqrs/cqrs-sqlserver/README.md new file mode 100644 index 0000000..7925a6d --- /dev/null +++ b/src/cqrs/cqrs-sqlserver/README.md @@ -0,0 +1,91 @@ +# Akka.NET Event-Sourcing and CQRS with Akka.Persistence + +The goal of this sample is to demonstrate: + +1. How do to basic event-sourcing with Akka.Persistence; +2. How to use Akka.Persistence.Query to project events written to Akka.Persistence; and +3. How to use Entity Framework Core to create CQRS-style read models that are used by the frontend application. + +## Technology + +This solution is built with: + +- Minimal APIs; +- C# `record` types; +- [Blazor](https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor) and [MudBlazor](https://www.mudblazor.com/); +- Google.Protobuf for message and state schema; +- Akka.NET v1.5 w/ Akka.Persistence and [Akka.Persistence.Query](https://getakka.net/articles/persistence/persistence-query.html); +- [Entity Framework Core](https://learn.microsoft.com/en-us/ef/core/), for our read models; +- [Akka.Persistence.Sql](https://github.com/akkadotnet/Akka.Persistence.Sql); and +- [Akka.Hosting](https://github.com/akkadotnet/Akka.Hosting) - which minimizes the amount of configuration for Akka.NET to practically zero. + +## Domain + +Like all of the samples in this repository, we are using a simple domain since the focus of the sample is meant to be _how to use Akka.NET infrastructure succinctly and successfully_. That being said, it's still worth understanding what our domain does: product inventory + revenue tracking. It's the same domain as ["Akka.NET Cluster.Sharding with Akka.Persistence.SqlServer and Razor Pages"](../../clustering/sharding-sqlserver). + +> This app does not use Akka.Cluster, although it could be easily modified to do so (nothing would really change other than migrating some actors to `ShardRegion`s and `SingletonManager`s). + +In the `CqrsSqlServer.Backend` application: + +1. **`ProductTotalsActor`** - our `ReceivePersistentActor` that will process `IProductCommand `, transforming them into (0..N) `IProductEvent`s, updating its `ProductState`, and replying to the original sender with a `ProductCommandResponse` with the results of each command. This entity actor demonstrates one of the more robust ways of executing event-sourcing on top of Akka.Persistence using C#9 `record` types and cleanly separating command / event / query message types from each other. +2. **`ProductProjectorActor`** - a singleton `ReceivePersistentActor` that functions as a projector + materialized view creator for all of the events saved by _all_ of the `ProductTotalsActor` instance. It uses Akka.Persistence.Query's `EventsByTag` functionality to query all of the events tagged by the `MessageTagger` - and then safely renders read models using the `CqrsSqlServerContext` EF Core model. + +In the `CqrsSqlServer.Frontend` application, we don't use Akka.NET _at all_ - we just spin up a simple Blazor UI that reads data that reads the `CqrsSqlServerContext` read models. + +## Running This Sample + +### Launch Dependencies + +To run this sample, we first need to spin up our dependencies - a SQL Server instance with a predefined `Akka` database ready to be configured. + +**Windows** + +```shell +start-dependencies.cmd +``` + +**Linux or OS X** + +```shell +./start-dependencies.sh +``` + +This will build a copy of our [MSSQL image](https://github.com/petabridge/akkadotnet-code-samples/tree/master/infrastructure/mssql) and run it on port 1533. The sample is already pre-configured to connect to it at startup. + +### `dotnet ef` Dependency + +`start-dependencies.cmd` and `start-dependencies.sh` will also run `dotnet ef database update` - make sure you have the Entity Framework Core `dotnet` CLI installed! + +```shell +dotnet tool install --global dotnet-ef +``` + +or update to the latest + +```shell +dotnet tool update --global dotnet-ef +``` +### Run the Sample + +First, take a look at the `appSettings.json` for `CqrsSqlServer.Backend.csproj`: + +```json +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + }, + "SeedDb": true +} +``` + +Make sure you run with `"SeedDb": true` the first time you launch the application, otherwise it won't do anything. + +If you run the application a second time with this setting, some of the random data generated by Bogus might conflict with data already stored inside the application - that should be fine. Those events will just get ignored by the persistent actors. + +Launch `CqrsSqlServer.Backend.csproj` via Visual Studio, Rider, or the `dotnet` CLI first. + +Then, once that app is running, launch `CqrsSqlServer.Frontend.csproj` and go to http://localhost:5191/