Viv*_*ndi 2 design-patterns domain-driven-design cqrs
我正在尝试将 CQRS+ES 应用于我的宠物项目。但我不确定如何处理复杂的命令。
假设我有一个网页,您可以在其中创建新的User. 因此,在该页面上,您只需输入名字、姓氏、用户名和密码即可。但是,您还必须Role向该用户添加一个或多个s。当点击“保存”按钮时,将触发以下命令CreateUserWithRolesCommand。
以下是命令处理程序中的有效方法吗?
public class CreateUserWithRolesCommandHandler : ICommandHandler<CreateUserWithRolesCommand>
{
private readonly AppDbContext _context;
public UserCommandHandler(AppDbContext context)
{
_context = context;
}
public void Handle(CreateUserCommand command)
{
// todo: begin db transaction
var user = new User();
user.Username = command.Username;
user.Password = command.Password;
user.Firstname = command.Firstname;
user.Lastname = command.Lastname;
_context.User.Add(user);
_context.Save();
// After save, get user id
van userId = user.Id;
van userRoles = new UserRoles;
// Ommiting foreach loop and just taking the
// first role to keep the example simpler
userRole.RoleId = command.Roles.First().RoleId;
userRole.UserId = userId;
_context.UserRoles.Add(userRole);
_context.Save();
// end db transaction and commit if all successful
}
}
Run Code Online (Sandbox Code Playgroud)
我首先看到的是贫血领域模型。你只有二传手,这是不行的。将所有 setter 替换为命令方法。在这种情况下,您应该只有一个User.create(usename, password, firstName, lastName)返回 void 的方法。
其次,有两个聚合,因此您需要有两个事务。在您的代码中只有一笔交易。请记住,聚合是最大的事务边界。
但是,您会考虑到在第二个事务(角色被添加到用户)之前可能(并且将会)发生一些不好的事情。例如,用户刚刚添加到存储库后,服务器就重新启动或崩溃。重新启动后,它将没有足够的信息来继续添加用户角色的过程。
一个解决方案是将其建模为Saga/Process 管理器。您将拥有一个使用所有所需信息创建的 CreateUserWithRoles 实体。在这种情况下,CreateUserCommand 的内容就足够了。然后,您需要添加一个progress状态变量,即一个枚举(Started、UserCreated 和 RoleAdded),它将 记住上次执行的状态和/或使 User.create 和 UserRoles.add 幂等。创建 CreateUserWithRoles 实体后run即可。该run方法通过查看跳过已执行的步骤progress并执行剩余的步骤。这样,如果发生不好的事情(相信我,它会发生),Saga 就可以恢复。
您还需要一种方法来检测所有处于停止状态的 Sagas 并恢复它们(通过执行它们的run方法)。
PS:我使用术语“事务”来表达这样一个事实:操作必须以原子模式(全部或全部)完成,但可扩展的事件存储实现根本不应该使用数据库事务。
| 归档时间: |
|
| 查看次数: |
2868 次 |
| 最近记录: |