假设我有一个复杂的系统,那里有大量的人。简单的想法是员工/经理的关系,许多员工向一位经理报告。现在,除了经理之外,还有能够代表经理行事的支持人员,他们可以操纵经理的员工。
在CQRS系统中,如果操作的调用方是支持人员,您将如何为"编辑员工"的假设操作建模消息。只有根据经理安全关系的员工在其领域内对员工采取行动,该行动才能成功。
验证其安全性需要查询数据库,以验证被修改的人是否确实在该经理的员工链中。
这个查询将在哪里发生?在发出"编辑员工"消息之前?
如果在发出消息之前预先验证了数据,那么在最终一致的系统中,假设在处理"编辑员工"消息之前,发生了一个单独的操作,该操作将删除用户完成"编辑员工"操作的权限。如果命令处理程序不验证该消息的安全问题,即使用户不再有权执行该消息,该消息仍然会成功
这似乎意味着双面验证,类似于UI验证&服务器端验证将是最好的做法。然而,完成验证的方法似乎违反了CQRS的关键原则。
在使用CQRS时,当必须处理这些和其他类似的交叉问题时,什么方法是最好的?
首先,我同意@Yahia的评论,即没有一个通用的答案。话虽如此,以下是我的做法。
首先,我可能会进行双重验证——第一次收到请求时在控制器中进行一次验证,然后在稍后处理命令时在域中进行验证。有些人可能不同意这一点,但我宁愿阻止发出命令,并立即让用户知道他们没有被授权执行某些操作,而不是让命令通过,并依靠最终的一致性发出一些错误通知,在用户无法执行操作后提醒用户。
因此,就伪代码而言,以下是我编辑员工的方法:
控制器
[HttpPost]
ActionResult Edit(Employee emp){
//get employee org information from _employeeRepository
//validate if _loggedInUserID is able to edit emp.ID
if(isValid) {
//construct command
_commandService.EnqueueCommand(new EditEmployee(emp.ID, emp.Name, emp.Salary));
} else {
return View("PermissionError");
}
return Redirect("EmployeeProperties");
}
因此,在这里,我的命令服务接收命令并将其路由到我的域中的适当AR,即Employee。
员工域
protected void EditEmployee(userID, employeeID, employeeName, salary){
//get employee org information from _employeeRepository
//validate if userID is able to edit employeeID
if(isValid) {
//apply event
ApplyEvent(new EmployeeEdited(userID, employeeID, employeeName, salary));
}
}
所以我会在我的控制器和域中应用相同的安全检查。我可能会把它作为一个封装的方法(好吧,可能是一个封装好的标准类,我会把它传递给存储库)。
所以我希望这有助于我如何处理这种情况。如果有问题请告诉我,我会在回答中详细说明。
我希望这能有所帮助。祝你好运
对于这个域,我可能会完全跳过CQRS,让web层直接与DB层对话(没有消息)。简单的乐观并发应该处理可能发生的少数冲突。