TTa*_*Tar 7 java oop domain-driven-design
这个问题的核心是一个设计问题.我将使用Java/Java EE示例来说明问题.
考虑一个使用JPA构建的Web邮件应用程序,用于服务层的持久性和EJB.假设我们的EJB中有一个服务方法,如下所示:
public void incomingMail(String destination, Message message) {
Mailbox mb = findMailBox(destination); // who cares how this works
mb.addMessage(message);
}
Run Code Online (Sandbox Code Playgroud)
这似乎是一种合理的商业方法.据推测,邮箱对象仍将被附加,它将无缝地将更改保存回数据库.毕竟,这是透明持久性的承诺.
邮箱对象将具有此方法:
public void addMessage(Message message) {
messages.add(message);
}
Run Code Online (Sandbox Code Playgroud)
这里变得复杂 - 假设我们想要其他邮箱类型.假设我们有一个自动响应发件人的AutoRespondingMailbox,以及一个HelpDeskMailbox,它会在收到每封电子邮件时自动打开一个帮助台票证.
自然要做的是扩展Mailbox,其中AutoRespondingMailbox具有以下方法:
public void addMessage(Message message) {
String response = getAutoResponse();
// do something magic here to send the response automatically
}
Run Code Online (Sandbox Code Playgroud)
问题是我们的Maibox对象及其子类是"域对象"(在本例中,也是JPA实体).Hibernate人(以及许多其他人)宣扬非依赖域模型 - 即不依赖于容器/运行时提供的服务的域模型.这种模型的问题是AutoRespndingMailbox.addMessage()方法无法发送电子邮件,因为它无法访问,例如,JavaMail.
HelpDeskMailbox会出现完全相同的问题,因为它无法访问WebServices或JNDI注入以与HelpDesk系统进行通信.
因此,您不得不将此功能放在服务层中,如下所示:
public void incomingMail(String destination, Message message) {
Mailbox mb = findMailBox(destination); // who cares how this works
if (mb instanceof AutoRespondingMailbox) {
String response = ((AutoRespondingMailbox)mb).getAutoResponse();
// now we can access the container services to send the mail
} else if (mb instanceof HelpDeskMailbox) {
// ...
} else {
mb.addMessage(message);
}
}
Run Code Online (Sandbox Code Playgroud)
必须以这种方式使用instanceof是问题的第一个迹象.每次要子类化邮箱时必须修改此服务类是另一个问题的迹象.
有没有人有关于如何处理这些情况的最佳做法?有人会说Mailbox对象应该可以访问容器服务,这可以通过一些捏造来完成,但它肯定会与JPA的预期用法相悖,因为容器提供除了实体之外的所有依赖注入,清楚地指示这不是一个预期的用例.
那么,我们期望做什么呢?提升我们的服务方法和放弃多态性?我们的对象自动降级为C风格的结构,我们失去了OO的大部分好处.
Hibernate团队会说我们应该在域层和服务层之间拆分业务逻辑,将所有不依赖于容器的逻辑放入域实体中,并将所有依赖于容器的逻辑放入服务层.我可以接受,如果有人可以给我一个如何做到这一点的例子,而不必完全放弃多态性并诉诸于instanceof和其他这样的肮脏
你错过了一些东西:Mailbox对象依赖于运行时提供的接口是完全合理的."不依赖于运行时服务"是正确的,因为您不应该具有编译时依赖性.
由于唯一的依赖是接口,您可以使用像StructureMap,Unity等IoC容器来为对象提供测试实例而不是运行时实例.
最后,AutoRespondingMailbox的代码可能如下所示:
public class AutoRespondingMailbox {
private IEmailSender _sender;
public AutoRespondingMailbox(IEmailSender sender){
_sender = sender;
}
public void addMessage(Message message){
String response = getAutoResponse();
_sender.Send(response);
}
Run Code Online (Sandbox Code Playgroud)
请注意,此类确实取决于某些内容,但它不一定由运行时提供 - 对于单元测试,您可以轻松提供写入控制台的虚拟IEmailSender等.此外,如果您的平台发生更改或需求发生更改,则可以轻松地在构造上提供使用与原始方法不同的方法的不同IEmailSender.这就是"限制依赖"态度的原因.
邮箱就是邮箱...
...但是自动回复邮箱是附加了一些规则的邮箱;这可以说不是邮箱的子类,而是控制一个或多个邮箱和一组规则的 MailAgent。
警告:我对 DDD 的经验有限,但这个例子给我的印象是基于一个错误的假设,例如应用规则的行为属于邮箱。我认为对邮件应用规则与邮箱无关,即收件人邮箱可能只是过滤/路由规则使用的标准之一。因此,在这种情况下,ApplyRules(消息)或ApplyRules(邮箱,消息)服务对我来说更有意义。
归档时间: |
|
查看次数: |
741 次 |
最近记录: |