车神ne的博客

.Net技术分享站

导航

大话领域驱动设计——分层架构

这一篇,我们首先要分析领域驱动设计的分层架构,在实际编码时,战术模式下的各种概念,需要最终落地到分层架构的各个层中。

如果大家对于DDD有所了解,一定看到过下面这个经典的DDD四层架构图。我们也是以经典的DDD四层架构为基础做详细的讲解:

领域层是我们领域模型具体代码实现的位置,通常包含实体、聚合根、值对象、领域服务、领域事件等的具体实现,也包含仓储的接口声明。领域是整个应用的核心。

应用层可以理解为对于领域层的封装及聚合,是比较轻量的一层,主要将领域层的业务对象及功能封装成可供表示层使用的服务。应用层通常包含应用服务和数据传输对象。

用户界面层也称为表示层,为用户提供可视化的交互界面,是系统和用户的接口。

基础设施层整个系统的基础功能支撑,包含通用的模块、方法也包含仓储的实现。

和传统以三层为基础的分层架构不同,在DDD标准的四层架构里表示层也可以直接引用领域层,但是在实际的开发里面,我们一般不会这么做,而是领域层的所有功能都经过应用层的对象封装。而UI层只和应用层交互。

仓储是对数据操作的封装,和传统三层架构中数据访问作为三层架构中重要的一层(数据访问层)不同,在DDD四层架构中,仓储接口的声明是在领域层,但实际的仓储实现是在基础设施层。之所以出现这种分层,是因为在DDD的思想中,当我们设计时不应该去关注如何对数据持久化,只需要知道有哪些数据在何时需要持久化即可。而且随着EF等ORM框架的出现,数据持久化的代码也变得越来越简单。甚至在ABP vNext框架中,我们大多数时候可以直接使用默认的仓储接口和实现。

领域层的代码设计和模块划分是按照领域模型来完成的,也就是说领域层的所有代码都是依据业务本身来设计。而应用层设计的时我们更多的会考虑表现层需要哪些接口或哪些数据。一个应用层的模块,可以对应一个或多个领域,一个接口也可以跨多个领域的数据和功能的整合。

但是在实际设计过程中,应用层的设计除了考虑表现层的模块划分以外,更多的会考虑领域层的划分。这主要考虑到我们如果要做微服务下服务的拆分时主要是依据领域划分来拆分微服务。通常会将一个领域或者几个关联度较高的领域划分成一个微服务。如果某一应用层模块同时引用A和B领域而A和B领域分别划到了不同的服务中,这个应用模块就会无法分配。所以我们会看到很多时候领域层和服务层的模块划分基本一致。同时为了保证领域层对象和应用层对象映射时方便,通常我们也要保证领域实体和应用层数据传输对象中相同意义的字段名称保持一致。

在ABP vNext框架中,领域层包含Domain和Domain.Share两个项目,Domain为领域层的核心项目,以文件夹划分领域。包含Entity(实体)、AggregateRoot(聚合根)、ValueObject(值对象)、IDomainService(领域服务接口)、DomainService(领域服务实现)、IRepository(仓储接口)及解决方案的其他领域对象,Domain.Share项目包含常量,枚举和其他对象,这些对象实际上是领域层的一部分,但是解决方案中所有的项目中都会使用到。

应用层包含Application和Application.Contracts两个项目。Application.Contracts项目包含DTO(数据传输对象)和IApplicationService(应用服务接口),Application项目包含ApplicationService(应用服务实现)。

ABP vNext在基础设施层提供的项目为EntityFrameworkCore和MongoDB,分别对应仓储在EF和MongoDB下的实现。

ABP vNext为表现层提供的项目为Web,是MVC下表现层的实现。对于前后端分离项目,后端不包含表现层,而使用HttpApi.Host作为启动项,该项目是作为服务宿主存在,主要提供WebApi所需要的服务监听和管道模型。

实际在项目引用关系中,EntityFrameworkCore项目引用的是DomainService项目,这个和开头部分那张图是相反的。原因是仓储接口定义在Domain项目中而实现定义在EntityFrameworkCore项目中,通过依赖注入的方式注入到上层需要调用的模块中。

 

posted on 2022-03-18 09:41  车神ne  阅读(371)  评论(0编辑  收藏  举报