实现领域驱动设计-应用程序

应用程序定义

“应用程序”表示那些支撑核心域模型的组件,通常包括领域模型本身,用户界面,内部使用的应用服务和基础设施组件等

这是书中的定义,理解起来跟通常意义的“应用程序”也是一样的,就是一个可以用的软件,就是完成一定业务功能的完整的程序,就是把领域模型、领域服务、应用服务、资源库、基础设施加上用户界面组合起来,构成一个整体。形式可以有很多种,可以是桌面应用程序或web应用程序乃至移动端App。当然使用bash编写一个脚本,实现一定功能,其实也算是一个应用程序,只是一般太简单了,不属于本章讨论的应用程序范围。

下图来一个应用程序的典型构成:


应用程序的典型模型

这是我理解画出的,跟书中的应用程序的主要方面的图并不一样。用户界面通过应用服务实现业务功能,应用服务把操作委托给领域模型(实体、值对像、聚合)、领域服务(可能不存在)、资源库。领域模型、领域服务、资源库是领域要素,是通用语言的表达,是接口定义。而真正的技术实现都由基础设施实现,基础设施屏蔽领域概念与技术实现,基础设施直接跟技术组件打交道,比如存储的DB、消息中间件等,领域模型(包括领域服务和资源库)是不直接跟技术组件打交道的。领域服务和资源库,会使用和组合领域模型(主要是聚合),分别完成业务逻辑和聚合存取。

用户界面

用户界面,称为UI,而一般为了给终端用户带来更好的操作体验,会实现带图形操作接口,即GUI,可以表现为web应用或桌面应用和移动端应用。

用户界面通常需要渲染多个聚合实例中的属性,但用户一次只会修改其中一个聚合实例。

可以通过数据传输对象(Data Transfer Object,DTO)或领域负载对象(Domain Payload Object,DPO)来组装多个聚合实例,DTO直接拷贝属性,类似深拷贝,DPO只拷贝聚合实体引用,所以前者适合需要序列化的场景(如RPC),后者适合单虚拟机应用架构中。

如果多聚合组装逻辑比较复杂且成本高,可以使用CQRS架构。

使用调停者发布聚合的内部状态。

可以使用数据转换器来处理针对不同类型客户端的输出。

展现模型(Presenation Model),是区别于视图模型的(View model),展现模型可以作为渲染视图的适配器,可以跟踪用户的编辑,是围绕着应用服务的一个最小化门面。展现模型,其实跟MVC架构中的C即Controller的职责很像。

应用服务

应用服务是领域模型的直接客户。应用服务应该做成很薄的一层,并且只使用它们来协调对模型的任务操作。

应用服务负责用例流的任务协调,每个用例流对应着一个应用服务方法。应用服务管理着事务、安全和任务委派等操作,把操作委派给领域模型。

应用服务方法参数可以直接使用领域对象吗?建议不使用,入参使用命令对象,命令对象属性使用基本数据类型,输出使用DTO。虽然会增加很多对象的生成和释放消耗。

应用服务可以使用独立接口,也可以把接口和实现定义在一个类中。应用服务一般不需要使用基础设施来实现。

基础设施

基础设施就是为领域模型(包括领域服务和资源库)提供技术实现。如前面应用程序定义的图形,基础设施实现了领域服务、资源库的接口乃至用户界面,直接于技术组件打交道。

基础设施的实现可以依赖注入或服务工厂来完成接口实现的查找。

企业组件容器

我也是只使用Spring的

组合多个限界上下文

组合多个限界上下文一节的问题是:UI需要组合多个模型,而三个模型位于三个限界上下文。如果使用一个应用服务来组合多个模型,因为是集成多个限界上下文的,所以这时候应用服务其实需要内建一个防腐层,并且“映射”出新的领域模型,这些新领域模型更多是运载数据属性的需要,所以容易产生贫血领域对象。如果是创建一些新的、清晰的限界上下文?这也增加不少复杂度,特别是会导致用户接口层对多个应用层的依赖。那到底该选择哪种方式呢?书中也没给出答案,只能根据自己的实际情况做决定。

而我为什么要把这一节放到最后来讲,其实还想说在微服务的思路下,服务进程拆得很细,可能一个聚合或一个资源库实现就会独立成一个服务进程,如果这时候把一个服务进程都当成一个上下文,那会使得应用程序的领域概念很复杂,所以下面提出一个思路,限界上下文是按领域来划分按通用语言的表达,而不是服务进程:


多服务进程的上下文

左图中的每一个非内嵌框图都是代表一个独立服务进程的微服务

你可能感兴趣的:(实现领域驱动设计-应用程序)