首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >域服务与工厂与聚合根之争

域服务与工厂与聚合根之争
EN

Software Engineering用户
提问于 2014-05-05 07:44:38
回答 2查看 11.9K关注 0票数 13

在处理DDD几个月后,我仍然对域服务、工厂和聚合根的一般用途感到困惑,例如它们的职责重叠的地方。

示例:我需要1)在一个saga (流程管理器)中创建一个复杂的域实体,然后是一个需要在其他地方处理的特定域事件,而3)该实体显然是一个聚合根,它标记了其他实体的有界上下文。

  1. 工厂负责创建实体/聚合根。
  2. 服务可以创建实体,因为他还抛出域事件。
  3. 服务可以充当聚合根(使用ID 4创建“实体”的“子实体”)
  4. 聚合根可以创建和管理“子实体”。

当我将聚合根和工厂的概念引入到我的域中时,服务似乎不再需要了。但是,如果我不是,服务也可以用他所拥有的知识和依赖来处理所需的一切。

基于普适语言的汽车修理厂代码示例

代码语言:javascript
复制
public class Car : AggregateRoot {

    private readonly IWheelRepository _wheels;
    private readonly IMessageBus _messageBus;

    public void AddWheel(Wheel wheel) {
        _wheels.Add(wheel);
        _messageBus.Raise(new WheelAddedEvent());
    }

}

public static class CarFactory {

    public static Car CreateCar(string model, int amountofWheels);

}

..or...

代码语言:javascript
复制
public class Car {

    public ICollection<Wheel> Wheels { get; set; }

}

public interface ICarService {

    Car CreateCar(args);
    void DeleteCar(args);
    Car AddWheel(int carId, Wheel wheel);

}
EN

回答 2

Software Engineering用户

发布于 2016-01-12 01:23:20

在处理DDD几个月后,我仍然对域服务、工厂和聚合根的一般用途感到困惑,例如它们的职责重叠的地方。

聚合根负责确保状态与业务不变量一致。在CQRS术语中,您可以通过向聚合根发出命令来更改模型。

域服务是聚合的查询支持机制。例如,域服务可能支持计算。命令处理程序将域服务传递给聚合,聚合(处理命令)需要计算结果,因此它将将其状态中作为计算输入的部分传递给域服务。域服务进行计算并返回结果,然后聚合将决定如何处理结果。

“工厂”可能意味着几件事。如果您使用工厂来创建一个位于聚合边界内的实体,那么它只是一个实现细节--如果对象的构造很复杂,您可以使用它。

在某些情况下,使用“储存”一词。这通常意味着它是负责创建聚合根的持久化层(而不是域模型的一部分)的一部分。粗略地说,命令处理程序(这是应用程序的一部分)验证一个命令,然后使用Repository加载命令寻址到的聚合根。然后,命令处理程序将在聚合根上调用指定的方法,从命令对象传入参数,也可能传入域服务作为参数。

在您的示例中,要问的关键问题是,决定是否运行命令的责任在哪里?应用程序负责确保命令格式良好(所有命令数据都存在,数据被转换为域识别的值,而不抛出验证错误等等)。但是谁能决定“不,你现在不能加一个轮子--商业规则不允许它!”

在DDD世界中,这无疑是总根的责任。因此,确定这一点的任何逻辑都应该位于聚合根目录中,而ICarService就会消失。

(在替代实现中,聚合公开其状态,而cars服务检查业务规则并操作对象的状态,被视为反模式--一个“贫血”聚合的例子。集合中的“设置者”是一种代码气味。聚合中的"Getters“通常是一种代码气味--尤其是在CQRS中,在读取模型中,支持查询的责任应该是”其他地方“)。

票数 11
EN

Software Engineering用户

发布于 2014-05-05 08:15:00

DDD在一定程度上是对贫血域模型的反应,在该模型中,实体只具有状态,而不具有行为。

的确,从某种意义上说,你可以把汽车的所有行为都放在一个单独的服务中,但你为什么要这样做呢?要做到这一点,您需要在汽车中公开各种状态,通常您希望保持私有状态(比如车轮)。

如果你像那样暴露轮子,任何代码都可以对这个集合做各种古怪的事情,除了任何正常的业务流程之外。请记住,聚合的意义在于在业务流程事务之间具有事务一致性。像那样暴露车轮完全破坏了安全。

例如:假设你的公司只想支持四个轮子的汽车。如果封装了对车轮的访问,则可以强制执行。如果你不这样做,这是完全有可能增加100个车轮的汽车,因为车轮将暴露。

服务通常用作协调器。命令处理程序(假设类似于CQRS)将命令对象转换为更强类型的参数,从而减少了原始的痴迷。然后,它可以使用更多的“域-ish”参数调用服务。服务从存储库中检索实体并调用它们的行为。根据您的体系结构,它可以收集任何更改(事件),并将它们传递给总线或其他任何东西。

在更高级的场景中,您将使用一个工作单元来跟踪所有实体,并可以一次性收集所有实体的更改。

至于工厂(作为旁白),没有什么可以阻止您添加静态工厂方法(您的聚合类),而不是创建一个单独的工厂。

票数 9
EN
页面原文内容由Software Engineering提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://softwareengineering.stackexchange.com/questions/237943

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档