关于CQRS的两个大问号

时间:2013-06-27 08:41:11

标签: cqrs

我是一名C#开发人员,但我几乎阅读了有关cqrs的每一个教程,如果语言是Java,无关紧要,因为我想学习cqrs的结构和基础。

但是现在我想,我读了这么多教程的事实是问题,因为教程中存在差异,现在我很困惑,不知道我必须使用哪种技术。

我脑子里有两个主要的问题,也许你们中的一些人可以带来一些清晰度。

  1. 在命令方面,我应该在哪里放置逻辑来调用我的ORM? 一些教程在命令处理程序中执行此操作(对我来说更具逻辑性),有些教程在事件处理程序中执行,这将由命令处理程序触发,在这种情况下只执行验证逻辑。

  2. 对于事件存储和撤消思考,我需要将哪些数据保存到数据库中,一些教程会保存聚合,一些教程会保存事件模型。

  3. 我希望有人可以解释我使用什么模式以及为什么,也许在不同情况下,我不知道。

    一个实际的例子会很棒。 (只有伪代码)
    也许是用户注册,RegisterTheUser命令:

    要做的事情:

    • 检查用户名是否已被使用
    • 将用户添加到db
    • 发送确认邮件(在命令中或在UserIsRegistered事件中?)
    • Fire事件ConfirmationMailSended或仅UserIsRegistered事件?

    亲切的问候

    编辑: 这是我目前的实现(简单)

    public class RegisterTheUser : ICommand
    {
        public String Login { get; set; }
    
        public String Password { get; set; }
    }
    
    
    public class RegisterTheUserHandler : IHandleCommand<RegisterTheUser, AccountAggregate>
    {
        public void Handle(AccountAggregate agg, RegisterTheUser command)
        {
            if (agg.IsLoginAlreadyInUse(command.Login))
                throw new LoginIsAlreadyInUse();
    
            agg.AddUserAccount(command);
    
            CommandBus.Execute<SendMail>(x => { });
    
                        EventBus.Raise<UserIsRegistred>(x => { x.Id = agg.UserAccount.Id; });
        }
    }
    
    
    public class UserIsRegistred : IEvent
    {
        public Guid Id { get; set; }
    }
    
    
    public class AccountAggregate : AggregateBase
    {
        public AccountAggregate(IUnitOfWork uow)
        {
            UnitOfWork = uow;
        }
    
    
        private IUnitOfWork UnitOfWork { get; set; }
    
    
        public UserAccount UserAccount { get; set; }
    
    
        public void AddUserAccount(RegisterTheUser command)
        {
            UserAccount = new UserAccount
            {
                Id = Guid.NewGuid(),
                IsAdmin = false,
                Login = command.Login,
                Password = Crypto.Sha512Encrypt(command.Password)
            };
    
            UnitOfWork.UserAccountRepository.Add(UserAccount);
    
            UnitOfWork.Commit();
        }
    
    
        public Boolean IsLoginAlreadyInUse(String login)
        {
            var result = UnitOfWork.UserAccountRepository.SingleOrDefault(x => x.Login == login);
    
            return (result != null);
        }
    }
    

1 个答案:

答案 0 :(得分:1)

所以有很多问题,但我会接受回答。

  

在命令端,我应该在哪里放置逻辑来调用我的ORM   示例

在命令处理程序或事件处理程序中使用此逻辑对我来说,实际上取决于您正在构建的系统类型。如果你有一个相当简单的系统,你可以在事件处理程序中拥有持久性逻辑,它接收你的域引发的事件。这里的想法是由命令处理程序处理的命令已经具有所需的信息,并且您的命令处理程序最终不仅仅是路由器。如果在命令处理程序中需要更多复杂性,例如处理sagas,长时间运行的事务或一些额外的验证层,那么命令处理程序将在此处使用持久层来提取数据(可能还有写入数据)然后路由命令到正确的域或发出更多命令或引发事件。所以我认为这实际上取决于你所处理的复杂程度。

我倾向于在开始时支持简单性而不是复杂性,并且可能会考虑在事件处理程序中开始使用该逻辑。如果您的系统更复杂,请移至命令处理程序。

  

对于事件存储和撤消思考,我必须保存哪些数据   进入数据库,一些教程保存聚合,一些保存事件   模型

我不确定你在这里问的是什么,但是如果你问的是应该在你的事件存储中存储什么,那么我认为一个简单的解决方案是聚合id,聚合类型,带数据的事件(序列化)和事件类型。在我的头脑中,这可能是您需要的基础:根据您正在使用的聚合ID,获取该聚合的所有事件(按顺序引发),然后重播它们以重建聚合。我不认为你需要保存聚合,除非有一些令人信服的理由(这总是可行的)。

至于你对实际例子的要求以及你所提出的步骤,这可能是一个问题本身,但我对此的看法是:

  • 检查用户名是否已被使用 根据您的应用程序,您可能希望在发出命令之前从控制器的读取端(或任何提升命令的层)执行此操作。此时进行验证,但您可能希望在持久化之前再次进行验证。您可以在事件处理程序中执行此操作,因为您违反了数据库中的唯一索引,因此它可能会捕获异常。

  • 将用户添加到DB 同样,我的想法是保持简单并在事件处理程序中处理它,因为您的域正在引发UserIsRegistered事件。

  • 发送确认电子邮件 您的域可以引发UserIsRegistered事件,第二个事件处理程序(EmailHandler)也会订阅该事件并发送电子邮件。

  • 事件处理程序可以引发ConfirmationMailSent事件,添加到事件队列并进行相应处理。我想我不确定你想在这里发生什么。

但是,希望这有点帮助。