Skip to content

Commit

Permalink
Replaced Event Bus with Event Store in projection tests to make them …
Browse files Browse the repository at this point in the history
…more explicit and self-explanatory
  • Loading branch information
oskardudycz committed Nov 29, 2022
1 parent d804a00 commit 8edf424
Show file tree
Hide file tree
Showing 12 changed files with 327 additions and 412 deletions.
Expand Up @@ -93,65 +93,37 @@ public void GettingState_ForSequenceOfEvents_ShouldSucceed()
var otherPendingShoppingCartId = Guid.NewGuid();
var otherClientId = Guid.NewGuid();

var logPosition = 0ul;

var events = new EventEnvelope[]
{
// first confirmed
new EventEnvelope<ShoppingCartOpened>(new ShoppingCartOpened(shoppingCartId, clientId),
EventMetadata.For(1, ++logPosition)),
new EventEnvelope<ProductItemAddedToShoppingCart>(
new ProductItemAddedToShoppingCart(shoppingCartId, twoPairsOfShoes),
EventMetadata.For(2, ++logPosition)),
new EventEnvelope<ProductItemAddedToShoppingCart>(
new ProductItemAddedToShoppingCart(shoppingCartId, tShirt), EventMetadata.For(3, ++logPosition)),
new EventEnvelope<ProductItemRemovedFromShoppingCart>(
new ProductItemRemovedFromShoppingCart(shoppingCartId, pairOfShoes),
EventMetadata.For(4, ++logPosition)),
new EventEnvelope<ShoppingCartConfirmed>(new ShoppingCartConfirmed(shoppingCartId, DateTime.UtcNow),
EventMetadata.For(5, ++logPosition)),

// cancelled
new EventEnvelope<ShoppingCartOpened>(new ShoppingCartOpened(cancelledShoppingCartId, clientId),
EventMetadata.For(1, ++logPosition)),
new EventEnvelope<ProductItemAddedToShoppingCart>(
new ProductItemAddedToShoppingCart(cancelledShoppingCartId, dress),
EventMetadata.For(2, ++logPosition)),
new EventEnvelope<ShoppingCartCanceled>(new ShoppingCartCanceled(cancelledShoppingCartId, DateTime.UtcNow),
EventMetadata.For(3, ++logPosition)),

// confirmed but other client
new EventEnvelope<ShoppingCartOpened>(new ShoppingCartOpened(otherClientShoppingCartId, otherClientId),
EventMetadata.For(1, ++logPosition)),
new EventEnvelope<ProductItemAddedToShoppingCart>(
new ProductItemAddedToShoppingCart(otherClientShoppingCartId, dress),
EventMetadata.For(2, ++logPosition)),
new EventEnvelope<ShoppingCartConfirmed>(
new ShoppingCartConfirmed(otherClientShoppingCartId, DateTime.UtcNow),
EventMetadata.For(3, ++logPosition)),

// second confirmed
new EventEnvelope<ShoppingCartOpened>(new ShoppingCartOpened(otherConfirmedShoppingCartId, clientId),
EventMetadata.For(1, ++logPosition)),
new EventEnvelope<ProductItemAddedToShoppingCart>(
new ProductItemAddedToShoppingCart(otherConfirmedShoppingCartId, trousers),
EventMetadata.For(2, ++logPosition)),
new EventEnvelope<ShoppingCartConfirmed>(
new ShoppingCartConfirmed(otherConfirmedShoppingCartId, DateTime.UtcNow),
EventMetadata.For(3, ++logPosition)),

// first pending
new EventEnvelope<ShoppingCartOpened>(new ShoppingCartOpened(otherPendingShoppingCartId, clientId),
EventMetadata.For(1, ++logPosition))
};

var eventBus = new EventStore();
var eventStore = new EventStore();
var database = new Database();

// TODO:
// 1. Register here your event handlers using `eventBus.Register`.
// 2. Store results in database.
eventBus.Append(events);

// first confirmed
eventStore.Append(shoppingCartId, new ShoppingCartOpened(shoppingCartId, clientId));
eventStore.Append(shoppingCartId, new ProductItemAddedToShoppingCart(shoppingCartId, twoPairsOfShoes));
eventStore.Append(shoppingCartId, new ProductItemAddedToShoppingCart(shoppingCartId, tShirt));
eventStore.Append(shoppingCartId, new ProductItemRemovedFromShoppingCart(shoppingCartId, pairOfShoes));
eventStore.Append(shoppingCartId, new ShoppingCartConfirmed(shoppingCartId, DateTime.UtcNow));

