Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Oop 如何优雅地避免使用DDD的域实体对基础设施服务的依赖? 背景_Oop_Domain Driven Design - Fatal编程技术网

Oop 如何优雅地避免使用DDD的域实体对基础设施服务的依赖? 背景

Oop 如何优雅地避免使用DDD的域实体对基础设施服务的依赖? 背景,oop,domain-driven-design,Oop,Domain Driven Design,假设我的任务是使用域驱动设计(DDD)在通知发送领域构建一个系统。该系统的关键要求之一是需要支持各种“类型”的通知,如短信、电子邮件等 在开发域模型的几次迭代之后,我继续将通知基类作为一个实体,子类SMSNotification,EmailNotification等作为子类(每个都是一个实体) 通知 公共抽象类通知扩展实体{ //…字段。。。 公共摘要void send(); } SMSNotification 公共类SMSNotification扩展通知{ 公共无效发送(){ //使用基础设

假设我的任务是使用域驱动设计(DDD)在通知发送领域构建一个系统。该系统的关键要求之一是需要支持各种“类型”的通知,如短信、电子邮件等

在开发域模型的几次迭代之后,我继续将
通知
基类作为一个实体,子类
SMSNotification
EmailNotification
等作为子类(每个都是一个实体)

通知

公共抽象类通知扩展实体{
//…字段。。。
公共摘要void send();
}
SMSNotification

公共类SMSNotification扩展通知{
公共无效发送(){
//使用基础设施服务发送SMS通知的逻辑。
}
}
电子邮件通知

公共类EmailNotification扩展了通知{
公共无效发送(){
//使用基础结构服务发送电子邮件通知的逻辑。
}
}
问题
  • 使用这种当前的设计方法,
    通知
    的每个子类都与基础设施服务交互,其中基础设施的任务是与一些外部系统接口
Eric Evans在其著作《域驱动设计》第107页中,在介绍
域服务的概念时,专门为这一点留出了一小页空间:

,在大多数开发系统中,在域对象和外部资源之间建立直接接口是很困难的。我们可以用一个门面来装饰这样的外部服务,该门面接受模型方面的输入。。。但无论我们有什么中介,即使他们不属于我们,这些服务都在履行领域责任

  • 相反,如果我在我的域模型中使用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)