Oop 如何优雅地避免使用DDD的域实体对基础设施服务的依赖? 背景
假设我的任务是使用域驱动设计(DDD)在通知发送领域构建一个系统。该系统的关键要求之一是需要支持各种“类型”的通知,如短信、电子邮件等 在开发域模型的几次迭代之后,我继续将Oop 如何优雅地避免使用DDD的域实体对基础设施服务的依赖? 背景,oop,domain-driven-design,Oop,Domain Driven Design,假设我的任务是使用域驱动设计(DDD)在通知发送领域构建一个系统。该系统的关键要求之一是需要支持各种“类型”的通知,如短信、电子邮件等 在开发域模型的几次迭代之后,我继续将通知基类作为一个实体,子类SMSNotification,EmailNotification等作为子类(每个都是一个实体) 通知 公共抽象类通知扩展实体{ //…字段。。。 公共摘要void send(); } SMSNotification 公共类SMSNotification扩展通知{ 公共无效发送(){ //使用基础设
通知
基类作为一个实体,子类SMSNotification
,EmailNotification
等作为子类(每个都是一个实体)
通知
公共抽象类通知扩展实体{
//…字段。。。
公共摘要void send();
}
SMSNotification
公共类SMSNotification扩展通知{
公共无效发送(){
//使用基础设施服务发送SMS通知的逻辑。
}
}
电子邮件通知
公共类EmailNotification扩展了通知{
公共无效发送(){
//使用基础结构服务发送电子邮件通知的逻辑。
}
}
问题
- 使用这种当前的设计方法,
的每个子类都与基础设施服务交互,其中基础设施的任务是与一些外部系统接口李>通知
域服务的概念时,专门为这一点留出了一小页空间:
,在大多数开发系统中,在域对象和外部资源之间建立直接接口是很困难的。我们可以用一个门面来装饰这样的外部服务,该门面接受模型方面的输入。。。但无论我们有什么中介,即使他们不属于我们,这些服务都在履行领域责任
- 相反,如果我在我的域模型中使用Evans的建议而不是在
通知的每个子类上使用send
方法获得SendNotificationService
,我不确定如何避免需要知道提供了什么类型的通知,以便采取适当的基础设施行动:
SendNotificationService
(域服务)
公共类SendNotificationService{
公共无效发送(通知){
//如果通知是SMS通知。。。
//利用基础设施服务发送短信。
//如果通知是电子邮件通知。。。
//利用基础设施服务发送电子邮件。
//
//(╯°□°)╯︵ ┻━┻)
}
}
我错过了什么?
- 面向对象的设计原则促使我首先推荐模型,包括
Notification
、SMSNotification
和EmailNotification
类。在Notification
的每个子类上实现send
方法是有意义的,因为所有通知都需要发送(证明其在通知
中的位置正确)并且通知
的每个“类型”或子类在通知的发送方式上都有专门的行为(证明在通知
中使发送
抽象是正确的)。这种方法也尊重开放/关闭原则(OCP),因为通知
类将不允许修改,并且由于支持新的通知类型,可以创建通知
的新子类来扩展功能。无论如何,对于不让实体与外部服务接口以及不让ent的子类存在共识DDD中的所有信息。
- 如果将发送通知的行为从
通知中删除,则其放置位置必须了解通知的“类型”,并相应地采取行动,我只能将其概念化为If…else…
语句链,这与OCP直接矛盾
在开发域模型的几次迭代之后,我继续将通知基类作为一个实体,子类SMSNotification、EmailNotification等作为子类
这可能是个错误
public abstract class Notification extends Entity<UUID> {
public abstract void send();
}
SMSNotification支持与SMSNotificationService提供商的协作,我们明确了这一点
您在这里提供的接口看起来更像。如果您想让它工作,您通常会在构造函数中连接特定的实现
您可以使用泛型做一些事情(取决于您的语言选择),使这些不同服务之间的相似性更加明显
public abstract class Notification<SERVICE> extends Entity<UUID> {
public abstract void send(SERVICE service);
}
public class SMSNotification extends Notification<SMSNotificationService> {
public void send(SMSNotificationService service){
//logic for sending the SMS notification using an infrastructure service.
}
}
public class NotificationCommand<SERVICE> {
private final SERVICE service;
private final Notification<SERVICE> notification;
public final send() {
notification.send(service);
}
}
同样,一个很好的俱乐部应该放在袋子里,但不适合这个用例——您可以这样做,但是突然之间,许多额外的组件需要了解通知服务,以便将它们放到需要的地方
notification.send(服务)和service.send(通知)之间的最佳选择
大概
notification.send(service)
使用“告诉,不要问”作为理由。您将协作者传递给域实体,它决定(a)是否协作,(b)传递给域服务的状态,以及(c)如何处理返回的任何状态
SMSNotification::send(SMSNotificationService service {
State currentState = this.getCurrentState();
{
Message m = computeMessageFrom(currentState);
service.sendMessage(m);
}
}
在边界上,应用程序不是;我怀疑,当我们从域的核心向域移动时,我们看到实体让位给值,让位给更原始的表示
在阅读了一些关于纯领域模型的内容,以及其中不应该有任何IO的事实之后,我不再确定了
事实上,这有点纠结。域服务的动机之一是将域模型与IO分离——所有IO关注点都由域服务实现(o
public abstract class Notification extends Entity<UUID> {
public abstract void send(SMSNotificationService sms, EmailNotificationService email, ....);
}
SMSNotificationFactory {
private final SMSNotificationService sms;
SMSNotification create(...) {
return new SMSNotification(sms, ...);
}
}
notification.send(service)
SMSNotification::send(SMSNotificationService service {
State currentState = this.getCurrentState();
{
Message m = computeMessageFrom(currentState);
service.sendMessage(m);
}
}
List<SMSRequest> messages = domainEntity.getMessages();
List<SMSResult> results = sms.send(messages)
domainEntity.onSMS(results)