// cancelled
eventStore.Append(cancelledShoppingCartId, new ShoppingCartOpened(cancelledShoppingCartId, clientId));
eventStore.Append(cancelledShoppingCartId, new ProductItemAddedToShoppingCart(cancelledShoppingCartId, dress));
eventStore.Append(cancelledShoppingCartId, new ShoppingCartCanceled(cancelledShoppingCartId, DateTime.UtcNow));

// confirmed but other client
eventStore.Append(otherClientShoppingCartId, new ShoppingCartOpened(otherClientShoppingCartId, otherClientId));
eventStore.Append(otherClientShoppingCartId, new ProductItemAddedToShoppingCart(otherClientShoppingCartId, dress));
eventStore.Append(otherClientShoppingCartId, new ShoppingCartConfirmed(otherClientShoppingCartId, DateTime.UtcNow));

// second confirmed
eventStore.Append(otherConfirmedShoppingCartId, new ShoppingCartOpened(otherConfirmedShoppingCartId, clientId));
eventStore.Append(otherConfirmedShoppingCartId, new ProductItemAddedToShoppingCart(otherConfirmedShoppingCartId, trousers));
eventStore.Append(otherConfirmedShoppingCartId, new ShoppingCartConfirmed(otherConfirmedShoppingCartId, DateTime.UtcNow));

// first pending
eventStore.Append(otherPendingShoppingCartId, new ShoppingCartOpened(otherPendingShoppingCartId, clientId));

// first confirmed
var shoppingCart = database.Get<ShoppingCartDetails>(shoppingCartId)!;
Expand Down
Expand Up @@ -3,6 +3,7 @@ namespace IntroductionToEventSourcing.GettingStateFromEvents.Tools;
public class EventStore
{
private readonly Dictionary<Type, List<Action<EventEnvelope>>> handlers = new();
private readonly Dictionary<Guid, List<EventEnvelope>> events = new();

public void Register<TEvent>(Action<EventEnvelope<TEvent>> handler) where TEvent : notnull
{
Expand All @@ -16,21 +17,26 @@ public class EventStore
handlers.Add(eventType, new List<Action<EventEnvelope>> { WrappedHandler });
}

public void Append(EventEnvelope eventEnvelope)
public void Append<TEvent>(Guid streamId, TEvent @event) where TEvent : notnull
{
if (!events.ContainsKey(streamId))
events[streamId] = new List<EventEnvelope>();

var eventEnvelope = new EventEnvelope<TEvent>(@event,
EventMetadata.For(
(ulong)events[streamId].Count + 1,
(ulong)events.Values.Sum(s => s.Count)
)
);

events[streamId].Add(eventEnvelope);

if (!handlers.TryGetValue(eventEnvelope.Data.GetType(), out var eventHandlers)) return;

foreach (var handle in eventHandlers)
{
handle(eventEnvelope);
}
}

public void Append(params EventEnvelope[] eventEnvelopes)
{
foreach (var @event in eventEnvelopes)
{
Append(@event);
}
}
}

Expand Up @@ -246,81 +246,52 @@ public void GettingState_ForSequenceOfEvents_ShouldSucceed()
var otherPendingShoppingCartId = Guid.NewGuid();
var otherClientId = Guid.NewGuid();

var logPosition = 0ul;

