领域驱动设计和工厂类的作用

Posted

技术标签:

【中文标题】领域驱动设计和工厂类的作用【英文标题】:Domain Driven Design and the role of the factory class 【发布时间】:2010-10-08 00:02:17 【问题描述】:

我不清楚工厂类的角色和职责是什么。我知道工厂类应该负责创建域对象(聚合根)及其关联的实体和值对象。

但我不清楚工厂“层”在 DDD 架构中的位置?工厂是否应该直接调用存储库以获取其数据或服务库?

工厂在哪里适合以下框架: UI > 应用程序 > 域 > 服务 > 数据

另外,因为工厂是唯一允许创建对象的地方,如果您想在数据和服务层中创建对象,您不会获得循环引用吗?

如果工厂类的作用是创建对象,那么服务层有什么好处呢?

我已经问了很多问题,并感谢任何回复。我缺少的是一个示例应用程序,它演示了域驱动设计项目中的所有层如何组合在一起......那里有什么吗?

【问题讨论】:

就示例而言,最近的dddsample.sourceforge.net 是我找到的最佳示例。非常完整。 【参考方案1】:

我看到应用服务层负责通过调用工厂来创建新实体。因此,实体工厂接口定义在 Application Service 模块中,实际实现是一个基础设施服务,与持久化一样。对于通用应用程序服务与域服务与基础设施服务的一个很好的清晰概念,我通过观看以下内容获得了很多清晰:Bob Martin Ruby Midwest 2011

【讨论】:

【参考方案2】:

存储库和工厂的区别在于,存储库代表抽象的持久存储,而工厂负责构建对象。

例如,假设我正在注册一个用户。我将从工厂获取我的用户对象

IUser user = userFactory.Create(name, email);

然后传递给repository,由repository负责处理。

userRepository.Insert(user);

DDD 中的工厂可以被认为是一种隐藏新的方法,一种对实例化细节的抽象。它允许您非常有效地编程到接口而不是具体的。

此外,这允许存储库专注于其实体类型,因此泛型的使用变得非常强大。

【讨论】:

【参考方案3】:

除非我真的陷入困境,否则我倾向于不强调设计模式,这可能是我自己的危险。我只是按照我的想法构建系统并重构,直到第二天它变得有意义。

我在以下情况下使用工厂:

某些层需要创建一个有规律的对象。 只有该层知道何时创建它,或者使用什么自定义 只有其他层知道要创建什么。

【讨论】:

我同意这可能是最合乎逻辑和“最有可能导致完成项目”的方法。随着时间的推移,让这些模式自己出现,只要确保有识别它们的能力..我自己也在学习这个.. 我想很多人都经历过设计模式的金锤时期...... :-) 就像你说的,你通常会有机地接触到一个已知的设计模式。但是了解和应用它们的一个很好的理由是,它还使其他开发人员能够快速掌握被引入代码库时发生的事情。如果您有一个工厂类,则无需搜索对象的每个不同实例,这对于没有经验的程序员来说是一个明确的点。【参考方案4】:

但我不清楚的是在哪里 工厂“层”位于 DDD 建筑学?工厂应该 直接调用仓库 获取其数据或服务 图书馆?

工厂应该是构建领域对象的一站式商店。需要执行此操作的代码的任何其他部分都应使用工厂。

通常,至少有三个数据源用作域对象构造的工厂的输入:来自 UI 的输入、来自持久性的查询结果和对域有意义的请求。因此,要回答您的具体问题,存储库将使用工厂。

这是一个例子。我在这里使用Holub's Builder pattern。 编辑:忽略此模式的使用。我已经开始意识到它doesn't mix too well with DDD factories。

