forked from oskardudycz/EventSourcing.NetCore
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Exercise 05 - Getting the current entity state from events using Marten
- Loading branch information
1 parent
b2e3faa
commit 00c974b
Showing
20 changed files
with
802 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 23 additions & 0 deletions
23
...nToEventSourcing/05-GettingStateFromEvents.Marten/05-GettingStateFromEvents.Marten.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<RootNamespace>IntroductionToEventSourcing.GettingStateFromEvents</RootNamespace> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="FluentAssertions" Version="6.5.1" /> | ||
<PackageReference Include="Marten" Version="5.0.0-alpha.6" /> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" /> | ||
<PackageReference Include="xunit" Version="2.4.1" /> | ||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> | ||
<PrivateAssets>all</PrivateAssets> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> | ||
</PackageReference> | ||
<PackageReference Include="coverlet.collector" Version="3.1.2"> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
<PrivateAssets>all</PrivateAssets> | ||
</PackageReference> | ||
</ItemGroup> | ||
|
||
</Project> |
110 changes: 110 additions & 0 deletions
110
...ToEventSourcing/05-GettingStateFromEvents.Marten/Immutable/GettingStateFromEventsTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
using FluentAssertions; | ||
using IntroductionToEventSourcing.GettingStateFromEvents.Tools; | ||
using Marten; | ||
using Xunit; | ||
|
||
namespace IntroductionToEventSourcing.GettingStateFromEvents.Immutable; | ||
|
||
// EVENTS | ||
public record ShoppingCartOpened( | ||
Guid ShoppingCartId, | ||
Guid ClientId | ||
); | ||
|
||
public record ProductItemAddedToShoppingCart( | ||
Guid ShoppingCartId, | ||
PricedProductItem ProductItem | ||
); | ||
|
||
public record ProductItemRemovedFromShoppingCart( | ||
Guid ShoppingCartId, | ||
PricedProductItem ProductItem | ||
); | ||
|
||
public record ShoppingCartConfirmed( | ||
Guid ShoppingCartId, | ||
DateTime ConfirmedAt | ||
); | ||
|
||
public record ShoppingCartCanceled( | ||
Guid ShoppingCartId, | ||
DateTime CanceledAt | ||
); | ||
|
||
// VALUE OBJECTS | ||
public record PricedProductItem( | ||
Guid ProductId, | ||
int Quantity, | ||
decimal UnitPrice | ||
); | ||
|
||
// ENTITY | ||
public record ShoppingCart( | ||
Guid Id, | ||
Guid ClientId, | ||
ShoppingCartStatus Status, | ||
PricedProductItem[] ProductItems, | ||
DateTime? ConfirmedAt = null, | ||
DateTime? CanceledAt = null | ||
); | ||
|
||
public enum ShoppingCartStatus | ||
{ | ||
Pending = 1, | ||
Confirmed = 2, | ||
Canceled = 3 | ||
} | ||
|
||
public class GettingStateFromEventsTests: MartenTest | ||
{ | ||
/// <summary> | ||
/// Solution - Mutable entity with When method | ||
/// </summary> | ||
/// <param name="documentSession"></param> | ||
/// <param name="shoppingCartId"></param> | ||
/// <returns></returns> | ||
private static Task<ShoppingCart> GetShoppingCart(IDocumentSession documentSession, Guid shoppingCartId) | ||
{ | ||
// 1. Add logic here | ||
throw new NotImplementedException(); | ||
} | ||
|
||
[Fact] | ||
[Trait("Category", "SkipCI")] | ||
public async Task GettingState_ForSequenceOfEvents_ShouldSucceed() | ||
{ | ||
var shoppingCartId = Guid.NewGuid(); | ||
var clientId = Guid.NewGuid(); | ||
var shoesId = Guid.NewGuid(); | ||
var tShirtId = Guid.NewGuid(); | ||
var twoPairsOfShoes = new PricedProductItem(shoesId, 2, 100); | ||
var pairOfShoes = new PricedProductItem(shoesId, 1, 100); | ||
var tShirt = new PricedProductItem(tShirtId, 1, 50); | ||
|
||
var events = new object[] | ||
{ | ||
new ShoppingCartOpened(shoppingCartId, clientId), | ||
new ProductItemAddedToShoppingCart(shoppingCartId, twoPairsOfShoes), | ||
new ProductItemAddedToShoppingCart(shoppingCartId, tShirt), | ||
new ProductItemRemovedFromShoppingCart(shoppingCartId, pairOfShoes), | ||
new ShoppingCartConfirmed(shoppingCartId, DateTime.UtcNow), | ||
new ShoppingCartCanceled(shoppingCartId, DateTime.UtcNow) | ||
}; | ||
|
||
await AppendEvents(shoppingCartId, events, CancellationToken.None); | ||
|
||
var shoppingCart = await GetShoppingCart(DocumentSession, shoppingCartId); | ||
|
||
shoppingCart.Id.Should().Be(shoppingCartId); | ||
shoppingCart.ClientId.Should().Be(clientId); | ||
shoppingCart.ProductItems.Should().HaveCount(2); | ||
|
||
shoppingCart.ProductItems[0].ProductId.Should().Be(shoesId); | ||
shoppingCart.ProductItems[0].Quantity.Should().Be(pairOfShoes.Quantity); | ||
shoppingCart.ProductItems[0].UnitPrice.Should().Be(pairOfShoes.UnitPrice); | ||
|
||
shoppingCart.ProductItems[1].ProductId.Should().Be(tShirtId); | ||
shoppingCart.ProductItems[1].Quantity.Should().Be(tShirt.Quantity); | ||
shoppingCart.ProductItems[1].UnitPrice.Should().Be(tShirt.UnitPrice); | ||
} | ||
} |
127 changes: 127 additions & 0 deletions
127
...onToEventSourcing/05-GettingStateFromEvents.Marten/Mutable/GettingStateFromEventsTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
using FluentAssertions; | ||
using IntroductionToEventSourcing.GettingStateFromEvents.Tools; | ||
using Marten; | ||
using Xunit; | ||
|
||
namespace IntroductionToEventSourcing.GettingStateFromEvents.Mutable; | ||
|
||
// EVENTS | ||
public record ShoppingCartOpened( | ||
Guid ShoppingCartId, | ||
Guid ClientId | ||
); | ||
|
||
public record ProductItemAddedToShoppingCart( | ||
Guid ShoppingCartId, | ||
PricedProductItem ProductItem | ||
); | ||
|
||
public record ProductItemRemovedFromShoppingCart( | ||
Guid ShoppingCartId, | ||
PricedProductItem ProductItem | ||
); | ||
|
||
public record ShoppingCartConfirmed( | ||
Guid ShoppingCartId, | ||
DateTime ConfirmedAt | ||
); | ||
|
||
public record ShoppingCartCanceled( | ||
Guid ShoppingCartId, | ||
DateTime CanceledAt | ||
); | ||
|
||
// 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; | ||
} | ||
|
||
// ENTITY | ||
public class ShoppingCart | ||
{ | ||
public Guid Id { get; private set; } | ||
public Guid ClientId { get; private set; } | ||
public ShoppingCartStatus Status { get; private set; } | ||
public IList<PricedProductItem> ProductItems { get; } = new List<PricedProductItem>(); | ||
public DateTime? ConfirmedAt { get; private set; } | ||
public DateTime? CanceledAt { get; private set; } | ||
} | ||
|
||
public enum ShoppingCartStatus | ||
{ | ||
Pending = 1, | ||
Confirmed = 2, | ||
Canceled = 3 | ||
} | ||
|
||
public class GettingStateFromEventsTests: MartenTest | ||
{ | ||
/// <summary> | ||
/// Solution - Mutable entity with When method | ||
/// </summary> | ||
/// <param name="documentSession"></param> | ||
/// <param name="shoppingCartId"></param> | ||
/// <returns></returns> | ||
private static Task<ShoppingCart> GetShoppingCart(IDocumentSession documentSession, Guid shoppingCartId) | ||
{ | ||
// 1. Add logic here | ||
throw new NotImplementedException(); | ||
} | ||
|
||
[Fact] | ||
[Trait("Category", "SkipCI")] | ||
public async Task GettingState_ForSequenceOfEvents_ShouldSucceed() | ||
{ | ||
var shoppingCartId = Guid.NewGuid(); | ||
var clientId = Guid.NewGuid(); | ||
var shoesId = Guid.NewGuid(); | ||
var tShirtId = Guid.NewGuid(); | ||
var twoPairsOfShoes = | ||
new PricedProductItem | ||
{ | ||
ProductId = shoesId, Quantity = 2, UnitPrice = 100 | ||
}; | ||
var pairOfShoes = | ||
new PricedProductItem | ||
{ | ||
ProductId = shoesId, Quantity = 1, UnitPrice = 100 | ||
}; | ||
var tShirt = | ||
new PricedProductItem | ||
{ | ||
ProductId = tShirtId, Quantity = 1, UnitPrice = 50 | ||
}; | ||
|
||
var events = new object[] | ||
{ | ||
new ShoppingCartOpened(shoppingCartId, clientId), | ||
new ProductItemAddedToShoppingCart(shoppingCartId, twoPairsOfShoes), | ||
new ProductItemAddedToShoppingCart(shoppingCartId, tShirt), | ||
new ProductItemRemovedFromShoppingCart(shoppingCartId, pairOfShoes), | ||
new ShoppingCartConfirmed(shoppingCartId, DateTime.UtcNow), | ||
new ShoppingCartCanceled(shoppingCartId, DateTime.UtcNow) | ||
}; | ||
|
||
await AppendEvents(shoppingCartId, events, CancellationToken.None); | ||
|
||
var shoppingCart = await GetShoppingCart(DocumentSession, shoppingCartId); | ||
|
||
shoppingCart.Id.Should().Be(shoppingCartId); | ||
shoppingCart.ClientId.Should().Be(clientId); | ||
shoppingCart.ProductItems.Should().HaveCount(2); | ||
|
||
shoppingCart.ProductItems[0].ProductId.Should().Be(shoesId); | ||
shoppingCart.ProductItems[0].Quantity.Should().Be(pairOfShoes.Quantity); | ||
shoppingCart.ProductItems[0].UnitPrice.Should().Be(pairOfShoes.UnitPrice); | ||
|
||
shoppingCart.ProductItems[1].ProductId.Should().Be(tShirtId); | ||
shoppingCart.ProductItems[1].Quantity.Should().Be(tShirt.Quantity); | ||
shoppingCart.ProductItems[1].UnitPrice.Should().Be(tShirt.UnitPrice); | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
Workshops/IntroductionToEventSourcing/05-GettingStateFromEvents.Marten/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# Exercise 05 - Getting the current entity state from events using Marten | ||
|
||
Having a defined structure of events and an entity representing the shopping cart from the [previous exercise](../01-EventsDefinition), fill a `GetShoppingCart` function that will rebuild the current state from events. | ||
|
||
If needed you can modify the events or entity structure. | ||
|
||
There are two variations: | ||
- using mutable entities: [Mutable/GettingStateFromEventsTests.cs](./Mutable/GettingStateFromEventsTests.cs), | ||
- using fully immutable structures: [Immutable/Solution1/GettingStateFromEventsTests.cs](./Immutable/GettingStateFromEventsTests.cs). | ||
|
||
Select your preferred approach (or both) to solve this use case. If needed you can modify entities or events. | ||
|
||
## Prerequisites | ||
Run [docker-compose](../docker-compose.yml) script from the main workshop repository to start Postgres instance. | ||
|
||
```shell | ||
docker-compose up | ||
``` | ||
|
||
After that you can use PG admin (IDE for Postgres) to see how tables and data look like. It's available at: http://localhost:5050. | ||
- Login: `admin@pgadmin.org`, Password: `admin` | ||
- To connect to server click right mouse on Servers, then Register Server and use host: `postgres`, user: `postgres`, password: `Password12!` |
Oops, something went wrong.