这是“畛域驱动设计实际之路”系列的第四篇文章,从单体架构的弊病引入微服务,联合畛域驱动的概念介绍了如何做微服务划分、设计畛域模型并展现了整体的微服务化的零碎架构设计。联合分层架构、六边形架构和整洁架构的思维,以理论应用场景为背景,展现了一个微服务的程序结构设计。
一、单体架构的弊病
单体构造示例(援用自互联网)
个别在业务倒退的初期,整个利用波及的性能需要较少,绝对比较简单,单体架构的利用比拟容易部署、测试,横向扩大也比拟易实现。
然而,随着需要的一直减少,越来越多的人退出开发团队,代码库也在飞速地收缩。缓缓地,单体利用变得越来越臃肿,可维护性、灵活性逐步升高,保护老本越来越高。
上面剖析下单体架构利用存在的一些弊病:
1、复杂性高
在我的项目初期应该有人能够做到对利用各个性能和实现一目了然,随着业务需要的增多,各种业务流程盘根错节的揉在一起,整个零碎变得宏大且简单,以至于很少有开发者分明每一个性能和业务流程细节。
这样会使得新业务的需要评估或者异样问题定位会占用较多的工夫,同时也蕴含着未知危险。更蹩脚的是,这种极度的复杂性会造成一种恶性循环,每一次更改都会使得零碎变得更简单,更难懂。
2. 技术债权多
随着时间推移、需要变更和人员更迭,会逐步造成应用程序的技术债权,并且越积越多。比方,团队必须长期应用一套雷同的技术栈,很难采纳新的框架和编程语言。有时候想引入一些新的工具时,就会使得我的项目中须要同时保护多套技术框架,比方同时保护 Hibernate 和 Mybatis,使得老本变高。
3. 谬误难隔离
因为业务我的项目的所有功能模块都在一个利用上承当,包含外围和非核心模块,任何一个模块或者一个小细节的中央,因为设计不合理、代码品质差等起因,都有可能造成利用实例的解体,从而使得业务全面受到影响。其根本原因就是外围和非核心性能的代码都运行在同一个环境中。
4. 我的项目团队间协同老本高,业务响应越来越慢
多个相似的业务我的项目之间势必会存在相似的功能模块,如果都采纳单体模式,就会带来反复性能建设和保护。而且,有时候还须要相互产生交互,买通单体零碎之间的交互集成和合作的老本也须要额定付出。
再者,当我的项目大到肯定水平,不同的模块可能是不同的团队来保护,迭代联调的抵触,代码合并分支的抵触都会影响整个开发进度,从而使得业务响应速度越来越慢。
5. 扩大老本高
随着业务的倒退,零碎在呈现业务解决瓶颈的时候,往往是因为某一个或几个功能模块负载较高造成的,但因为所有性能都打包在一起,在呈现此类问题时,只能通过减少利用实例的形式分担负载,没方法对独自的几个功能模块进行服务能力的扩大,从而带来资源额定配置的耗费,老本较高。
针对以上痛点,近年来越来越多的互联网公司采纳“微服务”架构构建本身的业务平台,而“微服务”也取得了越来越多技术人员的必定。
微服务其实是 SOA 的一种演变后的状态,与 SOA 的办法和准则没有本质区别。SOA 理念的外围价值是,松耦合的服务带来业务的复用,依照业务而不是技术的维度,联合高内聚、低耦合的准则来划分微服务,这正好与畛域驱动设计所提倡的理念相符合。
二、微服务设计
1. 微服务划分
从狭义上讲,畛域即是一个组织所做的事件以及其中蕴含的所有。每个组织都有它本人的业务范围和做事形式,这个业务范围以及在其中所进行的流动便是畛域。
DDD 的子域和限界上下文的概念,能够很好地跟微服务架构中的服务进行匹配。而且,微服务架构中的自治化团队负责服务开发的概念,也与 DDD 中每个畛域模型都由一个独立团队负责开发的概念吻合。DDD 提倡按业务畛域来划分零碎,微服务架构更强调从业务维度去做分治来应答零碎复杂度,跳过业务架构设计进去的架构关注点不在业务响应上,可能就是个大泥球,在面临需要迭代或响应市场变动时就很苦楚。
DDD 的外围诉求就是将业务架构映射到零碎架构上,在响应业务变动调整业务架构时,也随之变动零碎架构。而微服务谋求业务层面的复用,设计进去的零碎架构和业务统一;在技术架构上则零碎模块之间充沛解耦,能够自在地抉择适合的技术架构,去中心化地治理技术和数据。
以电商的资源订购零碎为例,典型业务用例场景包含查看资源,购买资源,查问用户已购资源等。
畛域驱动为每一个子域定义独自的畛域模型,子域是畛域的一部分,从业务的角度剖析咱们须要笼罩的业务用例场景,以高内聚低耦合的思维,联合繁多职责准则 (SRP) 和闭包准则(CCP),从业务畛域的角度,划分出用户治理子域,资源管理子域,订单子域和领取子域共四个子域。
每个子域对应一个限界上下文。限界上下文是一种概念上的边界,畛域模型便工作于其中,每个限界上下文都有本人的通用语言。限界上下文使得你在畛域模型四周加上了一个显式的、清晰的边界。当然,限界上下文不仅仅蕴含畛域模型。当应用微服务架构时,每个限界上下文对应一个微服务。
2.畛域模型
聚合是一个边界内畛域对象的集群,能够将其视为一个单元,它由根实体和可能的一个或多个其余实体和值对象组成。聚合将畛域模型合成为 块,每个聚合都能够作为一个单元进行解决。
聚合根是聚合中惟一能够由外部类援用的局部,客户端只能通过调用聚合根上的办法来更新聚合。
聚合代表了统一的边界,对于一个设计良好的聚合来说,无论因为何种业务需要而产生扭转,在单个事务中,聚合中的所有不变条件都是统一的。聚合的一个很重要的教训设计准则是,一个事务中只批改一个聚合实例。更新聚合时须要更新整个聚合而不是聚合中的一部分,否则容易产生一致性问题。
比方 A 和 B 同时在网上购买货色,应用同一张订单,同时意识到本人购买的货色超过预算,此时 A 缩小点心数量,B 缩小面包数量,两个消费者并发执行事务,那么订单总额可能会低于最低订单限额要求,但对于一个消费者来说是满足最低限额要求的。所以应该站在聚合根的角度执行更新操作,这会强制执行一致性业务规定。
另外,咱们不应该设计过大的聚合,解决大聚合形成的 ” 巨无霸 ” 对象时,容易呈现不同用例同时须要批改其中的某个局部,因为聚合设计时思考的一致性束缚是对整个聚合产生作用的,所以对聚合的批改会造成对聚合整体的变更,如果采纳乐观并发,这样就容易产生某些用例会被回绝的场景,而且还会影响零碎的性能和可伸缩性。
应用大聚合时,往往为了实现一项基本操作,须要将成千盈百个对象一起加载到内存中,造成资源的节约。所以应尽量采纳小聚合,一方面应用根实体来示意聚合,其中只蕴含最小数量的属性或值类型属性,这里的最小数量示意所需的最小属性汇合,不多也不少。必须与其余属性保持一致的属性是所需的属性。
在聚合中,如果你认为有些被蕴含局部应该建模成一个实体,此时,思考下这个局部是否会随着工夫而扭转,或者该局部是否能被全副替换。如果能够全副替换,那么能够建模成值对象,而非实体。因为值对象自身是不可变的,只能进行全副替换,应用起来更平安,所以,个别状况下优先应用值对象。很多状况下,许多建模成实体的概念都能够重形成值对象。小聚合还有助于事务的胜利执行,即它能够缩小事务提交抵触,这样不仅能够晋升零碎的性能和可伸缩性,另外零碎的可用性也失去了加强。
另外聚合间接的援用通过惟一标识实现,而不是通过对象援用,这样不仅缩小聚合的应用空间,更重要的是能够实现聚合间接的松耦合。如果聚合是另一个服务的一部分,则不会呈现跨服务的对象援用问题,当然在聚合外部对象之间是能够互相援用的。
上述对于聚合的次要应用准则总结起来能够演绎为以下几点:
- 只援用聚合根。
- 通过惟一标识援用其余聚合。
- 一个事务中只能创立或批改一个聚合。
- 聚合边界之外应用最终一致性。
当然在理论应用的过程中,比方某一个业务用例须要获取到聚合中的某个畛域对象,但该畛域对象的获取门路较繁琐,为了兼容该非凡场景,能够将聚合中的属性 (实体或值对象) 间接返回给应用层,使得应用层间接操作该畛域对象。
咱们常常会遇到在一个聚合上执行命令办法时,还须要在其余聚合上执行额定的业务规定,尽量应用最终一致性,因为最终一致性能够按聚合维度分步骤解决各个环节,从而晋升零碎的吞吐量。对于一个业务用例,如果应该由执行该用例的用户来保证数据的一致性,那么能够思考应用事务一致性,当然此时仍然须要遵循其余聚合准则。如果须要其余用户或者零碎来保证数据一致性,那么应用最终一致性。实际上,最终一致性能够反对绝大部分的业务场景。
基于上面对电商的资源订购零碎业务子域的划分,设计出资源聚合,订单聚合,领取聚合和用户聚合,资源聚合与订单聚合之间通过资源 ID 进行关联,订单聚合与领取聚合之间通过订单 ID 和用户 ID 进行关联,领取聚合和用户聚合之间通过用户 ID 进行关联。资源聚合根中蕴含多个资源包值对象,一个资源包值对象又蕴含多个预览图值对象。当然在理论开发的过程中,依据理论状况聚合根中也能够蕴含实体对象。每个聚合对应一个微服务,对于特地简单的零碎,一个子域可能蕴含多个聚合,也就蕴含多个微服务。
3.微服务零碎架构设计
基于上面对电商的资源订购零碎子域的剖析,服务器后盾应用用户服务,资源服务,订单服务和领取服务四个微服务实现。上图中的 API Gateway 也是一种服务,同时能够看成是 DDD 中的应用层,相似面向对象设计中的外观 (Facade) 模式。
作为整个后端架构的对立门面,封装了应用程序外部架构,负责业务用例的工作协调,每个用例对应了一个服务办法,调用多个微服务并将聚合后果返回给客户端。它还可能有其余职责,比方身份验证,拜访受权,缓存,速率限度等。以查问已购资源为例,API Gateway 须要查问订单服务获取以后用户已购的资源 ID 列表,而后依据资源 ID 列表查问资源服务获取已购资源的详细信息,最终将聚合后果返回给客户端。
当然在理论利用的过程中,咱们也能够依据 API 申请的复杂度,从业务角度,将 API Gateway 划分为多个不同的服务,避免又回归到 API Gateway 的单体瓶颈。
另外,有时候从业务畛域角度划分进去的某些子域比拟小,从资源利用率的角度,独自放到一个微服务中有点薄弱。这个时候咱们能够突破一个限界上下文对应一个微服务的理念,将多个子域合并到同一个微服务中,由微服务本人的应用层实现多子域工作的协调。
所以,在咱们的零碎架构中可能会呈现微服务级别的小应用层和 API Gateway 级别的大应用层应用场景,实践诚然是实践,还是须要结合实际状况灵便利用。
三、畛域驱动概念在单个微服务设计中的利用
1.架构抉择剖析
分层架构图(援用自互联网)
六边形架构图(援用自互联网)
整洁架构图(援用自互联网)
下面整洁架构图中的同心圆别离代表了软件系统中的不同档次,通常越凑近核心,其所在的软件档次就越高。
整洁架构的依赖关系规定通知咱们,源码中的依赖关系必须只指向同心圆的内层,即由低层机制指向高层策略。换句话说,任何属于内层圆中的代码都不应该关涉外层圆中的代码,尤其是内层圆中的代码不应该援用外层圆中代码所申明的名字,包含函数、类、变量以及所有其余有命名的软件实体。同样,外层圆应用的数据格式也不应该被内层圆中的代码所应用,尤其是当数据格式由外层圆的框架所生成时。
总之,不应该让外层圆中产生的任何变更影响到内层圆的代码。业务实体这一层封装的是整个业务畛域中最通用、最高层的业务逻辑,它们应该属于零碎中最不容易受外界影响而变动的局部,也就是说个别状况下咱们的外围畛域模型局部是比较稳定的,不应该因为外层的基础设施比方数据存储技术选型的变动,或者 UI 展现形式等的变动受影响,从而须要做相应的改变。
在以往的我的项目教训中,大多数同学习惯也比拟相熟分层架构,个别包含展现层、应用层,畛域层和基础设施层。六边形架构的一个重要益处是它将业务逻辑与适配器中蕴含的表示层和数据拜访层的逻辑拆散开来,业务逻辑不依赖于表示层逻辑或数据拜访层逻辑,因为这种拆散,独自测试业务逻辑要容易得多。
另一个益处是,能够通过多个适配器调用业务逻辑,每个适配器实现特定的 API 或用户界面。业务逻辑还能够调用多个适配器,每个适配器调用不同的内部零碎。所以六边形架构是形容微服务架构中每个服务的架构的好办法。
依据咱们具体的实践经验,比方在咱们平时的我的项目中最常见的就是 MySQL 和 Redis 存储,而且也很少扭转为其余存储构造。这里将分层架构和六边形架构进行思维交融,目标是一方面心愿咱们的微服务设计构造更柔美,另一方面心愿在已有编程习惯的根底上,更容易接受新的整洁架构思维。
咱们我的项目中微服务的实现联合分层架构,六边形架构和整洁架构的思维,以理论应用场景为背景,采纳的应用程序结构图如下。
从上图能够看到,咱们一个利用总共蕴含应用层 application,畛域层 domain 和基础设施层 infrastructure。畛域服务的 facade 接口须要裸露给其余三方零碎,所以独自封装为一个模块。因为咱们个别习惯于分层架构模式构建零碎,所以依照分层架构给各层命名。
站在六边形架构的角度,应用层 application 等同于入站适配器,基础设施层 infrastructure 等同于出站适配器,所以实际上应用层和基础设施层同属外层,能够认为在同一层。
facade 模块其实是从畛域层 domain 剥离进去的,站在整洁架构的角度,畛域层就是内核业务实体,这里封装的是整个业务畛域中最通用、最高层的业务逻辑,个别状况下外围畛域模型局部是比较稳定的,不受外界影响而变动。facade 是微服务裸露给外界的畛域服务能力,个别状况下接口的设定应合乎以后畛域服务的边界界定,所以 facade 模块属于内核畛域层。
facade 接口的实现在应用层 application 的 impl 局部,合乎整洁架构外层依赖内层的思维,对于 impl 输出端口和入站适配器,能够采纳不同的协定和技术框架实现,比方 dubbo 或 HSF 等。上面对各个模块的形成进行逐个解释。
2. 畛域层 Domain
工厂 Factory
对象的创立自身是一个次要操作,但被创立的对象并不适宜承当简单的拆卸操作。将这些职责混在一起可能会产生难以了解的高明设计。让客户间接负责创建对象又会使客户的设计陷入凌乱,并且毁坏拆卸对象的封装,而且导致客户与被创建对象的实现之间产生过于严密的耦合。
简单对象的创立是畛域层的职责,但这项工作并不属于那些用于示意模型的对象。所以个别应用一个独自的工厂类或者在畛域服务中提供一个结构畛域对象的接口来负责畛域对象的创立。
这里,咱们抉择给畛域服务减少一个畛域对象创立接口来承当工厂的角色。
/**
* description: 资源畛域服务
*
* @author Gao Ju
* @date 2020/7/27
*/
public class ResourceServiceImpl implements ResourceService {
/**
* 创立资源聚合模型
*
* @param resourceCreateCommand 创立资源命令
* @return
*/
@Override
public ResourceModel createResourceModel(ResourceCreateCommand resourceCreateCommand) {ResourceModel resourceModel = new ResourceModel();
Long resId = SequenceUtil.generateUuid();
resourceModel.setResId(resId);
resourceModel.setName(resourceCreateCommand .getName());
resourceModel.setAuthor(resourceCreateCommand .getAuthor());
List<PackageItem> packageItemList = new ArrayList<>();
...
resourceModel.setPackageItemList(packageItemList);
return resourceModel;
}
}
资源库 Repository
通常将聚合实例寄存在资源库中,之后再通过该资源库来获取雷同的实例。
如果批改了某个聚合,那么这种扭转将被资源库长久化,如果从资源库中移除了某个实例,则将无奈从资源库中从新获取该实例。
资源库是针对聚合维度创立的,聚合类型与资源库存在一对一的关系。
简略来说,资源库是对聚合的 CRUD 操作的封装。资源库外部采纳哪种存储设施 MySQL,MongoDB 或者 Redis 等,对畛域层来说其实是不感知的。
资源 repository 形成图
在咱们的我的项目中采纳 MySQL 作为资源 repository 的长久化存储,上图中每个 DO 对应一个数据库表,当然你也能够采纳其余存储构造或设计为其余表构造,具体的解决流程均由 repository 进行封装,对畛域服务来说只感知 Resource 聚合维度的 CRUD 操作,示例代码如下。
/**
* description: 资源仓储
*
* @author Gao Ju
* @date 2020/08/23
*/
@Repository("resourceRepository")
public class ResourceRepositoryImpl implements ResourceRepository {
/**
* 资源 Mapper
*/
@Resource
private ResourceMapper resourceMapper;
/**
* 资源包 Mapper
*/
@Resource
private PackageMapper packageMapper;
/**
* 资源包预览图 Mapper
*/
@Resource
private PackagePreviewMapper packagePreviewMapper;
/**
* 创立订单信息
*
* @param resourceModel 资源聚合模型
* @return
*/
@Override
public void add(ResourceModel resourceModel) {ResourceDO resourceDO = new ResourceDO();
resourceDO.setName(resourceModel.getName());
resourceDO.setAuthor(resourceModel.getAuthor());
List<PackageDO> packageDOList = new ArrayList<>();
List<PackagePreviewDO> packagePreviewDOList = new ArrayList<>();
for (PackageItem packageItem : resourceModel.getPackageItemList()) {PackageDO packageDO = new PackageDO();
packageDO.setResId(resourceModel.getResId());
Long packageId = SequenceUtil.generateUuid();
packageDO.setPackageId(packageId);
for (PreviewItem previewItem: packageItem.getPreviewItemList()) {PackagePreviewDO packagePreviewDO = new PackagePreviewDO();
...
packagePreviewDOList.add(packagePreviewDO);
}
packageDOList.add(packageDO);
}
resourceMapper.insert(resourceDO);
packageMapper.insertBatch(packageDOList);
packagePreviewMapper.insertBatch(packagePreviewDOList);
}
}
你可能有疑难,依照整洁架构的思维,repository 的接口定义在畛域层,repository 的实现应该定义在基础设施层,这样就合乎外层依赖稳定度较高的内层了。
联合咱们理论开发过程,个别存储构造选定或者表构造设定后,个别不太容易做很大的调整,所以就依照习惯的分层构造应用,畛域层间接依赖基础设施层实现,升高编码时带来的额定习惯上的老本。
畛域服务 Service
畛域驱动强调咱们应该创立充血畛域模型,将数据和行为封装在一起,将畛域模型与事实世界中的业务对象相映射。各类具备明确的职责划分,将畛域逻辑扩散到各个领域对象中。
畛域中的服务示意一个无状态的操作,它用于实现特定于某个畛域的工作。当某个操作不适宜放在畛域对象上时,最好的形式是应用畛域服务。
简略总结畛域服务自身所承载的职责,就是通过串联畛域对象、资源库,生成并公布畛域事件,执行事务管制等一系列畛域内的对象的行为,为下层应用层提供交互的接口。
/**
* description: 订单畛域服务
*
* @author Gao Ju
* @date 2020/8/24
*/
public class UserOrderServiceImpl implements UserOrderService {
/**
* 订单仓储
*/
@Autowired
private OrderRepository orderRepository;
/**
* 音讯公布器
*/
@Autowired
private MessagePublisher messagePublisher;
/**
* 订单逻辑解决
*
* @param userOrder 用户订单
*/
@Override
public void createOrder(UserOrder userOrder) {orderRepository.add(userOrder);
OrderCreatedEvent orderCreatedEvent = new OrderCreatedEvent();
orderCreatedEvent.setUserId(userOrder.getUserId());
orderCreatedEvent.setOrderId(userOrder.getOrderId());
orderCreatedEvent.setPayPrice(userOrder.getPayPrice());
messagePublisher.send(orderCreatedEvent);
}
}
在实际的过程中,为了简略不便,咱们依然采纳贫血畛域模型,将畛域对象本身行为和不属于畛域对象的行为都放在畛域服务中实现。
大部分场景畛域服务返回聚合根或者简略类型,某些非凡场景也能够将聚合根中蕴含的实体或值对象返回给调用方。畛域服务也能够同时操作多个畛域对象,多个聚合,将其转换为另外的输入。
介于咱们理论的应用场景,畛域比较简单,畛域服务只操作一个畛域的对象,只操作一个聚合,由应用服务来协调多个畛域对象。
3. 畛域事件 DomainEvent
在畛域驱动设计的上下文中,聚合在被创立时,或产生其余重大更改时公布畛域事件,畛域事件是聚合状态更改时所触发的。
畛域事件命名时,个别抉择动词的过去分词,因为状态扭转时就代表以后事件曾经产生,畛域事件的每个属性都是原始类型值或值对象,比方事件 ID 和创立工夫等,事件 ID 也能够用来做幂等用。
从概念上讲,畛域事件由聚合负责公布,聚合晓得其状态何时发生变化,从而晓得要公布的事件。
因为聚合不能应用依赖注入,须要通过办法参数的模式将音讯公布器传递给聚合,但这将基础设施和业务逻辑交错在一起,有悖于咱们解耦设计的准则。
更好的办法是将事件公布放到畛域服务中,因为服务能够应用依赖注入来获取对音讯公布器的援用,从而轻松公布事件。只有状态发生变化,聚合就会生成事件,聚合办法的返回值中包含一个事件列表,并将它们返回给畛域服务。
Saga 是一种在微服务架构中保护数据一致性的机制,Sage 由一连串的本地事务组成,每一个本地事务负责更新它所在服务的公有数据库,通过异步音讯的形式来协调一系列本地事务,从而保护多个服务之间数据的最终一致性。Saga 包含协同式和编排式,
咱们采纳协同式来实现分布式事务,公布的畛域事件以命令式音讯的形式发送给 Saga 参与方。如果畛域事件是自我公布自我生产,不依赖消息中间件实现,则能够应用事件总线模式来进行治理。上面以购买资源的过程为例进行阐明。
购买资源的过程
- 提交创立订单申请,OrderService 创立一个处于 PAYING 状态的 UserOrder,并公布 OrderCreated 事件。
- UserService 生产 OrderCreated 事件,验证用户是否能够下单,并公布 UserVerified 事件。
- PaymentService 生产 UserVerified 事件,进行理论的领取操作,并公布 PaySuccess 事件。
- OrderService 接管 PaySuccess 事件,将 UserOrder 状态改为 PAY_SUCCESS。
弥补过程
- PaymentService 生产 UserVerified 事件,进行理论的领取操作,若领取失败,并公布 PayFailed 事件。
- OrderService 接管 PayFailed 事件,将 UserOrder 状态改为 PAY_FAILED。
在 Saga 的概念中,
第 1 步叫可补偿性事务,因为前面的步骤可能会失败。
第 3 步叫关键性事务,因为它前面跟着不可能失败的步骤。第 4 步叫可重复性事务,因为其总是会胜利。
/**
* description: 畛域事件基类
*
* @author Gao Ju
* @date 2020/7/27
*/
public class BaseEvent {
/**
* 音讯惟一 ID
*/
private String messageId;
/**
* 事件类型
*/
private Integer eventType;
/**
* 事件创立工夫
*/
private Date createTime;
/**
* 事件批改工夫
*/
private Date modifiedTime;
}
/**
* description: 订单创立事件
*
* @author Gao Ju
* @date 2020/8/24
*/
public class OrderCreatedEvent extends BaseEvent {
/**
* 用户 ID
*/
private String userId;
/**
* 订单 ID
*/
private String orderId;
/**
* 领取价格
*/
private Integer payPrice;
}
4.Facade 模块
facade 和 domain 属于同一层,某些提供给三方应用的类定义在 facade,比方资源类型枚举 CategoryEnum 限度三方资源应用范畴,而后 domain 依赖 facade 中 enum 定义。
另外,依据迪米特法令和通知而非询问准则,客户端应该尽量少地晓得服务对象内部结构,通过调用服务对象的公共接口的形式来通知服务对象所要执行的操作。
所以,咱们不应该把畛域模型泄露到微服务之外,对外提供 facade 服务时,依据畛域对象包装出一个数据传输对象 DTO(Data Transfer Object),来实现和内部三方零碎的交互,比方上图中的 ResourceDTO。
5.应用层 Application
应用层是业务逻辑的入口,由入站适配器调用。facade 的实现,定时工作的执行和音讯监听处理器都属于入站适配器,所以他们都位于应用层。
失常状况下一个微服务对应一个聚合,实际过程中,某些场景下一个微服务能够蕴含多个聚合,应用层负责用例流的工作协调。畛域服务依赖注入应用层,通过畛域服务执行畛域业务规定,应用层还会解决受权认证,缓存,DTO 与畛域对象之间的防腐层转换等非畛域操作。
/**
* description: 订单 facade
*
* @author Gao Ju
* @date 2020/8/24
*/
public class UserOrderFacadeImpl implements UserOrderFacade {
/**
* 订单服务
*/
@Resource
private UserOrderService userOrderService;
/**
* 创立订单信息
*
* @param orderPurchaseParam 订单交易参数
* @return
*/
@Override
public FacadeResponse<UserOrderPurchase> createOrder(OrderPurchaseParam orderPurchaseParam) {UserOrder userOrder = new UserOrder();
userOrder.setUserId(request.getUserId());
userOrder.setResId(request.getResId());
userOrder.setPayPrice(request.getPayAmount());
userOrder.setOrderStatus(OrderStatusEnum.Create.getCode());
userOrderService.handleOrder(userOrder);
userOrderPurchase.setOrderId(userOrderDO.getId());
userOrderPurchase.setCreateTime(new Date());
return FacadeResponseFactory.getSuccessInstance(userOrderPurchase);
}
}
6. 基础设施层 Infrastructure
基础设施的职责是为应用程序的其余局部提供技术支持。与数据库的交互 dao 模块,与 Redis 缓存,本地缓存交互的 cache 模块,与参数核心,三方 rpc 服务的交互,音讯框架音讯发布者都封装在基础设施层。
另外,程序中用到的工具类 util 模块和异样类 exception 也对立封装在基础设施层。
从分层架构的角度,畛域层能够依赖基础设施层实现与其余外设的交互。另外,无论从分层架构的下层 application 层还是从六边形架构的角度的输出端口和适配器 application,都能够依赖作为底层或处于同层的输入端口和适配器的 infrastructure 层,比方调用 util 或者 exception 模块。
四、结束语
其实,无论是面向服务架构 SOA,微服务,畛域驱动,还是中台,其目标都是在说,咱们做架构设计的时候,应该从业务视角登程,对所波及的业务畛域,基于高内聚、低耦合的思维进行划分,最大限度且正当的实现业务重用。
这样不仅不便提供业余且稳固的业务服务,更有利于业务的积淀和可继续倒退。业务之下是基于技术的零碎实现,技术造就业务,业务引领技术,两者相辅相成,独特为社会提高做出奉献。
五、参考文献
- [1]《畛域驱动设计软件外围复杂性应答之道》Eric Evans 著, 赵俐 盛海燕 刘霞等译,人民邮电出版社
- [2]《实现畛域驱动设计》Vaughn Vernon 著, 滕云译, 张逸审,电子工业出版社
- [3]《微服务架构设计模式》[美]克里斯. 理查森(Chris Richardson) 著, 喻勇译,机械工业出版社
- [4]《架构整洁之道》[美]Robert C.Martin 著,孙宇聪 译,电子工业出版社
- [5]《企业 IT 架构转型之道阿里巴巴中台战略思想与架构实际》钟华编著,机械工业出版社
- [6]畛域驱动设计 (DDD) 实际之路(二):事件驱动与 CQRS,vivo 互联网技术
- [7]畛域驱动设计在互联网业务开发中的实际,美团技术团队
作者:Angel Gao