DDD:Persistence Logic应该属于Infrastructure Layer吗?

时间:2016-10-15 04:02:37

标签: c# asp.net-mvc repository domain-driven-design persistence

我的应用程序遵循DDD设计原则。它是一个ASP.NET MVC应用程序,MVC Web应用程序是表示层(我将控制器移动到应用程序层)。它还具有应用层,主要是应用服务,用例等。应用层之上是域模型所在的域层。然后是基础设施层,它位于其他所有层之上,并且应该不依赖于其他层。

但是我注意到有一个问题,如果持久性逻辑进入基础架构层,如DDD书籍所示,基础架构层将依赖于域层。例如,存储库需要知道要创建的域模型(实体)的类型,凭借这些知识,它们将依赖于域层。然而,最初的DDD原则表明,基础设施层应该完全没有任何依赖性。

所以现在我很困惑,Persistence Logic真的应该属于Infrastructure Layer吗?如果是这样,它会使Infrastructure Layer依赖于Domain Layer。如果没有,那么它应该在哪里?应用层?或者可能是应用层和域层之间的单独层(因为应用服务使用存储库,存储库使用域模型)。你怎么看?

4 个答案:

答案 0 :(得分:4)

依赖类型

我认为一个可能有帮助的重要概念是区分依赖类型 - 具体而言,层或组件可以依赖于另一层,因为:

  1. 是根据该层中定义的概念定义的,或
  2. 它使用另一层 - 通过委托给该层(也称为该层中服务的调用方法)
  3. 或以上两者
  4. Inversion of ControlDependency Injection使这种区别变得更加重要。他们指导我们应该依赖于抽象而不是结核。

    这意味着,例如,域层可以定义并依赖于存储库的抽象 - 例如一个IEntityRepository接口。

    然而,实施(具体化)然后在基础设施层实施。

    当应用程序层想要调用存储库时,它依赖于抽象(接口),控制系统(IoC Container)的反转为它提供了来自基础结构层的实现。

    在这种情况下,基础架构层依赖于域层 - 但只是为了了解它正在实现的接口,但 NOT 依赖于任何其他层,以便委托给它 - 它是调用堆栈中的最后一层。

    这个概念解决了您所关注的冲突,因为基础设施层并不依赖于其他任何东西来实现它的功能。

    洋葱建筑

    一旦开始将IoC概念纳入考虑您的架构,可以变得非常有用的模型是onion architecture模式。在这个视图中,我们保留了分层方法,但不是将其视为一个堆栈,而是将其视为一个分层的洋葱,其中Domain位于中心,并且与应用程序外部的所有内容进行交互。

    原始的DDD书籍没有具体提及这一点,但它已成为实施DDD系统的一种非常常见的模式。

    它也称为Ports and Adaptor模式,或Hexagonal Architecture

    这个想法是它根据“了解'来模拟依赖关系。就洋葱而言 - 外层知道内层,但内层不了解外层。

    但它模拟了洋葱运动的应用流程授权 - 从洋葱的一侧到另一侧。

    更具体一点:

    外层(也称为'主适配器')是请求进入系统的位置。适配器负责处理特定的API表示(例如REST api)并将其转换为对Application服务层的请求 - 下一层。这通常表示为洋葱的左上角。

    应用服务层代表一个用例'申请。通常,方法将使用存储库接口来检索聚合的实例,然后委托聚合根上的方法来执行业务逻辑,该业务逻辑涉及根据用例的需要更改聚合的状态。然后,应用程序服务层使用另一个接口来请求基础架构层“保存”。变化(通常使用工作单元抽象)

    这里我们有应用层将控制流委托给一个外层'洋葱(也称为辅助适配器')。这通常表示为洋葱的右下角,类似于基础设施层'在你的描述中。

    这就是IoC的用武之地。因为我们有一个内层委托给外层,所以只有外层才能实现内层定义的接口(它可以做到,因为它知道了内层)。但是,IoC容器注入了实际的具体实现,它有效地允许内层将控制权委托给外层而不依赖于它。

    在这个概念化过程中,请求从左上角流向右下角,而内层没有了解外层的任何信息。

答案 1 :(得分:0)

这里有2个选项

1)使用DAO对象。那么基础设施层只需要了解那些DAO对象。特别是如果您使用实体框架或类似的东西,那么这不是一个不好的策略。你确实从映射中获得了一些开销,但你可以使用Automapper。

2)与基础设施了解您的DDD模型这一事实相符。但由于DDD中的所有内容都围绕着您的域模型,因此这不是一个权衡。 (特别是如果您的域模型非常清楚,因此不会发生根本变化)。虽然不要因为数据库设计而影响您的域模型,但要非常小心。始终应该是数据库,而不是您没有进行某些更改的域模型,因为它很难在数据库中迁移。

答案 2 :(得分:0)

持久性逻辑通常属于基础设施层,它位于洋葱的外层,由于洋葱的外层依赖于内层,而域层位于中心,因此基础设施层依赖于在域层。

答案 3 :(得分:0)

在 DDD 范围内,持久化逻辑应该属于基础设施层,因为详细的持久化实现不是领域层的关注。

然而,由于我们对项目架构的看法不同,基础设施层可以依赖于领域层,也可以被它所依赖。

在传统的分层架构中,
从表示层,通过应用层,领域层到基础设施层,建议采用线性依赖。
在这种情况下,Infrasture Layer 被视为上游,定义了 Persistence Logic 的接口和实现。
领域层作为下游用户,会按照基础设施层提供的接口来获取实体,转化为领域对象,然后做领域逻辑。

Java 中的典型代码:

// package com.xxxx.product.application
class ProductOrderApp {
    private ProductRepository repository;
    void orderOneProduct(String userCode, String productCode) {
        ProductEntity enitity = repository.getOneByProductCode(productCode); 
        // 1. convert ProductEntity to Product Object
        // 2. pass Product Object to Domain layer to do some logic   
        ...
    }
}


// package com.xxxx.product.domain
class Product { ... }


// package com.xxxx.product.infrastructure
interface ProductRepository {
    // does not depend on any domain concepts
    ProductEntity getOneByProductCode(String code);
}

class ProductRepositoryImpl implements ProductRepository {
    @Override
    ProductEntity getOneByProductCode(String code) {
        ProductEntity entity = ... // get entity from some database
        return entity;  
    }
}

在洋葱架构中(在最佳答案中也有解释),
域层被认为是上游,所有其他下游层都应该遵循域层提供的接口。
因此,基础设施层依赖于域层,因为它实现了域层定义的接口。领域层不依赖于基础设施层。

Java 中的典型代码:

// package com.xxxx.product.domain
class Product { ... }

interface ProductRepository {
    // other Domain Logic can easily call this method as it uses Domain Objects for interection
    Product getOneByProductCode(ProductCode code);
}

// package com.xxxx.product.infrastructure
class ProductRepositoryImpl implements ProductRepository {
    @Override
    Product getOneByProductCode(ProductCode code) {
        ProductEntity entity = ... // get entity from some database
        // persistence logic knows domain objects and do the convertion here
        return convertToProduct(entity);  // convert to domain objects
    }
}