// domain layer
class Order

    private Integer ID;
    private Customer owner;
    private List<Product> ordered;

    // can't be null, needs complicated rules to initialize
    private Product featured; 

    // can't be null, needs complicated rules to initialize, not part of Order aggregate
    private Itinerary schedule; 

    void importFrom(Importer importer)  ... 

    void exportTo(Exporter exporter)  ... 

    ... insert business logic methods here ...

    interface Importer
    
        Integer importID();
        Customer importOwner();
        Product importOrdered();
    

    interface Exporter
    
        void exportID(Integer id);
        void exportOwner(Customer owner);
        void exportOrdered(Product ordered);
    


// domain layer
interface OrderEntryScreenExport  ... 

// UI
class UIScreen

    public UIScreen(OrderEntryDTO dto)  ... 


// App Layer
class OrderEntryDTO implements OrderEntryScreenExport  ... 

这是 OrderFactory 的样子:

interface OrderFactory

    Order createWith(Customer owner, Product ordered);
    Order createFrom(OrderEntryScreenExport to);
    Order createFrom(List<String> resultSets);

特色产品的逻辑和行程的生成都在 OrderFactory 中。

下面是工厂在每个实例中的使用方式。

在订单存储库中:

public List<Order> findAllMatching(Criteria someCriteria)

    ResultSet rcds = this.db.execFindOrdersQueryWith(someCriteria.toString());
    List<List<String>> results = convertToStringList(rcds);

    List<Order> returnList = new ArrayList<Order>();

    for(List<String> row : results)
        returnList.add(this.orderFactory.createFrom(row));

    return returnList;

在你的应用层:

public void submitOrder(OrderEntryDTO dto)

    Order toBeSubmitted = this.orderFactory.createFrom(dto);

    this.orderRepo.add(toBeSubmitted);

    // do other stuff, raise events, etc

在您的领域层中,可能会进行单元测试:

Customer carl = customerRepo.findByName("Carl");
List<Product> weapons = productRepo.findAllByName("Ruger P-95 9mm");
Order weaponsForCarl = orderFactory.createWith(carl, weapons);

weaponsForCarl.place();

assertTrue(weaponsForCarl.isPlaced());
assertTrue(weaponsForCarl.hasSpecialShippingNeeds());

工厂在哪里适合 以下框架:UI > App > Domain > Service > Data

域。

另外,因为工厂是唯一的 允许创建对象的地方 你不会得到循环引用吗 如果你想创建你的对象 在您的数据和服务层中?

在我的示例中,所有依赖项都是从上到下流动的。我使用Dependency Inversion Principle(PDF 链接)来避免您所说的问题。

如果工厂类的角色是 对于对象创建,有什么好处 服务层有吗?

如果您的逻辑不适合任何单个域对象,或者您的算法涉及编排多个域对象,请使用服务。该服务将封装任何不适合其他任何事物的逻辑,并将其委托给适合的领域对象。

在我在这里草草写下的示例中,我想为订单制定行程将涉及多个域对象。 OrderFactory 可以委托给这样的服务。

顺便说一句,您描述的层次结构可能应该是 UI > App > Domain Services > Domain > Infrastructure (Data)

我问了很多问题 感谢任何回应。我是什么 缺少的是一个示例应用程序 演示如何在一个 领域驱动设计项目来了 在一起……有什么事情吗 那里?

Jimmy Nilsson 的Applying Domain Driven Design and Patterns 是对 Eric Evans 的Domain-Driven Design 的极大赞美。它有很多代码示例,虽然我不知道是否强调分层。分层可能很棘手,几乎是一个与 DDD 分开的主题。

在 Evans 的书中,有一个非常小的分层示例,您可能想看看。分层是一种企业模式,Martin Fowler 写了Patterns of Enterprise Application Architecture,您可能会发现它也很有用。

【讨论】:

