【问题标题】:Where to implement Automapper in a DDD + layered architecture在 DDD + 分层架构中实现 Automapper 的位置
【发布时间】:2012-12-07 22:41:53
【问题描述】:

背景: 为了我自己的清晰/自我教育,我正在尝试使用 TDD + DDD 实现一个简单的订单输入应用程序。我的主要目标是通过分离关注点来保持架构清洁。

我有四层(目前)...

  1. 具有 CustomerRepository 类的持久性/DAL,该类可以对“聚合根”、客户及其相关的订单和订单项执行 GetById、保存、操作。为了允许“穷人的依赖注入”

  2. 域/BLL 层包含“业务实体”类,这些类执行细粒度操作以帮助创建新订单,根据订单大小和客户位置应用税收、折扣、运输逻辑。

  3. 应用程序外观(应用程序服务/编排)包含厚实、粗粒度的类,用于编排“业务实体”并减少与演示文稿(以及可能的 WebServices 层)之间的交流。

  4. 表示层

此外,我想在关键层之间传递 POCO DTO ......尤其是在 Persistence=>Domain 层和 ApplicationFacade=>Presentation 层之间。所以,我有 CustomerDto、OrderDto、OrderItemDto 以及在共享包中定义的适当关系。

我想使用构造函数注入将 ICustomerRepository 的实现注入到客户“业务实体”类中,然后在“业务实体”上调用 Customer.Save() 以启动创建/更新过程,最终调用在 CustomerRepository 上保存方法。毕竟,客户是“聚合根”,拥有保存所需的所有信息……它也是注入的客户存储库的“守护者”。

问题: 这是我遇到障碍的地方。我希望保持域/BLL 层尽可能纯净,并避免将其耦合到任何第三方框架和 API,但是 Customer.Save() 方法需要翻译客户“聚合根”并将其所有 Orders 和 OrderItems 放入其 DTO 版本中,以传输到注入的 Persistence 层 CustomerRepository ...这是 Automapper 的工作。

问题是...如果我不将 Automapper 放在 Domain/BLL 层中,我不确定它应该放在 哪里

尽管它的工作是编排,但把它放在 ApplicationFacade 中感觉不太合适。

把它放在 Domain/BLL 层肯定感觉不对,因为我想保持它的原始状态。

因此,我觉得我错过了一些东西……我在处理这个问题时对工作部分应该如何组合在一起来完成这项任务存在根本性的误解。有什么建议么? (请温柔一点,我对这一切都很陌生,对 SO 也很陌生。如果我需要展示我目前所拥有的一些代码,请告诉我。)

【问题讨论】:

    标签: architecture domain-driven-design automapper layer


    【解决方案1】:

    为了回答您的具体问题,我将更笼统地谈谈您的架构。您为项目设计的架构与 DDD 使用的典型架构略有不同。虽然划分职责的方式很典型,但在 DDD 中,域类不负责它们自己的持久性。事实上,DDD 的口头禅是persistence ignorance。这基本上意味着域类对持久性无知——它们没有Save 方法。相反,应用程序服务(架构中的应用程序外观层)与存储库协调以重构和持久化域实体。

    接下来,在实现存储库时,通常不需要在底层持久性技术和域类之间建立显式 DTO。使用 NHibernate 和 EF 等 ORM,域类和关系表之间的映射可以用映射类或基于 XML 的配置来表示。结果,您的域类被隐式映射 - 不需要 DTO。有些情况确实需要 DTO,在这种情况下,DTO 和域类之间的映射应该由存储库实现封装。这是您可以调用 AutoMapper 的地方。

    最后,在表示层和应用程序/域层之间使用 DTO 进行通信是一种常见的做法。要准确确定应该在哪里进行映射,您必须深入挖掘最适合您的项目的架构。大多数现代 DDD 架构都基于Hexagonal architecture。在六边形架构中,您的域位于中心,所有其他“层”使域适应特定技术。例如,存储库可以看作是域和特定数据库技术之间的adapter。 ASP.NET WebAPI 可以看作是域和 HTTP/REST 之间的适配器。同样,表示层可以看作是领域和特定领域之间的适配器。 DTO 可以在这些适配器中的每一个中体现,并且适配器负责映射到这些 DTO 和从这些 DTO 映射。

    一个典型的例子是使用应用程序服务在您的域上建立一个外观。还没有 DTO 在起作用。接下来,使用 ASP.NET WebAPI 创建一个服务层(DDD 中的开放主机服务) - 一个适配器。您创建 ASP.NET WebAPI 特定的 DTO,并由该适配器映射到这些 DTO 和从这些 DTO 映射。因此,如果您使用 AutoMapper,则应在该层调用它。这可以通过操作过滤器显式或以aspect-oriented 方式完成。表示层也是如此。表示层可以直接耦合到应用程序服务或 ASP.NET WebAPI 开放主机服务。在任何一种情况下,表示层都有责任在域类(来自应用程序服务或来自 WebAPI 的 DTO)与其自己的原语(如 ViewModel)之间进行映射。

    【讨论】:

    • 哇,谢谢!优秀的答案。我完全同意前两段——你发现的区别是正确的。最后两段我需要更多时间来消化,阅读您所做的一些参考资料(特别是 Hexagonal 架构和开放主机服务),看看这对我的小实验有何影响。非常感谢您花时间回复。
    • 关于第三段最后一句话的后续行动......我从应用程序外观/服务层返回 DTO 的理由——而不是将 POCO“业务实体”的实例返回到表示层——是隐藏域/BLL 层中每个业务实体上定义的更细粒度的方法。我希望那些更细粒度的方法被应用程序外观/服务层而不是表示层使用。那么,鉴于此,我应该做些什么吗?我见过一些为他们的业务实体定义的接口。
    • 我不会为域对象定义接口,因为 DTO 和域对象并不总是一对一的。您可以只创建 DTO 在应用程序服务和表示层之间传递,并在应用程序服务内使用 AutoMapper 映射它们。同样,这些 DTO 不一定直接对应于域对象。它们可以表示特定应用程序服务用例方法的参数或返回值。对于创建新实体的用例,DTO 可能看起来很像相应的域对象。
    • @eulerfx:+1 很好的答案。就像我和你在同一页上一样:在基础设施层(连同 Web API)中声明 DTO,而不是在应用程序层中声明它们?那么DTO-to-Domain的映射就变成了Infrastructure层的职责了?
    • @eulerfx:我正在阅读 this,作者主张将 DTO 保留在应用层中(这对我来说似乎有点不直观)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-03
    • 2011-04-19
    • 2017-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-16
    相关资源
    最近更新 更多