How to use eventstore in cqrs template

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>(); } 
+5
source share
1 answer

It doesn’t matter if you use EventStore or any other storage mechanism, you should still code the interfaces (contracts).

But first of all, you indicate that IMO commands are not properly defined, they must be immutable objects that carry data and represent a domain operation (CRUD or not), so why do you have methods defined in commands?

This is not a problem defining a command as a class, in the end it will be needed, but why don't you have an interface as a base type for all commands? (SOLID Principles)

All class names (commands / events) should make sense, that is, "Update", "Delete" ... they don’t speak very much.

Also, I don’t see where your service level is. The service level should be responsible for processing the commands, and how do you plan to do this?

You have an example of how I will do this (a short essay, but it gives you an idea):

 // Message definitions public interface IMessage { Guid ID {get; set;} } public interface IEvent : IMessage { } public interface ICommand : IMessage { } public class DeleteUserCommand : ICommand { public Guid ID {get; set;} public Guid UserId {get; set;} } public class UserDeletedEvent : IEvent { public Guid ID {get; set;} public Guid UserId {get; set;} } // Repository definitions public interface IRepository { } public interface IUserRepository : IRepository { void DeleteUser(Guid userId); } public UserRepository : IUserRepository { public void DeleteUser(Guid userId) {} } // Service definitions public interface IService { } public class UserService : IService, IHandles<DeleteUserCommand> { public IUserRepository UserRepository {get; set;} public void Handle(DeleteUserCommand deleteUserCommand) { UserRepository.DeleteUser(deleteUserCommand.Id) //raise event } } 
+1
source

Source: https://habr.com/ru/post/1200901/


All Articles