From 87480d9118f227aad8e199726d422cec68d071c9 Mon Sep 17 00:00:00 2001 From: Oskar Dudycz Date: Thu, 2 May 2024 16:58:37 +0200 Subject: [PATCH] Added slimmed business logic exercise --- EventSourcing.NetCore.sln | 7 + .../07-BusinessLogic.Slimmed.csproj | 28 +++ .../Immutable/BusinessLogicTests.cs | 108 ++++++++ .../Immutable/PricingCalculator.cs | 24 ++ .../Immutable/ShoppingCart.cs | 136 ++++++++++ .../Immutable/ShoppingCartService.cs | 109 ++++++++ .../Mixed/Aggregate.cs | 8 + .../Mixed/BusinessLogicTests.cs | 98 ++++++++ .../Mixed/PricingCalculator.cs | 21 ++ .../Mixed/ShoppingCart.cs | 236 ++++++++++++++++++ .../Mutable/Aggregate.cs | 24 ++ .../Mutable/BusinessLogicTests.cs | 98 ++++++++ .../Mutable/PricingCalculator.cs | 21 ++ .../Mutable/ShoppingCart.cs | 236 ++++++++++++++++++ .../07-BusinessLogic.Slimmed/README.md | 28 +++ .../Tools/EventStore.cs | 24 ++ .../assets/events.jpg | Bin 0 -> 67506 bytes 17 files changed, 1206 insertions(+) create mode 100644 Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/07-BusinessLogic.Slimmed.csproj create mode 100644 Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/BusinessLogicTests.cs create mode 100644 Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/PricingCalculator.cs create mode 100644 Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/ShoppingCart.cs create mode 100644 Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/ShoppingCartService.cs create mode 100644 Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/Aggregate.cs create mode 100644 Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/BusinessLogicTests.cs create mode 100644 Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/PricingCalculator.cs create mode 100644 Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/ShoppingCart.cs create mode 100644 Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/Aggregate.cs create mode 100644 Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/BusinessLogicTests.cs create mode 100644 Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/PricingCalculator.cs create mode 100644 Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/ShoppingCart.cs create mode 100644 Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/README.md create mode 100644 Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Tools/EventStore.cs create mode 100644 Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/assets/events.jpg diff --git a/EventSourcing.NetCore.sln b/EventSourcing.NetCore.sln index ba2b5266f..4f7fdf5f6 100644 --- a/EventSourcing.NetCore.sln +++ b/EventSourcing.NetCore.sln @@ -465,6 +465,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "11-OptimisticConcurrency.Ev EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "11-OptimisticConcurrency.EventStoreDB.Tests", "Workshops\IntroductionToEventSourcing\11-OptimisticConcurrency.EventStoreDB.Tests\11-OptimisticConcurrency.EventStoreDB.Tests.csproj", "{37E05147-F579-458D-9B4F-25B54AC078DE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "07-BusinessLogic.Slimmed", "Workshops\IntroductionToEventSourcing\07-BusinessLogic.Slimmed\07-BusinessLogic.Slimmed.csproj", "{B7DBA2CE-DAA9-42C0-A967-E4B62297EC1D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1043,6 +1045,10 @@ Global {37E05147-F579-458D-9B4F-25B54AC078DE}.Debug|Any CPU.Build.0 = Debug|Any CPU {37E05147-F579-458D-9B4F-25B54AC078DE}.Release|Any CPU.ActiveCfg = Release|Any CPU {37E05147-F579-458D-9B4F-25B54AC078DE}.Release|Any CPU.Build.0 = Release|Any CPU + {B7DBA2CE-DAA9-42C0-A967-E4B62297EC1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B7DBA2CE-DAA9-42C0-A967-E4B62297EC1D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B7DBA2CE-DAA9-42C0-A967-E4B62297EC1D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B7DBA2CE-DAA9-42C0-A967-E4B62297EC1D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1234,6 +1240,7 @@ Global {3AE580AB-209E-4F2A-AE7E-0BB89602A338} = {14C7B928-9D6C-441A-8A1F-0C49173E73EB} {7829C11A-59AF-4C10-A679-312B8940A68D} = {14C7B928-9D6C-441A-8A1F-0C49173E73EB} {37E05147-F579-458D-9B4F-25B54AC078DE} = {14C7B928-9D6C-441A-8A1F-0C49173E73EB} + {B7DBA2CE-DAA9-42C0-A967-E4B62297EC1D} = {14C7B928-9D6C-441A-8A1F-0C49173E73EB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A5F55604-2FF3-43B7-B657-4F18E6E95D3B} diff --git a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/07-BusinessLogic.Slimmed.csproj b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/07-BusinessLogic.Slimmed.csproj new file mode 100644 index 000000000..e944715a4 --- /dev/null +++ b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/07-BusinessLogic.Slimmed.csproj @@ -0,0 +1,28 @@ + + + + net8.0 + IntroductionToEventSourcing.BusinessLogic.Slimmed + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + diff --git a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/BusinessLogicTests.cs b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/BusinessLogicTests.cs new file mode 100644 index 000000000..a2dda3494 --- /dev/null +++ b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/BusinessLogicTests.cs @@ -0,0 +1,108 @@ +using FluentAssertions; +using IntroductionToEventSourcing.BusinessLogic.Slimmed.Tools; +using Xunit; + +namespace IntroductionToEventSourcing.BusinessLogic.Slimmed.Immutable; + +using static ShoppingCartEvent; +using static ShoppingCartCommand; + +public static class ShoppingCartExtensions +{ + public static ShoppingCart GetShoppingCart(this EventStore eventStore, Guid shoppingCartId) => + eventStore.ReadStream(shoppingCartId).Aggregate(ShoppingCart.Default(), ShoppingCart.Evolve); +} + +public class BusinessLogicTests +{ + [Fact] + public void RunningSequenceOfBusinessLogic_ShouldGenerateSequenceOfEvents() + { + var shoppingCartId = Guid.NewGuid(); + var clientId = Guid.NewGuid(); + var shoesId = Guid.NewGuid(); + var tShirtId = Guid.NewGuid(); + var twoPairsOfShoes = new ProductItem(shoesId, 2); + var pairOfShoes = new ProductItem(shoesId, 1); + var tShirt = new ProductItem(tShirtId, 1); + + var shoesPrice = 100; + var tShirtPrice = 50; + + var pricedPairOfShoes = new PricedProductItem(shoesId, 1, shoesPrice); + var pricedTShirt = new PricedProductItem(tShirtId, 1, tShirtPrice); + + var eventStore = new EventStore(); + + // Open + ShoppingCartEvent result = + ShoppingCartService.Handle( + new OpenShoppingCart(shoppingCartId, clientId) + ); + eventStore.AppendToStream(shoppingCartId, (object[])[result]); + + // Add Two Pair of Shoes + var shoppingCart = eventStore.GetShoppingCart(shoppingCartId); + result = ShoppingCartService.Handle( + FakeProductPriceCalculator.Returning(shoesPrice), + new AddProductItemToShoppingCart(shoppingCartId, twoPairsOfShoes), + shoppingCart + ); + eventStore.AppendToStream(shoppingCartId, (object[])[result]); + + // Add T-Shirt + shoppingCart = eventStore.GetShoppingCart(shoppingCartId); + result = ShoppingCartService.Handle( + FakeProductPriceCalculator.Returning(tShirtPrice), + new AddProductItemToShoppingCart(shoppingCartId, tShirt), + shoppingCart + ); + eventStore.AppendToStream(shoppingCartId, (object[])[result]); + + // Remove a pair of shoes + shoppingCart = eventStore.GetShoppingCart(shoppingCartId); + result = ShoppingCartService.Handle( + new RemoveProductItemFromShoppingCart(shoppingCartId, pricedPairOfShoes), + shoppingCart + ); + eventStore.AppendToStream(shoppingCartId, (object[])[result]); + + // Confirm + shoppingCart = eventStore.GetShoppingCart(shoppingCartId); + result = ShoppingCartService.Handle( + new ConfirmShoppingCart(shoppingCartId), + shoppingCart + ); + eventStore.AppendToStream(shoppingCartId, (object[])[result]); + + // Try Cancel + var exception = Record.Exception(() => + { + shoppingCart = eventStore.GetShoppingCart(shoppingCartId); + result = ShoppingCartService.Handle( + new CancelShoppingCart(shoppingCartId), + shoppingCart + ); + eventStore.AppendToStream(shoppingCartId, (object[])[result]); + }); + exception.Should().BeOfType(); + + shoppingCart = eventStore.GetShoppingCart(shoppingCartId); + + shoppingCart.Id.Should().Be(shoppingCartId); + shoppingCart.ClientId.Should().Be(clientId); + shoppingCart.ProductItems.Should().HaveCount(2); + shoppingCart.Status.Should().Be(ShoppingCartStatus.Confirmed); + + shoppingCart.ProductItems[0].Should().Be(pricedPairOfShoes); + shoppingCart.ProductItems[1].Should().Be(pricedTShirt); + + var events = eventStore.ReadStream(shoppingCartId); + events.Should().HaveCount(5); + events[0].Should().BeOfType(); + events[1].Should().BeOfType(); + events[2].Should().BeOfType(); + events[3].Should().BeOfType(); + events[4].Should().BeOfType(); + } +} diff --git a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/PricingCalculator.cs b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/PricingCalculator.cs new file mode 100644 index 000000000..013d92548 --- /dev/null +++ b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/PricingCalculator.cs @@ -0,0 +1,24 @@ +namespace IntroductionToEventSourcing.BusinessLogic.Slimmed.Immutable; + +public interface IProductPriceCalculator +{ + PricedProductItem Calculate(ProductItem productItems); +} + +public class FakeProductPriceCalculator: IProductPriceCalculator +{ + private readonly int value; + + private FakeProductPriceCalculator(int value) + { + this.value = value; + } + + public static FakeProductPriceCalculator Returning(int value) => new(value); + + public PricedProductItem Calculate(ProductItem productItem) + { + var (productId, quantity) = productItem; + return new PricedProductItem(productId, quantity, value); + } +} diff --git a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/ShoppingCart.cs b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/ShoppingCart.cs new file mode 100644 index 000000000..300fa5219 --- /dev/null +++ b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/ShoppingCart.cs @@ -0,0 +1,136 @@ +namespace IntroductionToEventSourcing.BusinessLogic.Slimmed.Immutable; +using static ShoppingCartEvent; + +// EVENTS +public abstract record ShoppingCartEvent +{ + public record ShoppingCartOpened( + Guid ShoppingCartId, + Guid ClientId + ): ShoppingCartEvent; + + public record ProductItemAddedToShoppingCart( + Guid ShoppingCartId, + PricedProductItem ProductItem + ): ShoppingCartEvent; + + public record ProductItemRemovedFromShoppingCart( + Guid ShoppingCartId, + PricedProductItem ProductItem + ): ShoppingCartEvent; + + public record ShoppingCartConfirmed( + Guid ShoppingCartId, + DateTime ConfirmedAt + ): ShoppingCartEvent; + + public record ShoppingCartCanceled( + Guid ShoppingCartId, + DateTime CanceledAt + ): ShoppingCartEvent; + + // This won't allow external inheritance + private ShoppingCartEvent(){} +} + +// VALUE OBJECTS +public record PricedProductItem( + Guid ProductId, + int Quantity, + decimal UnitPrice +) +{ + public decimal TotalPrice => Quantity * UnitPrice; +} + +public record ProductItem(Guid ProductId, int Quantity); + + +// ENTITY +public record ShoppingCart( + Guid Id, + Guid ClientId, + ShoppingCartStatus Status, + PricedProductItem[] ProductItems, + DateTime? ConfirmedAt = null, + DateTime? CanceledAt = null +) +{ + public bool IsClosed => ShoppingCartStatus.Closed.HasFlag(Status); + + public bool HasEnough(PricedProductItem productItem) + { + var (productId, quantity, _) = productItem; + var currentQuantity = ProductItems.Where(pi => pi.ProductId == productId) + .Select(pi => pi.Quantity) + .FirstOrDefault(); + + return currentQuantity >= quantity; + } + + public static ShoppingCart Default() => + new (default, default, default, []); + + public static ShoppingCart Evolve(ShoppingCart shoppingCart, ShoppingCartEvent @event) + { + return @event switch + { + ShoppingCartOpened(var shoppingCartId, var clientId) => + shoppingCart with + { + Id = shoppingCartId, + ClientId = clientId, + Status = ShoppingCartStatus.Pending + }, + ProductItemAddedToShoppingCart(_, var pricedProductItem) => + shoppingCart with + { + ProductItems = shoppingCart.ProductItems + .Concat(new [] { pricedProductItem }) + .GroupBy(pi => pi.ProductId) + .Select(group => group.Count() == 1? + group.First() + : new PricedProductItem( + group.Key, + group.Sum(pi => pi.Quantity), + group.First().UnitPrice + ) + ) + .ToArray() + }, + ProductItemRemovedFromShoppingCart(_, var pricedProductItem) => + shoppingCart with + { + ProductItems = shoppingCart.ProductItems + .Select(pi => pi.ProductId == pricedProductItem.ProductId? + pi with { Quantity = pi.Quantity - pricedProductItem.Quantity } + :pi + ) + .Where(pi => pi.Quantity > 0) + .ToArray() + }, + ShoppingCartConfirmed(_, var confirmedAt) => + shoppingCart with + { + Status = ShoppingCartStatus.Confirmed, + ConfirmedAt = confirmedAt + }, + ShoppingCartCanceled(_, var canceledAt) => + shoppingCart with + { + Status = ShoppingCartStatus.Canceled, + CanceledAt = canceledAt + }, + _ => shoppingCart + }; + } +} + +public enum ShoppingCartStatus +{ + Pending = 1, + Confirmed = 2, + Canceled = 4, + + Closed = Confirmed | Canceled +} diff --git a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/ShoppingCartService.cs b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/ShoppingCartService.cs new file mode 100644 index 000000000..098ea10d8 --- /dev/null +++ b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Immutable/ShoppingCartService.cs @@ -0,0 +1,109 @@ +namespace IntroductionToEventSourcing.BusinessLogic.Slimmed.Immutable; +using static ShoppingCartEvent; +using static ShoppingCartCommand; + +public abstract record ShoppingCartCommand +{ + public record OpenShoppingCart( + Guid ShoppingCartId, + Guid ClientId + ): ShoppingCartCommand; + + public record AddProductItemToShoppingCart( + Guid ShoppingCartId, + ProductItem ProductItem + ); + + public record RemoveProductItemFromShoppingCart( + Guid ShoppingCartId, + PricedProductItem ProductItem + ); + + public record ConfirmShoppingCart( + Guid ShoppingCartId + ); + + public record CancelShoppingCart( + Guid ShoppingCartId + ): ShoppingCartCommand; + + private ShoppingCartCommand() {} +} + +public static class ShoppingCartService +{ + public static ShoppingCartOpened Handle(OpenShoppingCart command) + { + var (shoppingCartId, clientId) = command; + + return new ShoppingCartOpened( + shoppingCartId, + clientId + ); + } + + public static ProductItemAddedToShoppingCart Handle( + IProductPriceCalculator priceCalculator, + AddProductItemToShoppingCart command, + ShoppingCart shoppingCart + ) + { + var (cartId, productItem) = command; + + if (shoppingCart.IsClosed) + throw new InvalidOperationException( + $"Adding product item for cart in '{shoppingCart.Status}' status is not allowed."); + + var pricedProductItem = priceCalculator.Calculate(productItem); + + return new ProductItemAddedToShoppingCart( + cartId, + pricedProductItem + ); + } + + public static ProductItemRemovedFromShoppingCart Handle( + RemoveProductItemFromShoppingCart command, + ShoppingCart shoppingCart + ) + { + var (cartId, productItem) = command; + + if (shoppingCart.IsClosed) + throw new InvalidOperationException( + $"Adding product item for cart in '{shoppingCart.Status}' status is not allowed."); + + if (!shoppingCart.HasEnough(productItem)) + throw new InvalidOperationException("Not enough product items to remove"); + + return new ProductItemRemovedFromShoppingCart( + cartId, + productItem + ); + } + + public static ShoppingCartConfirmed Handle(ConfirmShoppingCart command, ShoppingCart shoppingCart) + { + if (shoppingCart.IsClosed) + throw new InvalidOperationException($"Confirming cart in '{shoppingCart.Status}' status is not allowed."); + + if(shoppingCart.ProductItems.Length == 0) + throw new InvalidOperationException($"Cannot confirm empty shopping cart"); + + return new ShoppingCartConfirmed( + shoppingCart.Id, + DateTime.UtcNow + ); + } + + public static ShoppingCartCanceled Handle(CancelShoppingCart command, ShoppingCart shoppingCart) + { + if (shoppingCart.IsClosed) + throw new InvalidOperationException($"Canceling cart in '{shoppingCart.Status}' status is not allowed."); + + return new ShoppingCartCanceled( + shoppingCart.Id, + DateTime.UtcNow + ); + } +} diff --git a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/Aggregate.cs b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/Aggregate.cs new file mode 100644 index 000000000..8934d35ed --- /dev/null +++ b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/Aggregate.cs @@ -0,0 +1,8 @@ +namespace IntroductionToEventSourcing.BusinessLogic.Slimmed.Mixed; + +public interface IAggregate +{ + Guid Id { get; } + + void Evolve(object @event) { } +} diff --git a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/BusinessLogicTests.cs b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/BusinessLogicTests.cs new file mode 100644 index 000000000..926c0d29e --- /dev/null +++ b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/BusinessLogicTests.cs @@ -0,0 +1,98 @@ +using FluentAssertions; +using IntroductionToEventSourcing.BusinessLogic.Slimmed.Tools; +using Xunit; + +namespace IntroductionToEventSourcing.BusinessLogic.Slimmed.Mixed; + +using static ShoppingCartEvent; + +public static class ShoppingCartExtensions +{ + public static ShoppingCart GetShoppingCart(this EventStore eventStore, Guid shoppingCartId) => + eventStore.ReadStream(shoppingCartId) + .Aggregate(ShoppingCart.Initial(), (shoppingCart, @event) => + { + shoppingCart.Evolve(@event); + return shoppingCart; + }); +} + +public class BusinessLogicTests +{ + [Fact] + public void RunningSequenceOfBusinessLogic_ShouldGenerateSequenceOfEvents() + { + var shoppingCartId = Guid.NewGuid(); + var clientId = Guid.NewGuid(); + var shoesId = Guid.NewGuid(); + var tShirtId = Guid.NewGuid(); + var twoPairsOfShoes = new ProductItem { ProductId = shoesId, Quantity = 2 }; + var pairOfShoes = new ProductItem { ProductId = shoesId, Quantity = 1 }; + var tShirt = new ProductItem { ProductId = tShirtId, Quantity = 1 }; + + var shoesPrice = 100; + var tShirtPrice = 50; + + var pricedPairOfShoes = new PricedProductItem { ProductId = shoesId, Quantity = 1, UnitPrice = shoesPrice }; + var pricedTShirt = new PricedProductItem { ProductId = tShirtId, Quantity = 1, UnitPrice = tShirtPrice }; + + var eventStore = new EventStore(); + + // Open + var (opened,_) = ShoppingCart.Open(shoppingCartId, clientId); + eventStore.AppendToStream(shoppingCartId, [opened]); + + // Add Two Pair of Shoes + var shoppingCart = eventStore.GetShoppingCart(shoppingCartId); + ShoppingCartEvent result = shoppingCart.AddProduct( + FakeProductPriceCalculator.Returning(shoesPrice), + twoPairsOfShoes + ); + eventStore.AppendToStream(shoppingCartId, [result]); + + // Add T-Shirt + shoppingCart = eventStore.GetShoppingCart(shoppingCartId); + result = shoppingCart.AddProduct( + FakeProductPriceCalculator.Returning(tShirtPrice), + tShirt + ); + eventStore.AppendToStream(shoppingCartId, [result]); + + // Remove a pair of shoes + shoppingCart = eventStore.GetShoppingCart(shoppingCartId); + result = shoppingCart.RemoveProduct(pricedPairOfShoes); + eventStore.AppendToStream(shoppingCartId, [result]); + + // Confirm + shoppingCart = eventStore.GetShoppingCart(shoppingCartId); + result = shoppingCart.Confirm(); + eventStore.AppendToStream(shoppingCartId, [result]); + + // Try Cancel + var exception = Record.Exception(() => + { + shoppingCart = eventStore.GetShoppingCart(shoppingCartId); + result = shoppingCart.Cancel(); + eventStore.AppendToStream(shoppingCartId, [result]); + }); + exception.Should().BeOfType(); + + shoppingCart = eventStore.GetShoppingCart(shoppingCartId); + + shoppingCart.Id.Should().Be(shoppingCartId); + shoppingCart.ClientId.Should().Be(clientId); + shoppingCart.ProductItems.Should().HaveCount(2); + shoppingCart.Status.Should().Be(ShoppingCartStatus.Confirmed); + + shoppingCart.ProductItems[0].Should().BeEquivalentTo(pricedPairOfShoes); + shoppingCart.ProductItems[1].Should().BeEquivalentTo(pricedTShirt); + + var events = eventStore.ReadStream(shoppingCartId); + events.Should().HaveCount(5); + events[0].Should().BeOfType(); + events[1].Should().BeOfType(); + events[2].Should().BeOfType(); + events[3].Should().BeOfType(); + events[4].Should().BeOfType(); + } +} diff --git a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/PricingCalculator.cs b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/PricingCalculator.cs new file mode 100644 index 000000000..2ef3e96a5 --- /dev/null +++ b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/PricingCalculator.cs @@ -0,0 +1,21 @@ +namespace IntroductionToEventSourcing.BusinessLogic.Slimmed.Mixed; + +public interface IProductPriceCalculator +{ + PricedProductItem Calculate(ProductItem productItems); +} + +public class FakeProductPriceCalculator: IProductPriceCalculator +{ + private readonly int value; + + private FakeProductPriceCalculator(int value) + { + this.value = value; + } + + public static FakeProductPriceCalculator Returning(int value) => new(value); + + public PricedProductItem Calculate(ProductItem productItem) => + new() { ProductId = productItem.ProductId, Quantity = productItem.Quantity, UnitPrice = value }; +} diff --git a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/ShoppingCart.cs b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/ShoppingCart.cs new file mode 100644 index 000000000..1360276ef --- /dev/null +++ b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mixed/ShoppingCart.cs @@ -0,0 +1,236 @@ +namespace IntroductionToEventSourcing.BusinessLogic.Slimmed.Mixed; + +using static ShoppingCartEvent; + +// EVENTS +public abstract record ShoppingCartEvent +{ + public record ShoppingCartOpened( + Guid ShoppingCartId, + Guid ClientId + ): ShoppingCartEvent; + + public record ProductItemAddedToShoppingCart( + Guid ShoppingCartId, + PricedProductItem ProductItem + ): ShoppingCartEvent; + + public record ProductItemRemovedFromShoppingCart( + Guid ShoppingCartId, + PricedProductItem ProductItem + ): ShoppingCartEvent; + + public record ShoppingCartConfirmed( + Guid ShoppingCartId, + DateTime ConfirmedAt + ): ShoppingCartEvent; + + public record ShoppingCartCanceled( + Guid ShoppingCartId, + DateTime CanceledAt + ): ShoppingCartEvent; + + // This won't allow external inheritance + private ShoppingCartEvent() { } +} + +// VALUE OBJECTS +public class PricedProductItem +{ + public Guid ProductId { get; set; } + public decimal UnitPrice { get; set; } + public int Quantity { get; set; } + public decimal TotalPrice => Quantity * UnitPrice; +} + +public class ProductItem +{ + public Guid ProductId { get; set; } + public int Quantity { get; set; } +} + +// ENTITY +public class ShoppingCart: IAggregate +{ + public Guid Id { get; private set; } + public Guid ClientId { get; private set; } + public ShoppingCartStatus Status { get; private set; } + public IList ProductItems { get; } = new List(); + public DateTime? ConfirmedAt { get; private set; } + public DateTime? CanceledAt { get; private set; } + + public bool IsClosed => ShoppingCartStatus.Closed.HasFlag(Status); + + public void Evolve(object @event) + { + switch (@event) + { + case ShoppingCartOpened opened: + Apply(opened); + break; + case ProductItemAddedToShoppingCart productItemAdded: + Apply(productItemAdded); + break; + case ProductItemRemovedFromShoppingCart productItemRemoved: + Apply(productItemRemoved); + break; + case ShoppingCartConfirmed confirmed: + Apply(confirmed); + break; + case ShoppingCartCanceled canceled: + Apply(canceled); + break; + } + } + + public static (ShoppingCartOpened, ShoppingCart) Open( + Guid cartId, + Guid clientId) + { + var @event = new ShoppingCartOpened( + cartId, + clientId + ); + + return (@event, new ShoppingCart(@event)); + } + + public static ShoppingCart Initial() => new(); + + private ShoppingCart(ShoppingCartOpened @event) => + Apply(@event); + + //just for default creation of empty object + private ShoppingCart() { } + + private void Apply(ShoppingCartOpened opened) + { + Id = opened.ShoppingCartId; + ClientId = opened.ClientId; + Status = ShoppingCartStatus.Pending; + } + + public ProductItemAddedToShoppingCart AddProduct( + IProductPriceCalculator productPriceCalculator, + ProductItem productItem) + { + if (IsClosed) + throw new InvalidOperationException( + $"Adding product item for cart in '{Status}' status is not allowed."); + + var pricedProductItem = productPriceCalculator.Calculate(productItem); + + var @event = new ProductItemAddedToShoppingCart(Id, pricedProductItem); + + Apply(@event); + + return @event; + } + + private void Apply(ProductItemAddedToShoppingCart productItemAdded) + { + var (_, pricedProductItem) = productItemAdded; + var productId = pricedProductItem.ProductId; + var quantityToAdd = pricedProductItem.Quantity; + + var current = ProductItems.SingleOrDefault( + pi => pi.ProductId == productId + ); + + if (current == null) + ProductItems.Add(pricedProductItem); + else + ProductItems[ProductItems.IndexOf(current)].Quantity += quantityToAdd; + } + + public ProductItemRemovedFromShoppingCart RemoveProduct(PricedProductItem productItemToBeRemoved) + { + if (IsClosed) + throw new InvalidOperationException( + $"Removing product item for cart in '{Status}' status is not allowed."); + + if (!HasEnough(productItemToBeRemoved)) + throw new InvalidOperationException("Not enough product items to remove"); + + var @event = new ProductItemRemovedFromShoppingCart(Id, productItemToBeRemoved); + + Apply(@event); + + return @event; + } + + private bool HasEnough(PricedProductItem productItem) + { + var currentQuantity = ProductItems.Where(pi => pi.ProductId == productItem.ProductId) + .Select(pi => pi.Quantity) + .FirstOrDefault(); + + return currentQuantity >= productItem.Quantity; + } + + private void Apply(ProductItemRemovedFromShoppingCart productItemRemoved) + { + var (_, pricedProductItem) = productItemRemoved; + var productId = pricedProductItem.ProductId; + var quantityToRemove = pricedProductItem.Quantity; + + var current = ProductItems.Single( + pi => pi.ProductId == productId + ); + + if (current.Quantity == quantityToRemove) + ProductItems.Remove(current); + else + ProductItems[ProductItems.IndexOf(current)].Quantity -= quantityToRemove; + } + + public ShoppingCartConfirmed Confirm() + { + if (IsClosed) + throw new InvalidOperationException( + $"Confirming cart in '{Status}' status is not allowed."); + + if(ProductItems.Count == 0) + throw new InvalidOperationException($"Cannot confirm empty shopping cart"); + + var @event = new ShoppingCartConfirmed(Id, DateTime.UtcNow); + + Apply(@event); + + return @event; + } + + private void Apply(ShoppingCartConfirmed confirmed) + { + Status = ShoppingCartStatus.Confirmed; + ConfirmedAt = confirmed.ConfirmedAt; + } + + public ShoppingCartCanceled Cancel() + { + if (IsClosed) + throw new InvalidOperationException( + $"Canceling cart in '{Status}' status is not allowed."); + + var @event = new ShoppingCartCanceled(Id, DateTime.UtcNow); + + Apply(@event); + + return @event; + } + + private void Apply(ShoppingCartCanceled canceled) + { + Status = ShoppingCartStatus.Canceled; + CanceledAt = canceled.CanceledAt; + } +} + +public enum ShoppingCartStatus +{ + Pending = 1, + Confirmed = 2, + Canceled = 4, + + Closed = Confirmed | Canceled +} diff --git a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/Aggregate.cs b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/Aggregate.cs new file mode 100644 index 000000000..12fcb73b0 --- /dev/null +++ b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/Aggregate.cs @@ -0,0 +1,24 @@ +namespace IntroductionToEventSourcing.BusinessLogic.Slimmed.Mutable; + +public abstract class Aggregate +{ + public Guid Id { get; protected set; } = default!; + + private readonly Queue uncommittedEvents = new(); + + public virtual void Evolve(object @event) { } + + public object[] DequeueUncommittedEvents() + { + var dequeuedEvents = uncommittedEvents.ToArray(); + + uncommittedEvents.Clear(); + + return dequeuedEvents; + } + + protected void Enqueue(object @event) + { + uncommittedEvents.Enqueue(@event); + } +} diff --git a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/BusinessLogicTests.cs b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/BusinessLogicTests.cs new file mode 100644 index 000000000..2068c5de1 --- /dev/null +++ b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/BusinessLogicTests.cs @@ -0,0 +1,98 @@ +using FluentAssertions; +using IntroductionToEventSourcing.BusinessLogic.Slimmed.Tools; +using Xunit; + +namespace IntroductionToEventSourcing.BusinessLogic.Slimmed.Mutable; + +using static ShoppingCartEvent; + +public static class ShoppingCartExtensions +{ + public static ShoppingCart GetShoppingCart(this EventStore eventStore, Guid shoppingCartId) => + eventStore.ReadStream(shoppingCartId) + .Aggregate(ShoppingCart.Initial(), (shoppingCart, @event) => + { + shoppingCart.Evolve(@event); + return shoppingCart; + }); +} + +public class BusinessLogicTests +{ + [Fact] + public void RunningSequenceOfBusinessLogic_ShouldGenerateSequenceOfEvents() + { + var shoppingCartId = Guid.NewGuid(); + var clientId = Guid.NewGuid(); + var shoesId = Guid.NewGuid(); + var tShirtId = Guid.NewGuid(); + var twoPairsOfShoes = new ProductItem { ProductId = shoesId, Quantity = 2 }; + var pairOfShoes = new ProductItem { ProductId = shoesId, Quantity = 1 }; + var tShirt = new ProductItem { ProductId = tShirtId, Quantity = 1 }; + + var shoesPrice = 100; + var tShirtPrice = 50; + + var pricedPairOfShoes = new PricedProductItem { ProductId = shoesId, Quantity = 1, UnitPrice = shoesPrice }; + var pricedTShirt = new PricedProductItem { ProductId = tShirtId, Quantity = 1, UnitPrice = tShirtPrice }; + + var eventStore = new EventStore(); + + // Open + var shoppingCart = ShoppingCart.Open(shoppingCartId, clientId); + eventStore.AppendToStream(shoppingCartId, shoppingCart.DequeueUncommittedEvents()); + + // Add Two Pair of Shoes + shoppingCart = eventStore.GetShoppingCart(shoppingCartId); + shoppingCart.AddProduct( + FakeProductPriceCalculator.Returning(shoesPrice), + twoPairsOfShoes + ); + eventStore.AppendToStream(shoppingCartId, shoppingCart.DequeueUncommittedEvents()); + + // Add T-Shirt + shoppingCart = eventStore.GetShoppingCart(shoppingCartId); + shoppingCart.AddProduct( + FakeProductPriceCalculator.Returning(tShirtPrice), + tShirt + ); + eventStore.AppendToStream(shoppingCartId, shoppingCart.DequeueUncommittedEvents()); + + // Remove a pair of shoes + shoppingCart = eventStore.GetShoppingCart(shoppingCartId); + shoppingCart.RemoveProduct(pricedPairOfShoes); + eventStore.AppendToStream(shoppingCartId, shoppingCart.DequeueUncommittedEvents()); + + // Confirm + shoppingCart = eventStore.GetShoppingCart(shoppingCartId); + shoppingCart.Confirm(); + eventStore.AppendToStream(shoppingCartId, shoppingCart.DequeueUncommittedEvents()); + + // Try Cancel + var exception = Record.Exception(() => + { + shoppingCart = eventStore.GetShoppingCart(shoppingCartId); + shoppingCart.Cancel(); + eventStore.AppendToStream(shoppingCartId, shoppingCart.DequeueUncommittedEvents()); + }); + exception.Should().BeOfType(); + + shoppingCart = eventStore.GetShoppingCart(shoppingCartId); + + shoppingCart.Id.Should().Be(shoppingCartId); + shoppingCart.ClientId.Should().Be(clientId); + shoppingCart.ProductItems.Should().HaveCount(2); + shoppingCart.Status.Should().Be(ShoppingCartStatus.Confirmed); + + shoppingCart.ProductItems[0].Should().BeEquivalentTo(pricedPairOfShoes); + shoppingCart.ProductItems[1].Should().BeEquivalentTo(pricedTShirt); + + var events = eventStore.ReadStream(shoppingCartId); + events.Should().HaveCount(5); + events[0].Should().BeOfType(); + events[1].Should().BeOfType(); + events[2].Should().BeOfType(); + events[3].Should().BeOfType(); + events[4].Should().BeOfType(); + } +} diff --git a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/PricingCalculator.cs b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/PricingCalculator.cs new file mode 100644 index 000000000..e805ce8ea --- /dev/null +++ b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/PricingCalculator.cs @@ -0,0 +1,21 @@ +namespace IntroductionToEventSourcing.BusinessLogic.Slimmed.Mutable; + +public interface IProductPriceCalculator +{ + PricedProductItem Calculate(ProductItem productItems); +} + +public class FakeProductPriceCalculator: IProductPriceCalculator +{ + private readonly int value; + + private FakeProductPriceCalculator(int value) + { + this.value = value; + } + + public static FakeProductPriceCalculator Returning(int value) => new(value); + + public PricedProductItem Calculate(ProductItem productItem) => + new() { ProductId = productItem.ProductId, Quantity = productItem.Quantity, UnitPrice = value }; +} diff --git a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/ShoppingCart.cs b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/ShoppingCart.cs new file mode 100644 index 000000000..ffccdf71c --- /dev/null +++ b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Mutable/ShoppingCart.cs @@ -0,0 +1,236 @@ +namespace IntroductionToEventSourcing.BusinessLogic.Slimmed.Mutable; + +using static ShoppingCartEvent; + +// EVENTS +public abstract record ShoppingCartEvent +{ + public record ShoppingCartOpened( + Guid ShoppingCartId, + Guid ClientId + ): ShoppingCartEvent; + + public record ProductItemAddedToShoppingCart( + Guid ShoppingCartId, + PricedProductItem ProductItem + ): ShoppingCartEvent; + + public record ProductItemRemovedFromShoppingCart( + Guid ShoppingCartId, + PricedProductItem ProductItem + ): ShoppingCartEvent; + + public record ShoppingCartConfirmed( + Guid ShoppingCartId, + DateTime ConfirmedAt + ): ShoppingCartEvent; + + public record ShoppingCartCanceled( + Guid ShoppingCartId, + DateTime CanceledAt + ): ShoppingCartEvent; + + // This won't allow external inheritance + private ShoppingCartEvent() { } +} + +// VALUE OBJECTS +public class PricedProductItem +{ + public Guid ProductId { get; set; } + public decimal UnitPrice { get; set; } + public int Quantity { get; set; } + public decimal TotalPrice => Quantity * UnitPrice; +} + +public class ProductItem +{ + public Guid ProductId { get; set; } + public int Quantity { get; set; } +} + +// ENTITY +public class ShoppingCart: Aggregate +{ + public Guid ClientId { get; private set; } + public ShoppingCartStatus Status { get; private set; } + public IList ProductItems { get; } = new List(); + public DateTime? ConfirmedAt { get; private set; } + public DateTime? CanceledAt { get; private set; } + + public bool IsClosed => ShoppingCartStatus.Closed.HasFlag(Status); + + public override void Evolve(object @event) + { + switch (@event) + { + case ShoppingCartOpened opened: + Apply(opened); + break; + case ProductItemAddedToShoppingCart productItemAdded: + Apply(productItemAdded); + break; + case ProductItemRemovedFromShoppingCart productItemRemoved: + Apply(productItemRemoved); + break; + case ShoppingCartConfirmed confirmed: + Apply(confirmed); + break; + case ShoppingCartCanceled canceled: + Apply(canceled); + break; + } + } + + public static ShoppingCart Open( + Guid cartId, + Guid clientId) + { + return new ShoppingCart(cartId, clientId); + } + + public static ShoppingCart Initial() => new(); + + private ShoppingCart( + Guid id, + Guid clientId) + { + var @event = new ShoppingCartOpened( + id, + clientId + ); + + Enqueue(@event); + Apply(@event); + } + + //just for default creation of empty object + private ShoppingCart() { } + + private void Apply(ShoppingCartOpened opened) + { + Id = opened.ShoppingCartId; + ClientId = opened.ClientId; + Status = ShoppingCartStatus.Pending; + } + + public void AddProduct( + IProductPriceCalculator productPriceCalculator, + ProductItem productItem) + { + if (IsClosed) + throw new InvalidOperationException( + $"Adding product item for cart in '{Status}' status is not allowed."); + + var pricedProductItem = productPriceCalculator.Calculate(productItem); + + var @event = new ProductItemAddedToShoppingCart(Id, pricedProductItem); + + Enqueue(@event); + Apply(@event); + } + + private void Apply(ProductItemAddedToShoppingCart productItemAdded) + { + var (_, pricedProductItem) = productItemAdded; + var productId = pricedProductItem.ProductId; + var quantityToAdd = pricedProductItem.Quantity; + + var current = ProductItems.SingleOrDefault( + pi => pi.ProductId == productId + ); + + if (current == null) + ProductItems.Add(pricedProductItem); + else + current.Quantity += quantityToAdd; + } + + public void RemoveProduct(PricedProductItem productItemToBeRemoved) + { + if (IsClosed) + throw new InvalidOperationException( + $"Removing product item for cart in '{Status}' status is not allowed."); + + if (!HasEnough(productItemToBeRemoved)) + throw new InvalidOperationException("Not enough product items to remove"); + + var @event = new ProductItemRemovedFromShoppingCart(Id, productItemToBeRemoved); + + Enqueue(@event); + Apply(@event); + } + + private bool HasEnough(PricedProductItem productItem) + { + var currentQuantity = ProductItems.Where(pi => pi.ProductId == productItem.ProductId) + .Select(pi => pi.Quantity) + .FirstOrDefault(); + + return currentQuantity >= productItem.Quantity; + } + + private void Apply(ProductItemRemovedFromShoppingCart productItemRemoved) + { + var (_, pricedProductItem) = productItemRemoved; + var productId = pricedProductItem.ProductId; + var quantityToRemove = pricedProductItem.Quantity; + + var current = ProductItems.Single( + pi => pi.ProductId == productId + ); + + if (current.Quantity == quantityToRemove) + ProductItems.Remove(current); + else + current.Quantity -= quantityToRemove; + } + + public void Confirm() + { + if (IsClosed) + throw new InvalidOperationException( + $"Confirming cart in '{Status}' status is not allowed."); + + if(ProductItems.Count == 0) + throw new InvalidOperationException($"Cannot confirm empty shopping cart"); + + var @event = new ShoppingCartConfirmed(Id, DateTime.UtcNow); + + Enqueue(@event); + Apply(@event); + } + + private void Apply(ShoppingCartConfirmed confirmed) + { + Status = ShoppingCartStatus.Confirmed; + ConfirmedAt = confirmed.ConfirmedAt; + } + + public void Cancel() + { + if (IsClosed) + throw new InvalidOperationException( + $"Canceling cart in '{Status}' status is not allowed."); + + var @event = new ShoppingCartCanceled(Id, DateTime.UtcNow); + + Enqueue(@event); + Apply(@event); + } + + private void Apply(ShoppingCartCanceled canceled) + { + Status = ShoppingCartStatus.Canceled; + CanceledAt = canceled.CanceledAt; + } +} + +public enum ShoppingCartStatus +{ + Pending = 1, + Confirmed = 2, + Canceled = 4, + + Closed = Confirmed | Canceled +} diff --git a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/README.md b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/README.md new file mode 100644 index 000000000..608597f2d --- /dev/null +++ b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/README.md @@ -0,0 +1,28 @@ +# Exercise 07 - Business Logic + +Having the following shopping cart process: +1. The customer may add a product to the shopping cart only after opening it. +2. When selecting and adding a product to the basket customer needs to provide the quantity chosen. The product price is calculated by the system based on the current price list. +3. The customer may remove a product with a given price from the cart. +4. The customer can confirm the shopping cart and start the order fulfilment process. +5. The customer may also cancel the shopping cart and reject all selected products. +6. After shopping cart confirmation or cancellation, the product can no longer be added or removed from the cart. + +Write the code that fulfils this logic. Remember that in Event Sourcing each business operation has to result with a new business fact (so event). Use events and entities defined in previous exercises. + +![events](./assets/events.jpg) + +There are two variations: +- using mutable entities: [Mutable/BusinessLogicTests.cs](./Mutable/Solution1/BusinessLogicTests.cs), +- using fully immutable structures: [Immutable/BusinessLogicTests.cs](./Immutable/BusinessLogicTests.cs). + +Select your preferred approach (or both) to solve this use case. + +_**Note**: If needed update entities, events or test setup structure_ + +## Solution + +1. Immutable, with functional command handlers composition and entities as anemic data model: [Immutable/BusinessLogic.cs](./Immutable/BusinessLogic.cs). +2. Classical, mutable aggregates (rich domain model): [Mutable/Solution1/BusinessLogic.cs](./Mutable/Solution1/BusinessLogic.cs). +3. Mixed approach, mutable aggregates (rich domain model), returning events from methods, using immutable DTOs: [Mutable/Solution2/BusinessLogic.cs](./Mutable/Solution1/BusinessLogic.cs). + diff --git a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Tools/EventStore.cs b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Tools/EventStore.cs new file mode 100644 index 000000000..d910254ab --- /dev/null +++ b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/Tools/EventStore.cs @@ -0,0 +1,24 @@ +using System.Text.Json; + +namespace IntroductionToEventSourcing.BusinessLogic.Slimmed.Tools; + +public class EventStore +{ + private readonly Dictionary> events = new(); + + public void AppendToStream(Guid streamId, IEnumerable newEvents) + { + if (!events.ContainsKey(streamId)) + events[streamId] = []; + + events[streamId].AddRange(newEvents.Select(e => (e.GetType().FullName!, JsonSerializer.Serialize(e)))); + } + + public TEvent[] ReadStream(Guid streamId) where TEvent : notnull => + events.TryGetValue(streamId, out var stream) + ? stream.Select(@event => + JsonSerializer.Deserialize(@event.Json, Type.GetType(@event.EventType)!) + ) + .Where(e => e != null).Cast().ToArray() + : []; +} diff --git a/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/assets/events.jpg b/Workshops/IntroductionToEventSourcing/07-BusinessLogic.Slimmed/assets/events.jpg new file mode 100644 index 0000000000000000000000000000000000000000..35522f5804b2be096c7dbbc10ca8bedf69faa212 GIT binary patch literal 67506 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ÐTmn>|e zU^B!}??1vZme5Ex*D@p;hKaT`^toqcM5_2J+VW4~pm}f>{43fL?MLn@|7ESbLBAU3 zjsC+D^m}#;@B21SSCHE5GWla7N;3i404xA8E~;yo@MrZMw76$vo6en zfpR239J71?;Nf%eMDa{Qjk}hr*j@JU54j3e7cmC`FT}8v`L~X<96V(YR@-O8TKh~q z6rRjQB=e+L8KfDZ+3CHAp3EjBF0f-uP|b!v|8uqe{r|DuS*IBXJI~Q)hve$k){K2^ z<{P9+hMJH{Hjkhd#Jw+H9^*($4828K^IL-j9EdUbkc)K$vUH;>O?^J^@~iuR$G1xf z_G3@9^7=!wNJgd5d|fno`&37spqupjt(b-mw@!YA?Ws4?S+;q2k#0{Z8)*~nO zW=Ai^9z-sF3J*7Yd@^nKWKgDs^ltCQ1&PPSINnh+#K$en%1PGywv>A0AkoC<)1NIJ z>cr$;J@WwN=WC-hg_)sKVP|95Ji}GL#82Of<=wEoDh#I!Twj@Qd-?M? z>Oi`kY|lD=^RRg6d)$*zkBHLr%h%myW+CO8(Spa6YpbI3wb-sI$eq zeS>?xDY3zF64hKP^0MyvQlfXXDC60e#(zGDJ9YnBhvXEK{ZQy2tRnNz%zo@&2}s`Z zM_o#Yn0uvS)ci|1FRJv?=7axqa~g_RJhct2D>yX5d>aisj$6@;H9Ffl31L z&)xo#15d$IC!wv=_EY@_r+!xvXOhcrA213g{#zU{W&}p1umAv%`xy1{S)bG#XRWz} z2+CqMGYe+S6V9KRS86YfB(YUKtc}H(71s#*b;kz%P0Q>(t8m^UU9Hg2CEeZ;J;wt-7cucXwLms8yGLx@C zse(puJVdUKYdsHiGiLeIX?}6&AEHRS|oz4LMXn%8KA@$wuE5ljh=xWu}UJ(w& zZS{YM#dJQRJ8NFAU~IXXf_)?M#)8j)qy~z%G{OiO)qE+%%eoiLF9J4b7Q+&GyeCo4 zD^T^@B#dxWOuce)YQP9(RP({FM#6te$6G6r@+l)H?mpPhlep6!o11N%d@rX1&cmm#2fNK8qoOHDEDMeSb-#i9F`ny`LXblPU;Lpk(4woeDD|BQG= zFY;1B#LR)^Um`TW?;$3BEBNaJ(}_*egW-TdovoJ&zj!`e;1|sipM;qVvWkJ-5rZ$b zD0|h!!fCxD`?UYm9e)nw@!R4lH00WW?)a;FTe+tBidOX1m56DuiMzljj97{S@? zjGKmsOt{M*7RY?sje5eShA} z67)xAOlBGeM!;VlE1x+kc89Sbs7Ic8Jau+1HBHuqy)SUlbSsO2zwmcJ5{{eXy_?%t z*X{C3htH}h9pAjU&~LcSCcR$Qk9kV!EB)cR9kjlBfup~OR{UH^E#Z1EaO6TU)J!%} z*4nZB6cO!3tkQJ-@VLZixK2U2T!ECdMXZN>fPZx`Lv-6FDpCKinZZ3Au0#ub$}aDz zUB>m;RGn>2kSL01TY2@{Lfid5Q?>l6u=|U*({5s?($&_Wyi+gdB(EUo3D>$T^Lc1n zy1^&zV~Xj8zgOySdB;3AS#SW2?Xi|pSM$EQkQJ#Zhnr9?can`C)|USae1NM}J>Rnn zE+t(S(2a{@IR;5D(UuaXhqOKLwTKOuIuii=20SHkW!8n0f=W#Gpq0U13?foeQc4+A zJ-t)4Bi?N%0M8Kt6GdB1Y4&cbZ0#6eL&d&mSYpLU*A-awSQz;MSX`%)cz3O$BnNol z|Am?5VJc@|{v~$2o?`;fr!%Ugczs%& zxHyMRpuQPz?mfa)As%n0gZgM5{~RjZ*9Q9SPJs};WG{5As&|N10bg|Bc*-KWp73nS zH1SUA>U;BD28f=ZqZfMC<=5KxXlDcyl&$;PKUk#8<#9domZY!JQ`)wSV$IzNH~~mJ z9-0ugPXJv!?it!J>@d4fP13W;_Ff7|Y@f2J4}JU4A^u*T!!l~&ItHlCSs&eVL8how zHSi`yG>6${)F8|{yNHOrkqrmnGdQT-K7@j23*b5dV6hv2!e z^RfrYk(wY$3*q*UYq?8#YQ!6>Ao})EX~#s%)yCq)l7#{`V|X`z{i`2!PnU=r$Pdi! ziZH7#h9-uljk_lcAau(LcG8knxWVpySr*pMMM^Hn+G~8+LzxL={ut$@N%fL9%jZ|j z`k>zd4N5mBvt@JCsA8s#xdyRO6|46O4E#oF9xMV{N4~-QmH!##e(dC~&)!gmk56Ge z6VJ+uYUtkm!@|u>$lkR0nbtAb0M#_T|_L$jaX7tgk9bH6q)f0zu$Tri_N0t2CA;PSDtLL z+hApU{PzM=cj0)693Kz<4ybonHxJ{_pNdft_;7ixm?y1X{N=Qu0pY9K!DQAhuV)vG9A{8PFB9xjkREM+LG zKWZ8rL&lxG_dNFs$6Plz4n{v_sYOrJtB>VW^_R#n*2N-}-#?Fdy!Zi1wJUt|rrJSr z6{|-@d@jDgvGXNNYw9=dg0-JgLhFYq3H-uQOzm}%cW4n4oO@D}(`HM+Q+!t&6#>y6 zb+sNNhZrd_Oa$1~=O@VpF3$`)^IHTukc6htyq1dy7z1n1W`k*8JA|H<4!Tru(SLVM zW;_O1_deF<%L{NQ&AN(ZCvuNymCZc1ceLOl^6uKHHUU>lhT6JCM#GM%6T`WSKCR}} zea}^wiT$Y-x&cwe&u`ys;~!&GhKMj|1sfz*pkh<`JT(*EZEjOAdayaP3h<@c=(P{Q zRz2ZTCY!bIPyJJMa+Qri7)Ubjy~0(_Eap9R>yvQW)e-pF*RB*+r57=ItN%jBhQSm- zC2hH!Z2Bf)RRnKnLbSu&C^BhqQflqVCMWNd5$VU_$yt^Dp%Q$*uI;QhQk;l zo2pZe$Ed2wjBgkX*<51oL6>sK-92?*N8{8_B{DI^wp7gHGBm7Oa*vs-%1~7 ziU3}`*i#7Rc@yaW_OF{y>Gn-E=cnhx{UpMd(n!j-Jl)k_8O(0k>0h2{Eq{&CGRqsP z5qMOqJ88n!<&rqZoy&c@q(V8JO|Mp=^K5B?rB7|LN1>n~56?-5qN}}a@soGB=);EA z0}9$Yon}S7VN_RD0&tG|{$-fr$$crVGHG1T{sFOXk)|4a?Db2lJVV?8Xz(^9_droh z=G8s^5g!b56Ayn3g$U?8B41T}Iei1<^k|Pz7715>pOT4QM;pVFD&wx-1g*YnVf4nH z4_w0NM~rqA;!sMH(pQvGISUuI%Tw{$T=kYMI*C5k6DjzP$3yTX>i0htW7W5iS8Bx=dW zD5k(a6*=!F%h5MCvss@${-!PO%w0+~p0hS02~_yHtoD|kuzSTz!-NG)`9Kx$ff$K+ zUlqg5?||iQ_*I*GH+uzqgkQ1s!lbw`dB&QZ4MDWbqNjJ!kdN#iG8(LVcswFTbU;Nq zG4j|xC9LhDjnY+z$g0Djy-!SQ0H*Yo-c-xlKS0Q zV;!s;nChO9-@2tndX}panL;Zyaa6vYzjL$VaG028saVOca3eFoRx#0q!2B2}S5Ou2 zF=kNFz7qD(32(W{iAe0b|K-h?`TOE-mG48Z+4t}+LL&tTMHLX>cu zk}|wiN=n;mUj8tiwyeP74tu*D2J1x4AyMvU;5}m+;BnhG!+T#$OLaI$mnt^qEvK;; z$9>f5b97jvf}@^KQv+;r=L>j{;lM!K++>7rB-45`#Af+xlcA29#^(oBMlmXKUvVA6 zm%0992mEt|OPL0nzx&9f$s}ncuVpWHTGQ z`mnatP2mWPIUJqQ0azvc+B=#=F8MVBeHk7__a^;BRQ$VhZ%y#lG?0o9Yno0jvqDuYhQ(-94p zCvlqt0z9Jjc;Xna{CvYnM~@O4wZv`lTF<#R_fbsSSL(~~>K%<$UhOtZ{ZIM#>no10 z)Df>UARhY}7<`Sq(KCP>IsNdO--TjWJj8`Elp6XLE2xAlqyzV|Dv6)~Hbqqbmczzw z65g%ZQ7SZCz(Z7Wr?pxb9EEY{|JS2(Beu^Ws7J<8LLjWC=^WrT$2OuY7t=-tD(bsy z-d);1@Pq8(@v*D%Qjmg4alPP&x4RNVuxo&Tb8fll5zTm_&qt?O#k4uqL(SdG&jPVI zKd;1(bAL*PU(xSb8qq7y<9jhBEw`}A+j)rQ zct=m%bUvYPGkJYKh$$_95TT?}$@g7lUhp#>wT4Ewe(c_IN&|!P@gpo@(f4@lrHE;R zZC79N^sM?h)lKrHBY@>#dQb;$^t<}JseBc}W#8dFzzFRZpWgvgQDrvDKTSvYzM-kI zU0y#3ep$mnE5hO%)ZG~-2?R(sYAWu(4McX}PR@L{$H7!e`NI+X_X^!5AHt0uk<@HT z3IPR;WBvBNRM9rxmOpD_ynbCxP0c=&@?lxvcF`<@uSx0{^;OI#M7l^GUy~JjUlm`M zhob1FlTxPt{A#Gk2@@r2s-uGa7GGhU`wGNngL@&2kL(a$nU%Tr2dqp<*qM4eQmam2 z)@#U(;ZUJ2=1~h7Ru69wo|xkaX;m1W5I+T>0<{;yHKvki?)3xF*iW7V{u{Di8_;|m zSQ*w09IooK-Pb?Jcn6bi**Dp2m>Mk4!{*fIDveT|m9A6@?<&T&x|E7$AH%tJRHvOS zPxA@VA7T~Q2q4DwV^432F%bC|-;E=P0i!UH21o{nyT%aZ@wjP=^e|vQ*Vq1xt|tcK zsCm(6IC@45_%UGtH}-hX^Q>Jz6toX$jhqZNs6=!;{l#h4Do-Dq@7O^SXw(F#RR3N` z0G5PX$~OgD;_W7H zq`u3~E(4a+#6O4xXHc2+C)Z8Fjh0bsevD;dyMU!p-`Q3Y)<^Y-6N-drDivxw`v)YO zxZpFokhgZG_#z|k`}seZkg#GCb5xQp3)xZGSt)3}BP|8+2k5QWWo5fX zwr$^`_v)BNiPX2NS#}#uf3~-+K3~z!V1i7oXVk;HUDMmBZ`ZJq%97iv_(bEcR%2E< zB{JcY6vd^ewF)#-N2(2mLkt505xYvwFjiC>TEY%riu@{O3`d7N96)JlNy0-kO}Q%n zQ_&-ahH5D)2@%%>&>nwSrv}^c54Vt1v=`5OhJo$>*AoA;)_+{w2@b4#R;r6wNbVmY4q{PeuImlyMAwmDYkY&DA&VHbS81Ys9zHQT(F-&Q^WwE6A6^V{XL zRz4vopT-J(;Zpt0SSa%DQ!c<`ZP4-;tJ=`ayOd+Dc;T3yJxJ?uPOK(j^LKiSn~R$F z)aS8mTjL}pm;W(K{+lYs{NP0O%)8{th^{Eh#OzAalDi~Njd4d`L1Qr@g=KsHX|2gw z<{y{C&&78_X8qkO_W)|e$KyXpCYE+Zd;(-sGf-##H(8ihjQJtm z`G97WCZh$bR;34kV@Ev26THUBBDzAMd-mCMYO#SI^Cu94G&~qN|7<#$2D49VHycme z`J?@0IAIL9@=myEf zRv;8E?ihZYf;ryN2F5KsvbAxG#Kg#l72Wis^h`o4gR(ok`)!ma6z|ViRlGyy-zD^o)5xw_+=gQx)T}{l^l27W%eRo<piB6|SpeR}3 zETXfvdau{<24kWmcf{l*-{u!H9MUzsl<;Uwf4U=VI|7p+0CnP@X&L!{w{?_hQu?jj z-h&P0{w&rE^}+Q#`hS&oo>5J9+rE!oL{ORt0xG>DVCZ6@caxBWj)1fPq4#2;NJn}H zscD2BIwHM;bO=R7Is_EyMQ?ofyU#x7j=jen<9_mvn-3X~5Y~ED)|&H~Yt8xlFE4R_ zI5R>0uN#&>bnX6Y&3yH+6^VHTztrv;K+ezlpE%LGfNL4oaz6aN1|G5b(Iq1V000aC zFtG0|oOJ=jEiCBfy8Lf0=dAh4N)t{XYAg6mK=s>IksWu_i7Q0U=GRR@Ux%HPrX8M= zUJ$Z8gTs&6s_XaJodM)6`@b6a-N+yMIH%_4ccn&NAe~$ZW)-)uIpw}X&KCcNoY+5d zb1wV?_7rrr(SRC%EkoF!#^Sozu8mA3;|M(?V^*4At$YA z`;Whe(l^*(!{;<&pWOq%y71-QA3B8p?AAFAU%#s*Cvf0O1~N*B^i8bkyU|3hb41iJ zUVDApN}F=){_g`UPJrKx|35Q~E~afY2u)MJ5_dCpzkRn*jkKP(E6Tnxvw-kTad1rz zM{!Dgv`|QS-Sy45jzu|-SknQ5@D?C5Ms6cVJZKFbE*Q-L`8#fj`iJ-BwkxT>9cVxj-i72p)Z zF=HCfyEAjqe`nA9qwPQO`Ts&VPvMk%)9&>v>!HyRhv|q#Jn>P8*L{}}{Wx3!3 zdIWW=>i*^K1W%V-6uqtqFD6)N;mK|$|0w=;LEprVBbaAtHK9v8#lOO-zNcW8BCPh~ z=ME$J8#Ig*#D*Ck*6c{fYb+9M)j1eX^5%@4G2U&~s(cr(^Dw4`D>R9{@wNt~u+FflMWoGXS$SXdvKr=3vI2x`caqdn}kJ{tlE4Sz_kLS_Qz0y5pyM5;7{O3h3 za4vy_&+%R9dXwK~Q6MNdYQ8$+N!V#1Ie=dGG~)43stbzKwezQlegmKqWgJ;B?@OO6 zJi;iVpMF7@^r8aqv(b*%$}>u~v{Iw7=5(ktM478EC+nfn<0T;*2P`GZ`|{u$*vIka z9F~q0KXd6IRES-BBX$o<9cx7!+VA(H5+`zHPUA&|xLkFsr}=&^)+@`d-oW1O#_}G{wZBiTuP%)s^r~%CK7ejgha65w8$)MGg?)LLrPFzxpgjlbH}chfB!I&@qVzp zZ{dZ)!-ISCGO&r%l;pY4dh>j85xN|RW!`qQkZu;7Y^j>3Y(`=j{0ueD$xx52bCBOE~Su z;7mWk_esD@PkDXp9J%_Po}~F@Cu;PF#1o=jjhWd+i;V{m4rDQ%oJ^Mt8i^r!NpT}K_?^1ea;+ck?TNhUcM}-Ry72(9Dphr55B3@%@Dps z8J$VdA}pG)t!8pEusY?s_WrrKb4?MwK#L1F!7NL7d>Mw=N>}!T2E^?dOWap2mmsVf zl=ZKCM>*9iJ?)-93UDR*-{06=A0H33@ATaSj?w8T`s29 zs^d-ROEYNa8}Dgt7RE1Sq|98PPUD;?>*!gBoaq_Zn=L4|RIcIZ9vb)@1zIri=PlcN<03-X<3fDr5vRprgZpR0k0>?@4ouP$_3^)==9!I~k=| zdT)cMM#Vb2AMt{IDqx$Pf{e^$3zH>e_#_gh&_i%eUVQq^&EXf|(!S%-kiibtY{gk4 zHg2c}vP}kq`9y&O1ltr5$k3%u*IS9CR)bJ$bEg^vxD8fRz(dh4V(33w-Dt{V8ywO$ z&)<1XIRZ3DAz8RCwP_#>sT;|BR_QLmxf8`iV7h`AaZCnd4cBGP8RWO|QoDQBIdLvgNKT$?DD%fp&FF0P}62mUy66l#Ds4+ymaS57nxG4L|Ja}7a%qCkgOZJMf7#UmQ<7QwPs)0S zaMrDF~v|-#sRo#!rwf=}wgr`HKy(@b=6P z=bgW9>R3W@hk^Vk9xg`B%^r7V9|M7GHr6K^C9fo@Ab~8H&D7wpN#8jR*Cq2$*EyJH z8bl)40@c&ao6}+()%x1g!zjAn0B^`Mx9j#;N)@U#Fdg{k$la&6xLj2&Nl)A-l)PWM z;Gca}ai&0;bH9K)e&5BnNXZ~3Pq=8s$oO^{yLxv%pAGGBbXNF|xx_8vBD@ywT3!R~ zkxYxT54zOT)vMm73vUkULc+G8ii$>TFZFQ|ID@nTNw@T5<3+ne_FXw4ejux=fGqE> zfn;?$N9LW8w7KlQIcJKebf}V;@Xn~*0TI&`cYb%f0dnl#9LTYGMS><+M z`4vJ}dN5R2OednG2VT%GqgBU58%xay=TBs|uh1K~WwyJ4q^o}=%>pZ4?OefF%Z%(< zPd1LkjRb%ePG68fG{+vBj&c^#U*_-mg1-0A;cZ5O;>%$gP)|#okNYMEV+A99$rZ_j z@f5$W#+1DNf^Z5T4=uIn{{W6;c|~|CZRdj9`CW(^^t# z2QOT`ABWuR7I?2r`UN-~wR$?WgVu#Y911KwW|PI5tR3}PSJ^Vhiv&z+6^pdglw%-8 zqx|NOGT89;v6j6*V$C^wm%u|TPEO~~dzJUNx%G%He8a=Bn`YEl8s9d7@dvA6 zZky*Amhn-)E_spQJ`g#P7hCT^YH^*-*rWM@@k}aR10qg_r~B;-pJvidMLeX1u|6JS zczrr}szXClN}Ryv%DQ0p@9v1w584pgRf|X0)8JUTsyJ97tMtRWH_|WWa35^$4>3`* z6F!d3Oi<4^iDtj*#5JjLG4Z0?d!+O1*3v3V-0QyE;av4@&QOjFwf6{XgFzmNq&)5K zgj)NNQ^?{v)Zck1?1;+X`)Hc`mI4mCxGMhiwqo#I(K8nHcBT|&!mPpb=*>ZF0Q;JJ z_$asC&U!Y4K#TDn1kJne9MD)k>sIY;@4So=p}4n4_95_UK}PQ!n8K! ztx7RqE`y>scm5n6USlFx5gXE~+dis`5%Qze@O6w8hTNtLLj|E$`5Wa79bGgM%R%P_ zNVf_&VO(lQtB+qvM)hjGacFVN6(Q~p91F`i$3d%ZxhPcU8+vu3+KZ~Lc-t-9T>#r7?eM~ zYbtTPhV&SXbI?B92^|m}+nmifxK-OdCF7QFdlWRoq;KmXs5+FVsH_murisf~{2Ura zER@6O2C9P)$CY9nFgmRcwdsJuXSN79Aze8kA-l-9QlAO&8feL(IoJ|RZN?fYC@I|) zGXS|cC>~n7yVPF|l!-CxH5k zLR5lw5nn)T?c@q)ei1es-$-wz=9+H(Ja#d#I>=KKa7F#^33xtpX#Y#(GI^f9?P`nn zdFUsW{+F_hqn9|z@1fW4`WE0Z>f^I>O)Pi*^)>&QKiN7uz9P#vgfBIc6MO$Pe?^^} z+{uZ(n*XKm^YU7HOzz*;Pu_tzKZ%Yv76ej1cuZr(Wm9Tj=IIt~(WR;mL0B@|T98K+qj-8!Jt6BqB`>DY|7QOQH+x^O_ zPwSGoS!>rYgnJF!LGVlY4+a+0cu!e@e2K!^Aw90f{L$Z1;2cmnC*a;h!|pZB6}p6J zD?wWfb4H>G3Rz?&J#tUKgR`#IzjsW$x0^6s&U?lO!wz?bZ=M;;HoHK0+ z`bsG^&&|5j)XVhH*ftgEA$S?y!V8Py?9Lji#%<5i-8FQjS37`uOw%lmKi(cx3kt^v zFpYinSnP4?&>FMl{fJ_YT7sIN+G6VOZ|BBfKh2mN0+GmJ@Qn1FK-=3LZ*aVx+cLb=uCf zrthFT?n8Lh5%!{Nj@8WtT-knT5Ga$N{Fi| zqvFon{;iAtzrB<;tuJ?z+tGD^=hwjhS|fkvdI$W^5TfJ*alidvdS3r6f-ouKE&3+! zShl0p`3k|#Q;R#`x29HXD%Z-TR{fZWTpv}dAAu&nc|iGJZvVvt z;stCiU;5Ag<3Q&zvujK$uk$*Es@s@gArs+vq*c#S*)jNY<%Exzx?(Oc~FZd z{bYzS8f5s{Z#ChFO0~U|V3tZIa$%U%6C(Lr(&-1#1@|)Br9u2EnTF9VTy-*!RC{JB zVA`4bi4jZn94#&kvi8#YD*i?KZ;q4dTPcaDgXe0-WOjUYw2FO`bokiP)l*Z0F#T+k z2sCC1@^+=sZiIA7U$tau2DHMt;IV@$CL%FxlJ#)u-gYPrNM6A@xOE5>k|fsPs*gs` zb=MnLjaO;J0)4lDlSN91l$0;L8bTxS9`)+0oFxU*i=Kq)n|NdRXzFpa@?4`EZM|du z@mN$=GWro(y}1?Y%CJF<)MgXNx*V>a0ugJKO8*7W*}O$WfLWpoCrD|}c^&Qh8kz1s ziMh9{OI(Ovs_kpBycZTcXm!>5hy@C@!KxFd#Z7VXYjdWr@gez}v+!;|%+GLv66eXY z7YjesS32rD7;+2e!Z#z3TgmAmlkSHhWX!#0v(*l)Lpc`*m6dk=COq!*GpBn!D$q_Z zu`5o%2sPLo9m}%Z+p0*!-CL1XZu6yKFszj>alyp1#`29vN$F~fMWiKR3?v(O>W+n6 zQU>jm=?lP42vA%^bkrP-KrXvAYTLY)MNY#lX}nj7sLQmsOC&we&28RZ*-i{YyVBAL zB61tU&fFYt?XkYOxSQGd3xJCyX)(S+Z|g2~php)iX_cY9n=phWb0mU%Aj2?adS0k8 zWeL-8e{^&)Inlttv5C9>#NNnI?eVByij}VpU{<2=AwM&+5X{+ zN3Wce%#}iW?E9qX4|H?~+LDw>u8?Ggub&+VNjr7;NsrhkgBlQitM(oz=(3BsMn^ z&Y(~TF3Rs8Q1*PX`ZOHvKl4IVORD_@n==v+&sGcQukJxD!riyb(pJ}Qhyd4S~Efz!Lp&e8x)wBgd|4<0QS&d+Lwm>T} zEo5vdbidVuu|Hnm(#s%cJ)hV(j5Y-1^I#vY1?oqvyn?!ze^}p-^Z_w8^V>bFf$pT zz9e80)x*9Bk=kIF1-6J~aU&?yI(26ksyB6%3O*SSx|4<=31%UI>CWKco+01rX*fO^ z(ik16?kJnI*hJtDGr)LjCN}8>3up(AwpxQI%#g4}ypku|1V4uN`Xyx*BkD$=zHkY* zL4`o6S--q~_@p!2r1~rgfhEm^Mt68wpZ}>nS-^a=TAM=ElS?8g1Re!2_xYJJZhwizaIP@6+WZ1Awx86HZFj(L+g#yM`Ya~cMd3xRs=dpNpA#wXwc(4w(*LBU>GL z=9uGe)&@j9YgZ) zLg6#%R5wrQ5^Dp}?BZd5L?CnTmO}U1;20oj_q+_8O3KlTvK3p7s_p z9UP;tDrZ*kM47%tyuT3_^R_9Z-OMTR`6{12&SYK8Q+r)Qh@p1ZC#meFy*7UW6n+ck ztG|dCub414KCCpj&C^n_vJj)j3N&WUd!$dFp7s0`rY)#9T=w-)eC)1-cw&F(QZBZ5 z%m}F&m-UVuonf^^b+)w(w+%Ek33#-vj;hD5C`QdSTc<7m{qPl zJ+b<*=}A9pBERn9`S}vCICdi>{$x0GbxW1B_v9-M#5Dhi;w$ zU7K>Zv^FLyAap41oZhl1I%5Lqly&|XTmZq*(yY4s_*L#CjND-HwoTEBxFW!qZq&)1 z7vKE!Jxtx;i2;2=WF?=s*HwF|r)HuzyX+NPfMoHX&WIdWz`_+GAC)DWWa;svC9?#BhT1J#w`_;~w3@e3zv|8%`O$m`8=(0OIR8I zd{|3X(t0XK%bv^6gH6xRD0hTVR10Y&IusTsP9RY|jw3%`MxqL|M)p2|i#`#m?-ScQ z5A$Qjty__P)SvPn@JA@EH0c)@I>8wDVd(-_)WY|h?CHafuOP zARo*|v{Q~rRM{&r7rS%ka0zJZR|CnM)g2+ol)eo#KS!I|*|~uu*~j;%DdvJ(a@qL$ z$Sq_E?He`%kHet)D1!cg+D9=ZeyU1L(mby`%EOCamzlMVrk7J{eo6Iut(ADt4Ly2p|w?82k0 za@EDukw0K_F=|EXj&TZ@p?9reH`qI-XgCPWWl)p)(i#;J#NEb9AH`KF@K791SA-}e zY{gOmMm+Iwerxrv-Og!GkdglZlz~BteYrHzkfM|rX+iT_M3C-ssLj_&m|VN1Fkgh& z8``3=nfNW#W49KO7kc>F-IR!1$^;$_`utOC&JlP6-fh}ouGhqO6_fA{iXMYH6$^}k zY*BUsQ~6m*^&@G-6@^S@JL(!o2UxMp6Dzffu`Ze~k5fZR=Q(*E_eo0Q+9k#9N*foB zye(};F-)~}K~Z906NG=i-;CE&=B{{Zgae|@h1&$3K?9dk7z-Ay3gMUPQ@~$dgoRg~ z4r!b)-MlJT>c+s=v$^d#`(Eo8;O#~Fo60t1H^uR2g0aDr>2gz2izKJR1tU}8@39ew znv#AjBX(xTUBllZj1x`k26JCt`grO80YBCGIqLuDr}P20?^8i z7@wBeSaNPA3-pZvy0BN%8(?r~GHU zGcxz^yxm_?dA9;}y#1~h`x%O_WLQ`PkRWv@kr}t=Z1x4Qs`pUERmK0O2gi~wiu zoPBrpC3=wd@sk2C-cICWMxhInH-F=e9Qa)^r)`!gmrOg(KR=_p9@3vNe*dNy_y5Vp`K?4~VtU^0#lK_YR1=gLYie#k zU_DxV*-&`>xBe07J=brAn3u)$@5=4~9s$lG$&fK4h9A^QR~FJ=qYE@#q&(x4B3@~8 z+=*Z~cjx-W>wqiI9Qe;j3Vmov1_&E?_&ln4Zz*S{tn&d9Elb5?WJ;@X68io(700%+ z^z`5`&#)?U&z($;O5IU>=wo6L)Ii_mnHT)>-(19>^b(cO@;7zDcQ-P3p`gceV(bUN>;`kA9!gyh9{2+zkI3x)MG|}%@UWJdRJO5p+U+gA(j~B)lh7>OK;w6H*;h*MPa3>`*OzZFPP5U~ z5x1@qSF`>PVP^&$@2dboP&u!xAN0C>3A*!Nh5E2!PH_pIxg04&J%TLrIGHAn2g>h- zlZSIqwP-Eu0cn@EXokV4kjltbl7D;ov#os5M%Jrizt09D76uaw0acxi=roqg?-`6q_TR|0;t5ge3xcqx9+?Rr{@>~VZn8K)}z1^5)p zDzi{O5~t2{oVc5E+hwk)R^K#^EL%3kB{Ob)kZ^dt%;Ck-&)kCM)1;=!m{+%T9OSc1 zIIY&xjupQw`~q+`k^eM!LipXD(#z~`D{7mBru3soB%z*`_M~0o*+%=%Ha{Ajk#w>| zT9e2PrI+7hO{qQW zi`qI5Lrv3EwMP!9O^IH~3L|Ls8r6rOhYA~8q~A@e3?D0?vfD*cJ2i}}jg%DqL7S#O z>~%IJhk?We=rQMyF*&_YpkZ~}Rw(3ZXDZcBbb37%@e|sgrsC+bpXlMWg0fS&pL~7; z9$*(AZplvQ;dPP_AB91XN4W>BlIAv4_e3SvRD0>}@p!Rz7O~Rmxdma<{IL6o$9m`b zfyFk*s?iIbhvBB;jsnl8brfA!HI2w<$+?Ob3YB=`^97lpC}9@9P@8&ojtp$7rm$FJ z530^UscSqJC34PMNO%>;8XP9nn`e$h~qwuamUlk^>Fdd#dnEc=2clG zLT?9OkVJ-L5JQ0GBF`=XnWM)R+T=uS9H9FslNH{^5FQPnFF>mLqhXeiPD;z#Ij9$q zddiZCT*k-!i2#_tG3RJUw^jq0H>Xo+Yd(#cx_0~StoONq zF?sU{9czn#chpqlKR{jzN{5uJjywnXhta#wZ|$LjaXRKd5CA9YY6yvv70JV(XZwTp z>`40LsE`4|qw=i0d(D{Ut|`cT+BSw5MqwLe($ZciDaW)6P${mO`YEs`Xz0*1e{w=W zWgD6?y?X;MgzL09@3L+i-f-ts3Fjbw0v}cF)zBu_Ch}z#J-WP3oO$^D1t^W7X1s)? zcSwCanPff3(`Q69gv4Jz_crle9#p==k#mKlY1ss-57t&RF$HbD;*RP(5BRP=Y?kYc z@ayPckhmopg6i`zd!qcUdBx7T`TDtmZWz$piz$_oZMV>aKy%de-ToQ7F^`FD;mB z0!@X>rlSco;@iUFbGlzRseVGF25AdlEA1>c@M{KMG2@;-d)sKgX#f>@q50VAFjVtNxlL4qaPrHa9w}}OvFqHv6qtoFe7G? zsZLBizl$oVJEB6Zh?!jy)iviXwr%l^rS3`A*xOvB#$VF4p3LGf_e5XH#%j0mjiOxG zqVckw@cA>#Ymge0Ze}M6w+T*XYy`;lUXTqJq~A~&D{HlQDVL?4*?z;g5$&@l;xyTW zIJ2q}PU69%D@{)7-lo9L~*GqmWW1V(3wArAL?I z@KoP1kkTAcqy64CJA<%xs6mawI6#VN&HV{?dQZ2Qvf0R0AAuZB;9QDtMbZyRXtx@= z+V~2Bz7pOXIG(Q>WscKN`qs0@dPiXJ=ZmMVZmYH_o{a`poDHz%2oZ|AZDbn#ft_pc zLXB{_B{$~+50khME#>S3h67&)EV`!U`^I;fI|_--t;76uFgt5L@g3WhVE-Mh7RBW literal 0 HcmV?d00001