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

时间:2009-02-17 01:27:08

标签: design-patterns domain-driven-design

我不清楚工厂类的角色和责任是什么。我足够了解工厂类应该可以用于创建域对象(聚合根)及其关联的实体和值对象。

但我不清楚的是工厂“层”在于DDD架构?工厂应该直接调用存储库来获取其数据还是服务库?

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

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

如果工厂类的角色是用于创建对象,那么服务层有哪些好处?

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

6 个答案:

答案 0 :(得分:54)

  

但我不清楚的是在哪里   工厂“层”位于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中。

现在,这里是如何在每个实例中使用工厂。

在OrderRepository中:

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&gt;应用&gt;域&gt;服务&gt;数据

  

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

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

  

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

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

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

顺便说一下,您描述的层次结构可能应该是UI&gt;应用&gt;域服务&gt;域&gt;基础设施(数据)

  

我问了很多问题   感谢任何回应。我是什么   缺乏的是一个示例应用程序   演示了如何在一个层中的所有层   域驱动设计项目来了   在一起......有什么事吗?   有?

Jimmy Nilsson的

Applying Domain Driven Design and Patterns对Eric Evans的Domain-Driven Design表示赞赏。它有很多代码示例,但我不知道是否强调分层。分层可能很棘手,几乎是与DDD分开的主题。

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

答案 1 :(得分:11)

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

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

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

然后将其传递给存储库,存储库将负责处理它。

userRepository.Insert(user);

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

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

答案 2 :(得分:6)

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

我在以下时间使用工厂:

  • 某些图层需要创建具有一定规律性的对象。
  • 只有该图层知道何时创建它,或者使用哪些自定义
  • 只有其他一些图层确切知道要创建的内容。

答案 3 :(得分:3)

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

Application 包含 UI等,它不是继承层次结构。

小心那种“那种”操作员;很多人认为因为他们可以使用继承,所以他们必须使用继承。聚合(“包含”或“有”而不是“是”)通常是更好的选择。

更新

不一定。想想域名告诉你的内容:它就像“我需要一个产品,我知道产品的产品编号。当产品工厂完成后,我希望得到代表我产品的完全填充的对象。”在创建Product对象时,如何成为代表特定产品的有效Product对象需要做什么?

答案 4 :(得分:2)

  

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

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

  

工厂在哪里适合   以下框架:UI&gt;应用&gt;域&gt;服务&gt;数据

不知道这个分层来自何处,DDD中的图层没有固定,但我会说你最关注这种风格

UI&gt;应用&gt;域

在Domain中你有多种类型的对象,我会设置关于它们之间关系的规则:

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

如果工厂类的角色是用于创建对象,那么服务层有哪些好处?

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

答案 5 :(得分:1)

我看到Application Services层负责创建新实体,通过调用工厂来实现。因此,实体工厂接口在Application Service模块中定义,实际实现是基础结构服务,与持久性相同。有关一般应用程序服务与域服务与基础结构服务的明确想法,我通过观察这一点获得了很多清晰度:Bob Martin Ruby Midwest 2011