在Rich与Anemic领域模型的辩论中,互联网充满了哲学上的建议,但缺乏权威的例子。这个问题的目的是找到适当的领域驱动设计模型的明确指南和具体示例。(理想情况下为C#。)
对于一个实际示例,这种DDD实现似乎是错误的:
下面的WorkItem域模型不过是属性包,由Entity Framework用于代码优先数据库。按照福勒的说法,这是贫血的。
WorkItemService层显然是对域服务的常见误解。它包含WorkItem的所有行为/业务逻辑。Per Yemelyanov等人认为,这是程序性的。(第6页)
因此,如果以下内容是错误的,我该怎么做呢?
该行为,即AddStatusUpdate或Checkout,应该属于WorkItem类吗?
WorkItem模型应具有哪些依赖关系?
public class WorkItemService : IWorkItemService {
private IUnitOfWorkFactory _unitOfWorkFactory;
//using Unity for dependency injection
public WorkItemService(IUnitOfWorkFactory unitOfWorkFactory) {
_unitOfWorkFactory = unitOfWorkFactory;
}
public void AddStatusUpdate(int workItemId, int statusId) {
using (var unitOfWork = _unitOfWorkFactory.GetUnitOfWork<IWorkItemUnitOfWork>()) {
var workItemRepo = unitOfWork.WorkItemRepository;
var workItemStatusRepo = unitOfWork.WorkItemStatusRepository;
var workItem = workItemRepo.Read(wi => wi.Id == workItemId).FirstOrDefault();
if (workItem == null)
throw new ArgumentException(string.Format(@"The provided WorkItem Id '{0}' is not recognized", workItemId), "workItemId");
var status = workItemStatusRepo.Read(s => s.Id == statusId).FirstOrDefault();
if (status == null)
throw new ArgumentException(string.Format(@"The provided Status Id '{0}' is not recognized", statusId), "statusId");
workItem.StatusHistory.Add(status);
workItemRepo.Update(workItem);
unitOfWork.Save();
}
}
}
(此示例已简化为更易于阅读。代码肯定仍然很笨拙,因为这是一个令人困惑的尝试,但是域行为是:通过将新状态添加到存档历史记录来更新状态。最终我同意其他答案,这只能由CRUD处理。)
更新资料
@AlexeyZimarev给出了最佳答案,这是Jimmy Bogard用C#编写的关于C#的完美视频,但由于在链接之外没有提供足够的信息,因此显然在下面进入了注释。我在下面的回答中对笔记进行了粗略的草稿,总结了视频。请随时对答案进行任何更正,以发表评论。该视频长达一个小时,但非常值得观看。
更新-2年后
我认为这是DDD刚刚成熟的标志,即使研究了2年,我仍然不能保证我知道这样做的“正确方法”。无处不在的语言,聚合的根源以及其行为驱动设计的方法是DDD对行业的宝贵贡献。持续性的无知和事件源导致混乱,我认为这样的哲学使它无法被广泛采用。但是,如果我不得不以我所学到的方式再次编写此代码,我认为它将看起来像这样:
我仍然欢迎这篇(非常活跃的)帖子提供任何答案,这些答案提供了有效域模型的所有最佳实践代码。
"I don't want to duplicate all my entities into DTOs simply because I don't need it and it violates DRY, and I also don't want my client application to take a dependency on EntityFramework.dll"
。实体框架术语中的“实体”与“域模型”中的“实体”不同