Seb*_*ber 7 domain-driven-design cqrs event-sourcing
我以StackOverflow为例,因为显然你知道那个网站,而我真正的用例非常接近.
那么让我们想象一下简化的SO域描述:
最后一个大胆的规则是对我很重要的规则.
我对AggregateRoot的理解是它应该包含用于决定接受或拒绝命令的状态,并且它不应该查询DB来执行此操作.它保证了应用程序的一致性.它应该只监听它发出的事件以更新其状态.
现在我认为SO域有一个名为Question的聚合根.然后该问题将处理如下命令:
问题是,当触发EditQuestion时,问题AggregateRoot将如何决定是接受还是拒绝该命令?因为如果你还记得,如果你有<1000声誉,如果你试图编辑另一个用户的问题,那么该命令应该被拒绝.
对于我来说,问题AR保持所有用户声誉的列表,以便能够知道如何对该命令采取行动似乎并不合适.
问题是,当我试图建模我的域时,我有一遍又一遍的这个建模问题,而且我总是最终得到一个大胖子AggregateRoot
有人能告诉我我缺少什么并帮我解决这个问题吗?谢谢
这个问题似乎说我们不应该将授权系统放在域模型中.我同意这对于基于角色的身份验证等问题可能很实用.但是,对我来说,"用户无法编辑,除非他们有足够的声誉"实际上是一个SO业务规则,那么它怎么可能在域外?
重要提示:在回答时,请考虑您是业务专家.您知道StackOverflow是一个用户,可以自己猜出SO约束是什么.即使你对他们的看法不对,也不是什么大不了的事:只是为你错误的业务约束提出建议我很好!
这不是我第一次提出这样的问题,它总是没有答案,只是无休止的讨论.我想知道的是,如果必须构建此站点,您将如何建模StackOverflow,重点关注要编辑的最低信誉的业务规则.
嗯,事情很简单IMO(仅在此SO场景中).这就是我要做的事情(显然,其他开发者可能有不同的方法):
你用"cqrs"很好地标记了这个问题.在EditQuestion处理程序中,我将使用域服务(从CQRS的角度来看的查询),它将检查某个用户是否具有所需的点,然后返回true/false.像这样的东西(或多或少的伪代码)
public class CanUserEditQuestionService
{
//constructor with deps\\
public bool Handle(CanUserEditQuestion input)
{
//query the read model, maybe a query object to get us the rep of the user
var rep=getReputation.Get(input.UserId);
//we can have a dependency here which tell us the number of points required for a specific permission
return(rep>=1000);
}
}
Run Code Online (Sandbox Code Playgroud)
如果查询返回true,那么处理程序将对Question实体执行更改,即question.ChangeText()或smth(我认为SO采用事件采购方法).
你在这里有一个概念"问题"的简单用例,它的命令行为"编辑"和规定谁可以做什么的业务规则.问题是,1000代表规则永远不是问题概念定义的一部分,因此它不属于该聚合,即如何编辑问题.但它是用例本身的一部分,也是应用程序服务的一部分.
我确定你会问我:"如果域查询使用的读模型在命令模型后面怎么办?".在这种情况下,它很重要,延迟可能在几秒钟内测量.此外,主要的是:业务规则不是问题汇总的一部分,因此它不关心立即是否一致.
另一件事是用户代表总是与问题不同的概念,因此处理代表应该永远不是问题聚合的一部分.但它是应用程序服务的一部分.
如果您将应用程序视为一组用例来处理概念(它们本身封装了数据和业务约束),那么很容易识别哪个是应用程序服务,聚合,域服务等.