简体   繁体   English

如何在cqrs模式中使用eventstore

[英]How to use eventstore in cqrs pattern

I am working on a CQRS pattern. 我正在研究CQRS模式。 I have created one project related to this approach in which I can insert and retrieve data. 我创建了一个与此方法相关的项目,我可以在其中插入和检索数据。 I came to know that there are two different models Write Model(Commands) and Read Model(Query). 我开始知道有两种不同的模型Write Model(Commands)和Read Model(Query)。 I just want to know that my approach for write model is right or not. 我只想知道我的写模型方法是否正确。 And how to use temporarily database for event sourcing when multiple users doing same operations. 当多个用户进行相同的操作时,如何使用临时数据库进行事件采购。

Command.cs 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 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 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 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>();
  }

It should't matter whether you're using EventStore or any other storing mechanism, you should be coding against interfaces (contracts) anyway. 无论你是使用EventStore还是任何其他存储机制,都应该对接口(契约)进行编码。

But first things first, you commands IMO are not properly defined, they should be immutable objects which carry data and represent a domain operation (CRUD or not), so why do you have methods defined in the commands? 但首先,你的命令IMO没有正确定义,它们应该是不可变的对象,它们携带数据并表示域操作(CRUD与否),那么为什么你在命令中定义了方法呢?

It is not a problem defining a command as a class, you'll need one in the end, but why don't you have an interface as the base type for all the commands? 将命令定义为类不是问题,最后需要一个,但为什么不将接口作为所有命令的基类型? (SOLID principles) (SOLID原则)

All the class names (commands/events) have to be meaningful, that said, Update, Delete... don't say much really. 所有的类名(命令/事件)都必须有意义,即更新,删除......真的不多说。

Also I don't see where your service layer is. 我也看不到你的服务层在哪里。 The service layer should be responsible for handling the commands, so how are you planning to do this? 服务层应负责处理命令,那么您打算如何做到这一点?

Bellow you have an example of how I would do it (a tad abstract 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
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM