【问题标题】:Who is responsible for the process with API requests in Domain Layer of DDD(Domain Driven Design)DDD(Domain Driven Design)领域层API请求由谁负责处理
【发布时间】:2019-01-19 10:31:08
【问题描述】:

我是 DDD 的初学者。

我不知道谁负责处理 API 请求。
我不知道谁负责间接依赖于基础设施层的功能。
比如带有Web API请求的函数。

例如我正在创建在线购物系统,系统支持取消订单。

我想我在领域层有 2 个类候选来实现订单取消功能。 A:域服务 B:域对象

但我无法判断哪个更好。

我担心设计“A”过于程序化,看起来很乏味。
我还担心设计“B”会使领域对象过于博学和依赖。 (因为我认为对象应该尽可能简单)

设计候选A

// Simple but anemic
class Order {
    id: number;
    date: Date;
    state: OrderState;

    constructor(
        ...
    ) {
        ...
    }
}

// Procedural
class OrderService {
    private readonly orderRepository: OrderRepository;

    constructor(
        orderRepository: OrderRepository
    ) {
        ...
    }

    cancel(orderId: number): void {
        orderRepository.update(orderId, { state: OrderSate.Cancelled });
    }
}

设计候选人 B

// knowledgeable and dependent
class Order {
    id: number;
    date: Date;
    state: OrderState;

    private readonly orderRepository: OrderRepository;

    constructor(
        orderRepository: OrderRepository,
        ...
    ) {
        ...
    }

    cancel(): void {
        orderRepository.update(this.id, { state: OrderSate.Cancelled });
    }
}

The model I thought

【问题讨论】:

    标签: domain-driven-design


    【解决方案1】:

    我是 DDD 的初学者。我不知道谁负责处理 API 请求。

    业务逻辑属于领域模型,编排属于应用。

    因此,您的应用程序中的逻辑通常看起来像...

    Order order = orderRepository.get(orderId);
    order.cancel();
    

    当Order::cancel 被调用时,Order 模型负责知道如何操作它自己的内存数据结构。 OrderRepository 负责持久化。应用程序驱动其他两个元素的调用。

    混淆的部分原因是“服务”这个概念被过度使用了。在 DDD 中,“域服务”是指作为域模型一部分的模式(就像值对象和实体是域模型模式一样)。

    更新 20190120

    但是谁更新服务器中的数据库?如果 order.cancel() 仅更新自己的内存,则您的代码似乎尚未更新服务器中的数据库。这是我的想法。

    这是一个非常重要的问题,不是吗? :)

    当 Eric Evans 在他的书中首次描述存储库模式时,他特意选择了具有集合语义的存储库模式,并建议事务管理应该是一个应用程序关注点。

    在伪代码中:

    beginTransaction();
    Order order = orderRepository.get(orderId);
    order.cancel();
    commitTransaction();
    

    这个概念是底层的 mapper 可以查看存储库并发现哪些实体是“脏的”,从而生成对持久存储的适当调用。

    最近,您更有可能看到具有存储的存储库,而不是集合、语义

    beginTransaction();
    Order order = orderRepository.get(orderId);
    order.cancel();
    orderRepository.save(order);
    commitTransaction();
    

    基本机制是相同的,但我们消除了共享更改方式的一些魔力。

    【讨论】:

    • 感谢您的回答。 > Order 模型负责知道如何操作它自己的内存 是的,我也这么认为。但是谁更新服务器中的数据库?如果 order.cancel() 仅更新自己的内存,则您的代码似乎尚未更新服务器中的数据库。这是我的想法。 i.stack.imgur.com/KSnb4.png
    • 我明白了。换句话说,领域层没有人负责服务器请求,而是应用层负责,不是吗?
    【解决方案2】:

    这些选项还有其他选择。一种是取消存储库的事情,并让模型定义行为:

    public interface Order {
        void cancel();
    }
    

    然后借助特定技术实现该行为:

    public final class SqlOrder implements Order {
        ...
        @Override
        public void cancel() {
            connection.execute("update order...");
        }
    }
    

    根据您的用例,这可能会带来以下好处:不贫血,摆脱可能不必要的间接(存储库),使用领域语言而不是 CRUD 技术,更易于维护,更易于测试,因为订单只是一个现在的界面等等。

    【讨论】:

    • 感谢您的友好回答。你的意思是以下吗? 1.我应该只定义接口作为域对象,并尽可能在基础设施层中实现它。 2、Domain Object的实现允许不仅包含数据对象,还包含功能对象。我对 2 感到奇怪,因为有人告诉我“数据或实体类应该只包含数据对象。例如,student.graduate() 应该是 student.graduate(school)”。也许我的思维方式可能已经过时了。
    • 纯数据对象当然是一种流行的方法,但它不是唯一的方法。正如蓝皮书所建议的那样,我更喜欢使用商业语言(无处不在的语言)。这意味着任何 syntactical 构造都应该是有效的 business 功能(尽可能)。扭转局面:我不应该编写在业务环境中没有意义的代码(例如一系列调用)。功能开发人员会说:如果它编译,它就可以工作。
    • 另外,为了澄清,我不会为 all “业务对象”这样做。对于他们中的一些人来说,这是有道理的,对于其他人来说则不那么有意义。我总是会根据它需要提供的功能来决定我使用什么。
    • 谢谢!我现在有了更深的了解。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-06
    • 2022-10-17
    • 1970-01-01
    • 1970-01-01
    • 2013-07-01
    • 2020-03-20
    相关资源
    最近更新 更多