我是一名C#开发人员,但我几乎阅读了有关cqrs的每一个教程,如果语言是Java,无关紧要,因为我想学习cqrs的结构和基础。
但是现在我想,我读了这么多教程的事实是问题,因为教程中存在差异,现在我很困惑,不知道我必须使用哪种技术。
我脑子里有两个主要的问题,也许你们中的一些人可以带来一些清晰度。
在命令方面,我应该在哪里放置逻辑来调用我的ORM? 一些教程在命令处理程序中执行此操作(对我来说更具逻辑性),有些教程在事件处理程序中执行,这将由命令处理程序触发,在这种情况下只执行验证逻辑。
对于事件存储和撤消思考,我需要将哪些数据保存到数据库中,一些教程会保存聚合,一些教程会保存事件模型。
我希望有人可以解释我使用什么模式以及为什么,也许在不同情况下,我不知道。
一个实际的例子会很棒。 (只有伪代码)
也许是用户注册,RegisterTheUser命令:
要做的事情:
亲切的问候
编辑: 这是我目前的实现(简单)
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);
}
}
答案 0 :(得分:1)
所以有很多问题,但我会接受回答。
在命令端,我应该在哪里放置逻辑来调用我的ORM 示例
在命令处理程序或事件处理程序中使用此逻辑对我来说,实际上取决于您正在构建的系统类型。如果你有一个相当简单的系统,你可以在事件处理程序中拥有持久性逻辑,它接收你的域引发的事件。这里的想法是由命令处理程序处理的命令已经具有所需的信息,并且您的命令处理程序最终不仅仅是路由器。如果在命令处理程序中需要更多复杂性,例如处理sagas,长时间运行的事务或一些额外的验证层,那么命令处理程序将在此处使用持久层来提取数据(可能还有写入数据)然后路由命令到正确的域或发出更多命令或引发事件。所以我认为这实际上取决于你所处理的复杂程度。
我倾向于在开始时支持简单性而不是复杂性,并且可能会考虑在事件处理程序中开始使用该逻辑。如果您的系统更复杂,请移至命令处理程序。
对于事件存储和撤消思考,我必须保存哪些数据 进入数据库,一些教程保存聚合,一些保存事件 模型
我不确定你在这里问的是什么,但是如果你问的是应该在你的事件存储中存储什么,那么我认为一个简单的解决方案是聚合id,聚合类型,带数据的事件(序列化)和事件类型。在我的头脑中,这可能是您需要的基础:根据您正在使用的聚合ID,获取该聚合的所有事件(按顺序引发),然后重播它们以重建聚合。我不认为你需要保存聚合,除非有一些令人信服的理由(这总是可行的)。
至于你对实际例子的要求以及你所提出的步骤,这可能是一个问题本身,但我对此的看法是:
检查用户名是否已被使用 根据您的应用程序,您可能希望在发出命令之前从控制器的读取端(或任何提升命令的层)执行此操作。此时进行验证,但您可能希望在持久化之前再次进行验证。您可以在事件处理程序中执行此操作,因为您违反了数据库中的唯一索引,因此它可能会捕获异常。
将用户添加到DB 同样,我的想法是保持简单并在事件处理程序中处理它,因为您的域正在引发UserIsRegistered事件。
发送确认电子邮件 您的域可以引发UserIsRegistered事件,第二个事件处理程序(EmailHandler)也会订阅该事件并发送电子邮件。
事件处理程序可以引发ConfirmationMailSent事件,添加到事件队列并进行相应处理。我想我不确定你想在这里发生什么。
但是,希望这有点帮助。