我正在创建一个试图避免Fat Controller气味的ASP.NET MVC应用程序。我这样做是通过使控制器方法简单地将轻量级命令发送到命令总线,然后由命令处理程序拾取。命令处理程序在域模型上执行命令,然后创建持久化的状态更改事件。
我这样做是为了尝试远离CRUD模型“从存储库中获取X,更改并将其放回”,从Web应用程序中删除所有特定于域的知识并允许用户的意图直接传达给域模型。
所以,假设一个Contact聚合的组成如下(为简洁起见,我省略了除了一个setter方法之外的所有方法)。
public class Contact {
private Address _homeAddress;
public Address HomeAddress {
get { return _homeAddress; }
set {
if(newHomeAddress.Equals(_homeAddress)) return;
_homeAddress = newHomeAddress;
AddEvent(new HomeAddressChanged(Id, _homeAddress));
}
}
public Address WorkAddress { get; set; }
public PhoneNumber PhoneNumber { get; set; }
public EmailAddress EmailAddress { get; set; }
}
执行HomeAddress更改的命令处理程序将如此。
public class ChangeHomeAddressCommandHandler : IHandleCommand<ChangeHomeAddressCommand>
{
private IRepository<Contact> _repo;
public ChangeHomeAddressCommandHandler(IRepository<Contact> repo)
{
_repo = repo;
}
public void Execute(ChangeHomeAddressCommand command)
{
var toEdit = _repo.One(command.Id);
toEdit.HomeAddress = command.NewHomeAddress;
_repo.CommitChanges(toEdit);
}
}
我的麻烦在于,用户提交的表单需要允许编辑整个联系人,即其所有相关地址,电话号码和c),这意味着需要为每个人提供命令和处理程序财产状况的变化。
这些处理程序中的每一个都需要加载聚合,进行更改然后提交更改。因此,即使您不更改所有属性,命令处理程序仍然需要加载和构建Contact聚合四次,这是不必要的昂贵。
我考虑了一些选择......
可以添加每个可能子命令(即单个ChangeHomeAddressCommand)的实例的“宏”命令(可称为EditContactCommand)。宏命令加载聚合并将其传递给子命令并在dispose上提交更改。
使用户界面更加“专注于任务”。编辑页面不是用于收集输入的文本框的结构化集合,而是使用带有“更改”按钮的标签来调用模式对话框。当模态对话框没问题时,将一个AJAX帖子发送回控制器,控制器又转发一个命令。或者实际上,构建较小的页面,只暴露Contact聚合的某些方面。您只需要更改实际更改的内容,并且可以在没有大的“保存”样式提交的情况下进行更改。 (我不确定用户是否会穿这个,因为他们似乎喜欢他们的文本框海洋!)
我会感激任何建议,经验和智慧。感谢。
答案 0 :(得分:0)
问题可能在于您正在努力解开一个应用程序(据我们从这个小代码中可以看出)本质上非常粗糙。
无论你如何弯曲你的命令使它们看起来不像CRUD,如果他们不描述领域现实,他们就没有任何意义 - 它只会增加更多不必要的复杂性。如果电子邮件地址触发了重新发送验证电子邮件等的整个过程,那么更改电子邮件地址可能是一个独立的命令,但如果它只是修改了电子邮件字段则不是。
我认为修改整个实体的命令没有任何问题,只要它们是与您的域专家一起探索的有效域操作/事件,并且不是100%。应用程序很少是纯粹的CRUD,但是当它们存在时,DDD肯定不是最好的选择方法。
答案 1 :(得分:0)
你可能已经在角落里画自己了。我错过了用户的意图。为什么家庭住址被更改?用户是否输错了或者联系人确实移动了?如果是后者,您可能需要发送电子邮件 - 如果是前者,可能不是。
让场景驱动您发现用户的意图。