I am working on a CQRS template. I created one project related to this approach in which I can insert and extract data. I found out that there are two different models Write Model (Commands) and Read Model (Query). I just want to know that my approach to the recording model is right or not. And how to use a temporary database to search for events when multiple users perform the same operations.
Command.cs
public class Command : Message { } public class Insert : Command { public readonly Guid Id; public readonly string Name; public Insert(Guid id, string name) { Id = id; Name = name; } } public class Update : Command { public readonly Guid Id; public readonly string NewName; public readonly int OriginalVersion; public Update(Guid id, string newName) { Id = id; NewName = newName; } } public class Delete : Command { public Guid Id; public readonly int OriginalVersion; public Delete(Guid id) { Id = id; } }
Event.cs
public class Event:Message { public int Version; } public class Inserted : Event { public readonly Guid Id; public readonly string Name; public Inserted(Guid id, string name) { Id = id; Name = name; } } public class Updated : Event { public readonly Guid Id; public readonly string NewName; public readonly int OriginalVersion; public Updated(Guid id, string newName) { Id = id; NewName = newName; } } public class Deleted : Event { public Guid Id; public Deleted(Guid id) { Id = id; } }
EventStore.cs
public interface IEventStore { void SaveEvents(Guid aggregateId, IEnumerable<Event> events, int expectedVersion); List<Event> GetEventsForAggregate(Guid aggregateId); } public class EventStore : IEventStore { private readonly IEventPublisher _publisher; private struct EventDescriptor { public readonly Event EventData; public readonly Guid Id; public readonly int Version; public EventDescriptor(Guid id, Event eventData, int version) { EventData = eventData; Version = version; Id = id; } } public EventStore(IEventPublisher publisher) { _publisher = publisher; } private readonly Dictionary<Guid, List<EventDescriptor>> _current = new Dictionary<Guid, List<EventDescriptor>>(); public void SaveEvents(Guid aggregateId, IEnumerable<Event> events, int expectedVersion) { List<EventDescriptor> eventDescriptors; if (!_current.TryGetValue(aggregateId, out eventDescriptors)) { eventDescriptors = new List<EventDescriptor>(); _current.Add(aggregateId, eventDescriptors); } else if (eventDescriptors[eventDescriptors.Count - 1].Version != expectedVersion && expectedVersion != -1) { throw new ConcurrencyException(); } var i = expectedVersion; foreach (var @event in events) { i++; @event.Version = i; eventDescriptors.Add(new EventDescriptor(aggregateId, @event, i)); _publisher.Publish(@event); } } public List<Event> GetEventsForAggregate(Guid aggregateId) { List<EventDescriptor> eventDescriptors; if (!_current.TryGetValue(aggregateId, out eventDescriptors)) { throw new AggregateNotFoundException(); } return eventDescriptors.Select(desc => desc.EventData).ToList(); } } public class AggregateNotFoundException : Exception { } public class ConcurrencyException : Exception { }
ReadModel.cs
public interface IReadModelFacade { IEnumerable<InventoryItemListDto> GetInventoryItems(); InventoryItemDetailsDto GetInventoryItemDetails(Guid id); } public class InventoryItemDetailsDto { public Guid Id; public string Name; public int CurrentCount; public int Version; public InventoryItemDetailsDto(Guid id, string name, int currentCount, int version) { Id = id; Name = name; CurrentCount = currentCount; Version = version; } } public class InventoryItemListDto { public Guid Id; public string Name; public InventoryItemListDto(Guid id, string name) { Id = id; Name = name; } } public class InventoryListView : Handles<Inserted>, Handles<Updated> { public void Handle(Inserted message) { BullShitDatabase.list.Add(new InventoryItemListDto(message.Id, message.Name)); } public void Handle(Updated message) { var item = BullShitDatabase.list.Find(x => x.Id == message.Id); item.Name = message.NewName; } } public class InvenotryItemDetailView : Handles<Inserted>, Handles<Updated> { public void Handle(Inserted message) { BullShitDatabase.details.Add(message.Id, new InventoryItemDetailsDto(message.Id, message.Name, 0, 0)); } public void Handle(Updated message) { InventoryItemDetailsDto d = GetDetailsItem(message.Id); d.Name = message.NewName; d.Version = message.Version; } private InventoryItemDetailsDto GetDetailsItem(Guid id) { InventoryItemDetailsDto d; if (!BullShitDatabase.details.TryGetValue(id, out d)) { throw new InvalidOperationException("did not find the original inventory this shouldnt happen"); } return d; } } public class ReadModelFacade : IReadModelFacade { public IEnumerable<InventoryItemListDto> GetInventoryItems() { return BullShitDatabase.list; } public InventoryItemDetailsDto GetInventoryItemDetails(Guid id) { return BullShitDatabase.details[id]; } } public static class BullShitDatabase { public static Dictionary<Guid, InventoryItemDetailsDto> details = new Dictionary<Guid, InventoryItemDetailsDto>(); public static List<InventoryItemListDto> list = new List<InventoryItemListDto>(); }