var events = new EventEnvelope[]
{
// first confirmed
new EventEnvelope<ShoppingCartOpened>(new ShoppingCartOpened(shoppingCartId, clientId),
EventMetadata.For(1, ++logPosition)),
new EventEnvelope<ProductItemAddedToShoppingCart>(
new ProductItemAddedToShoppingCart(shoppingCartId, twoPairsOfShoes),
EventMetadata.For(2, ++logPosition)),
new EventEnvelope<ProductItemAddedToShoppingCart>(
new ProductItemAddedToShoppingCart(shoppingCartId, tShirt), EventMetadata.For(3, ++logPosition)),
new EventEnvelope<ProductItemRemovedFromShoppingCart>(
new ProductItemRemovedFromShoppingCart(shoppingCartId, pairOfShoes),
EventMetadata.For(4, ++logPosition)),
new EventEnvelope<ShoppingCartConfirmed>(new ShoppingCartConfirmed(shoppingCartId, DateTime.UtcNow),
EventMetadata.For(5, ++logPosition)),

// cancelled
new EventEnvelope<ShoppingCartOpened>(new ShoppingCartOpened(cancelledShoppingCartId, clientId),
EventMetadata.For(1, ++logPosition)),
new EventEnvelope<ProductItemAddedToShoppingCart>(
new ProductItemAddedToShoppingCart(cancelledShoppingCartId, dress),
EventMetadata.For(2, ++logPosition)),
new EventEnvelope<ShoppingCartCanceled>(new ShoppingCartCanceled(cancelledShoppingCartId, DateTime.UtcNow),
EventMetadata.For(3, ++logPosition)),

// confirmed but other client
new EventEnvelope<ShoppingCartOpened>(new ShoppingCartOpened(otherClientShoppingCartId, otherClientId),
EventMetadata.For(1, ++logPosition)),
new EventEnvelope<ProductItemAddedToShoppingCart>(
new ProductItemAddedToShoppingCart(otherClientShoppingCartId, dress),
EventMetadata.For(2, ++logPosition)),
new EventEnvelope<ShoppingCartConfirmed>(
new ShoppingCartConfirmed(otherClientShoppingCartId, DateTime.UtcNow),
EventMetadata.For(3, ++logPosition)),

// second confirmed
new EventEnvelope<ShoppingCartOpened>(new ShoppingCartOpened(otherConfirmedShoppingCartId, clientId),
EventMetadata.For(1, ++logPosition)),
new EventEnvelope<ProductItemAddedToShoppingCart>(
new ProductItemAddedToShoppingCart(otherConfirmedShoppingCartId, trousers),
EventMetadata.For(2, ++logPosition)),
new EventEnvelope<ShoppingCartConfirmed>(
new ShoppingCartConfirmed(otherConfirmedShoppingCartId, DateTime.UtcNow),
EventMetadata.For(3, ++logPosition)),

// first pending
new EventEnvelope<ShoppingCartOpened>(new ShoppingCartOpened(otherPendingShoppingCartId, clientId),
EventMetadata.For(1, ++logPosition))
};

var eventBus = new EventStore();
var eventStore = new EventStore();
var database = new Database();

// TODO:
// 1. Register here your event handlers using `eventBus.Register`.
// 2. Store results in database.
var shoppingCartDetailsProjection = new ShoppingCartDetailsProjection(database);

eventBus.Register<ShoppingCartOpened>(shoppingCartDetailsProjection.Handle);
eventBus.Register<ProductItemAddedToShoppingCart>(shoppingCartDetailsProjection.Handle);
eventBus.Register<ProductItemRemovedFromShoppingCart>(shoppingCartDetailsProjection.Handle);
eventBus.Register<ShoppingCartConfirmed>(shoppingCartDetailsProjection.Handle);
eventBus.Register<ShoppingCartCanceled>(shoppingCartDetailsProjection.Handle);
eventStore.Register<ShoppingCartOpened>(shoppingCartDetailsProjection.Handle);
eventStore.Register<ProductItemAddedToShoppingCart>(shoppingCartDetailsProjection.Handle);
eventStore.Register<ProductItemRemovedFromShoppingCart>(shoppingCartDetailsProjection.Handle);
eventStore.Register<ShoppingCartConfirmed>(shoppingCartDetailsProjection.Handle);
eventStore.Register<ShoppingCartCanceled>(shoppingCartDetailsProjection.Handle);

var shoppingCartShortInfoProjection = new ShoppingCartShortInfoProjection(database);

eventBus.Register<ShoppingCartOpened>(shoppingCartShortInfoProjection.Handle);
eventBus.Register<ProductItemAddedToShoppingCart>(shoppingCartShortInfoProjection.Handle);
eventBus.Register<ProductItemRemovedFromShoppingCart>(shoppingCartShortInfoProjection.Handle);
eventBus.Register<ShoppingCartConfirmed>(shoppingCartShortInfoProjection.Handle);
eventBus.Register<ShoppingCartCanceled>(shoppingCartShortInfoProjection.Handle);
eventStore.Register<ShoppingCartOpened>(shoppingCartShortInfoProjection.Handle);
eventStore.Register<ProductItemAddedToShoppingCart>(shoppingCartShortInfoProjection.Handle);
eventStore.Register<ProductItemRemovedFromShoppingCart>(shoppingCartShortInfoProjection.Handle);
eventStore.Register<ShoppingCartConfirmed>(shoppingCartShortInfoProjection.Handle);
eventStore.Register<ShoppingCartCanceled>(shoppingCartShortInfoProjection.Handle);

