1

我是一名 C# 开发人员,但我几乎阅读了所有关于 cqrs 的教程,不管语言是否是 Java,因为我想学习 cqrs 的结构和基础。

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

两个主要问题在我的脑海中肆虐,也许你们中的一些人可以在那里澄清一些问题。

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

  2. 对于事件存储和撤消认为,我必须将哪些数据保存到数据库中,一些教程保存聚合,一些保存事件模型。

我希望有人可以解释我要使用什么模式以及为什么,也许两者都在不同的场景中,我不知道。

一个实际的例子会很棒。(仅伪代码)
可能是用户注册,RegisterTheUser 命令:

要做的事情:

  • 检查用户名是否已被使用
  • 将用户添加到数据库
  • 发送确认邮件(在命令中还是在 UserIsRegistered 事件中?)
  • 触发事件 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);
    }
}
4

1 回答 1

1

所以有很多问题,但我会尽力回答。

在命令方面,例如,我应该在哪里放置调用我的 ORM 的逻辑?

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

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

对于事件存储和撤消认为,我必须将哪些数据保存到数据库中,一些教程保存聚合,一些保存事件模型

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

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

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

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

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

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

但是,希望这会有所帮助。

于 2013-06-27T13:57:14.310 回答