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.
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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 void Register<TEvent>(Action<EventEnvelope<TEvent>> handler) where TEvent
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);
}
}
}

Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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);
}
}
}
Loading

0 comments on commit 8edf424

Please sign in to comment.