超级超级答案。帮了我很多。谢谢 我通常会拆分从构建器创建新对象的工厂(从持久性中重新水化实体),这样持久性问题就不会出现在工厂的界面中。 很好的答案,但我有一个问题。在应用层中使用工厂(从 DTO 创建对象)的情况下,工厂如何在域层中?在我看来,DTO 对应于一个用例,并用于 UI/应用程序层。我目前正在处理应用程序层,正是这个问题让我写了这篇文章:-) Domain 对象能否在其实现中引用Repository?例如,Order 可以使用OrderRepositoryInterface 检索订单信息吗?假设我们有一个 Orders 集合,其中 getThoseOfPersonWithName("David") 方法返回一个 List&lt;Order&gt;,其中所有者的名称是 David。这个Orders 集合是否可以在其getThoseOfPersonWithName(String name) 方法中引用OrderRepository 的实例并使用例如criteria = new Criteria(); criteria.add("name", name); return this.orderRepo.findAllMatching(criteria); ?或者像这样的事情在 DDD 中被认为是发誓吗?谢谢。【参考方案5】:

工厂应该直接调用存储库以获取其数据还是服务库?

我不会说,如果可能的话,它应该直接传递它需要的信息。

工厂在哪里适合 以下框架:UI > App > Domain > Service > Data

不确定这种分层是从哪里来的,分层在 DDD 中不是固定的,但我会说你最好专注于这种风格

UI > 应用 > 域

在域中,您拥有多种类型的对象,我会为它们之间的关系设置规则:

应该向工厂传递他们完成工作所需的一切,因此在大多数情况下,我不会让他们调用其他服务或存储库。 在大多数情况下,实体不应联系存储库,而是应由服务(或其他上层)负责这项工作。 实体不应调用服务,服务位于实体/值对象/规范之上并酌情协调它们。 域内的服务用于协调,它们不包含重要的域/业务行为。

如果工厂类的作用是创建对象,那么服务层有什么好处呢?

Eric 在书中很好地解释了这一点,所以我会参考它,但如果你有交叉聚合行为或不能很好地融入一个聚合的行为(例如书中的帐户示例),它最终会很好。

【讨论】:

“工厂应该传递他们工作所需的一切,因此在大多数情况下我不会让他们调用其他服务或存储库。” - 如果我有一个名为“public Order GetOrderById(int id)”的工厂方法,那么 impl 代码中会包含什么?它必须调用存储库? Th3Fix3r - 我的理解是您的 GetOrderById 成员应该在您的存储库中并从服务中调用,该服务正在协调 Order 对象的水合。 我认为 repos 是聚合的唯一有效来源,因此服务也使用 repos 而不是工厂。在这种情况下,我仍然不明白工厂的目的,我认为 repo 包含创建代码。【参考方案6】:

我和 DDD 的人可能会对此争论不休,但基本上“工厂”类的想法是发布域对象。然后域对象访问数据并成为您正在使用的域的“模型”的一部分。

应用程序包含 UI 等,它不是继承层次结构。

小心那种“那种”运算符;很多人认为因为他们可以使用继承,所以他们必须使用继承。聚合(“contains”或“has a”而不是“is a”)通常是更好的选择。

更新

不一定。想想域告诉你什么:它类似于“我需要一个产品,并且我知道产品的产品编号。当产品工厂完成后,我希望得到代表我的产品的完全填充的对象。”当您创建 Product 对象时,它需要做什么才能成为代表特定产品的有效 Product 对象?

【讨论】:

我不确定我是否跟随。所以你说的是我的域类中有一个方法来获取我的数据......所以 Product p = ProductFactory.CreateProduct();然后我发出一个电话 p.GetData()? 支持聚合 > 继承。这就是四人帮的重要信息!

以上是关于领域驱动设计和工厂类的作用的主要内容,如果未能解决你的问题,请参考以下文章

领域驱动设计

领域驱动设计和 IoC/依赖注入

领域驱动设计 - 战术设计 - 3/3聚合与模型的构建方法

在重构业务系统时,应用领域驱动设计

DDD领域驱动设计-DDD概览

领域驱动设计系列浅析VODTODOPO的概念区别和用处