何处在DDD +分层架构中实现Automapper

Bob*_*bor 23 architecture domain-driven-design layer automapper

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

我有四层(现在)......

  1. 具有CustomerRepository类的Persistence/DAL,可以对"聚合根",客户及其相关订单和OrderItem执行GetById,Save,操作.为了让"穷人的依赖注入"

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

  3. 应用程序Facade(应用程序服务/编排)包含大块,粗粒度的类来编排"业务实体"并减少与表示(可能是WebServices层)的聊天.

  4. 表示层

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

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

问题: 这是我遇到麻烦的地方.我想保持Domain/BLL Layer尽可能纯,并避免将其耦合到任何第三方框架和API, Customer.Save()方法需要将Customer"聚合根"及其所有Orders和OrderItems转换为他们的DTO版本用于传输到注入的持久层CustomerRepository ...这是Automapper的工作.

问题是......如果我不把Automapper在域/ BLL层,我真的不知道哪里应该去.

将它放在ApplicationFacade中感觉不对,即使它的工作是编排.

把它放在Domain/BLL层中肯定是不对的,因为我想让它保持原始状态.

因此,我觉得我已经错过了一些东西......我正在接近这一点,因为他们对工作部分应该如何共同完成这项任务有着根本的误解.有什么建议?(请保持温和,我对这一切都是新手,对SO来说是新手.如果我需要展示一些我到目前为止的代码,请告诉我.)

eul*_*rfx 48

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

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

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

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

  • 关于你的第三段的最后一句的后续......我从applicationfacade/services层返回DTO的理由 - 而不是将POCO"业务实体"的实例返回到表示层 - 是为了隐藏更精细的方法在域/ BLL层中的每个业务实体上定义.我希望applicationfacade/services层使用那些更细粒度的方法,而不是表示层.所以,鉴于此,我应该做些什么?我见过一些为他们的业务实体定义接口. (2认同)
  • 我不会为域对象定义接口,因为DTO和域对象并不总是一对一.您可以创建DTO以在应用程序服务和表示层之间传递,并使用应用程序服务中的AutoMapper映射它们.同样,这些DTO不一定直接对应于域对象.它们可以表示特定应用程序服务用例方法的参数或返回值.对于创建新实体的用例,DTO可能看起来很像相应的域对象. (2认同)