应用程序定义
“应用程序”表示那些支撑核心域模型的组件,通常包括领域模型本身,用户界面,内部使用的应用服务和基础设施组件等
这是书中的定义,理解起来跟通常意义的“应用程序”也是一样的,就是一个可以用的软件,就是完成一定业务功能的完整的程序,就是把领域模型、领域服务、应用服务、资源库、基础设施加上用户界面组合起来,构成一个整体。形式可以有很多种,可以是桌面应用程序或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需要组合多个模型,而三个模型位于三个限界上下文。如果使用一个应用服务来组合多个模型,因为是集成多个限界上下文的,所以这时候应用服务其实需要内建一个防腐层,并且“映射”出新的领域模型,这些新领域模型更多是运载数据属性的需要,所以容易产生贫血领域对象。如果是创建一些新的、清晰的限界上下文?这也增加不少复杂度,特别是会导致用户接口层对多个应用层的依赖。那到底该选择哪种方式呢?书中也没给出答案,只能根据自己的实际情况做决定。
而我为什么要把这一节放到最后来讲,其实还想说在微服务的思路下,服务进程拆得很细,可能一个聚合或一个资源库实现就会独立成一个服务进程,如果这时候把一个服务进程都当成一个上下文,那会使得应用程序的领域概念很复杂,所以下面提出一个思路,限界上下文是按领域来划分按通用语言的表达,而不是服务进程:
多服务进程的上下文左图中的每一个非内嵌框图都是代表一个独立服务进程的微服务
网友评论