// first confirmed
eventStore.Append(shoppingCartId, new ShoppingCartOpened(shoppingCartId, clientId));
eventStore.Append(shoppingCartId, new ProductItemAddedToShoppingCart(shoppingCartId, twoPairsOfShoes));
eventStore.Append(shoppingCartId, new ProductItemAddedToShoppingCart(shoppingCartId, tShirt));
eventStore.Append(shoppingCartId, new ProductItemRemovedFromShoppingCart(shoppingCartId, pairOfShoes));
eventStore.Append(shoppingCartId, new ShoppingCartConfirmed(shoppingCartId, DateTime.UtcNow));

// cancelled
eventStore.Append(cancelledShoppingCartId, new ShoppingCartOpened(cancelledShoppingCartId, clientId));
eventStore.Append(cancelledShoppingCartId, new ProductItemAddedToShoppingCart(cancelledShoppingCartId, dress));
eventStore.Append(cancelledShoppingCartId, new ShoppingCartCanceled(cancelledShoppingCartId, DateTime.UtcNow));

eventBus.Append(events);
// confirmed but other client
eventStore.Append(otherClientShoppingCartId, new ShoppingCartOpened(otherClientShoppingCartId, otherClientId));
eventStore.Append(otherClientShoppingCartId, new ProductItemAddedToShoppingCart(otherClientShoppingCartId, dress));
eventStore.Append(otherClientShoppingCartId, new ShoppingCartConfirmed(otherClientShoppingCartId, DateTime.UtcNow));

// second confirmed
eventStore.Append(otherConfirmedShoppingCartId, new ShoppingCartOpened(otherConfirmedShoppingCartId, clientId));
eventStore.Append(otherConfirmedShoppingCartId, new ProductItemAddedToShoppingCart(otherConfirmedShoppingCartId, trousers));
eventStore.Append(otherConfirmedShoppingCartId, new ShoppingCartConfirmed(otherConfirmedShoppingCartId, DateTime.UtcNow));

// first pending
eventStore.Append(otherPendingShoppingCartId, new ShoppingCartOpened(otherPendingShoppingCartId, clientId));

// first confirmed
var shoppingCart = database.Get<ShoppingCartDetails>(shoppingCartId)!;
Expand Down
Expand Up @@ -2,10 +2,11 @@ namespace IntroductionToEventSourcing.GettingStateFromEvents.Tools;

public class EventStore
{
private readonly Dictionary<Type, List<Action<EventEnvelope>>> handlers = new();

private readonly Random random = new();

private readonly Dictionary<Type, List<Action<EventEnvelope>>> handlers = new();
private readonly Dictionary<Guid, List<EventEnvelope>> events = new();

public void Register<TEvent>(Action<EventEnvelope<TEvent>> handler) where TEvent : notnull
{
var eventType = typeof(TEvent);
Expand Down Expand Up @@ -33,11 +34,29 @@ public void Append(EventEnvelope eventEnvelope)
}
}

public void Append(params EventEnvelope[] eventEnvelopes)
public void Append<TEvent>(Guid streamId, TEvent @event) where TEvent : notnull
{
foreach (var @event in eventEnvelopes)
if (!events.ContainsKey(streamId))
events[streamId] = new List<EventEnvelope>();

var eventEnvelope = new EventEnvelope<TEvent>(@event,
EventMetadata.For(
(ulong)events[streamId].Count + 1,
(ulong)events.Values.Sum(s => s.Count)
)
);

events[streamId].Add(eventEnvelope);

if (!handlers.TryGetValue(eventEnvelope.Data.GetType(), out var eventHandlers)) return;

foreach (var handle in eventHandlers)
{
Append(@event);
var numberOfRepeatedPublish = random.Next(1, 5);
do
{
handle(eventEnvelope);
} while (--numberOfRepeatedPublish > 0);
}
}
}

0 comments on commit 8edf424

Please sign in to comment.