微服务这种架构风格迅速地成为软件行业的热词,并被许多互联网公司采纳。如今微服务已经进入所谓的Service Mesh 2.0时代,诸多微服务框架如雨后春笋般出现。由于微服务是业务而非技术在驱动,随之开始重视领域驱动设计(DDD),并尝试将领域驱动设计引入到微服务架构设计中。而微服务的完整定义来自Martin Fowler的文章《MicroServices》:
- 每个服务运行在自己的进程中;微服务的物理边界不一定等于网络边界,最小的物理边界变成了“进程”
- 微服务之间采用轻量级通信;它认为是基于HTTP协议的资源API,通常指的是RESTful API。不过随着类似Netty、gRPC等RPC框架、PB序列化协议以及Kafka等消息中间件的广泛运用,在生产应用中,微服务之间的通信也不仅限于RESTful。
- 微服务应基于业务能力进行构建;
- 采用自动化部署机制实现微服务的独立部署;
- 服务的管理应采用最小的中心化管理。
总之,微服务首先是业务手段,然后才是技术手段。
那究竟该如何如何从单体架构演进到微服务架构?
其实有一篇非常棒的文章How to break a Monolith into Microservices,他从微服务理念“通过业务能力组织服务”,并从这几个方面着手:
1.降低对单体系统的依赖
需要引入领域驱动设计介绍的防腐层(即在单体系统中定义一个新的API),目的是避免单体系统的代码实现直接泄漏给微服务和便于在未来用新的微服务实现替换单体系统的实现
2. 优先分离黏合逻辑
单体架构为人诟病的一点在于它太容易随着时间推移而变成一个高度耦合的“毛线球”。各个功能纠缠在一起,没有体现出清晰的领域逻辑,这会给微服务拆分带来很大的障碍。Dehghani建议优先识别出单体系统中的这些黏合逻辑(Sticky Capabilities,其实它是并非系统的核心业务能力,而是一种近乎于横切关注点的功能),分解它们。
3.纵向解耦并尽早发布数据
所谓的纵向解耦就是从客户端发起调用的服务API到数据库进行“一刀切”,Dehghani认为从微服务的“去中心化数据管理(Decentralized Data Management)”特征看,应尽早解耦数据库。
4.解耦核心领域和变化频繁的领域
为什么要演进到微服务?不能为了拆分而拆分,而应该在拆分微服务时,随时权衡拆分的成本与收益。
因为核心领域的价值要远超其他子领域,因为变化频繁的领域在单体架构中的维护成本太高。前者是因为拆分后的收益高,后者是因为不拆分带来的成本高。
要应用这一原则,可以引入领域驱动设计的战略设计,通过识别系统的核心领域与子领域,由此确定提取微服务的边界和目标。领域是否为核心领域,与痛点和价值有关,这需要结合客户的愿景、目标和经营模式等因素综合考量。例如在文中,Dehghani认为“客户个性化”是核心领域,因为它能够为客户提供更好的用户体验,有助于提高客户的黏度。
5. 依据能力而非代码去解耦
当开发人员从已有系统中提取服务时,无非两种形式:提取代码(重用现有实现)或重写能力。
重新审视这二者带来的成本和价值。重写未必代价高昂,毕竟我们可以抛开过去的技术债务轻装前行,正所谓“无债一身轻”嘛。所以Dehghani给出了一个原则:重用和提取高价值低毒性的代码,重写和废弃低价值高毒性的代码。什么是代码毒性(code toxicity)?我想,应该就是Martin Fowler在《重构》一书中提及的代码坏味道。若需评估代码毒性的高低,可以使用CheckStyle等工具。
6. 先宏观再微观
微服务边界的常见方法是运用领域驱动设计的限界上下文(Bounded Context)。过于微小的服务既会带来服务的“大爆炸”,还会出现大量只有CRUD操作的贫血服务(anemic service)。
微服务到底有多“微”,这是一个问题。衡量的指标包括团队的规模、重写一个服务花费的时间、服务封装了多少行为。但这些指标并无客观的量化值,同时,还得取决于这个系统自身的规模与业务复杂性。Dehghani给出的建议:先从更大的服务开始,直到微服务的基础条件都满足了,再寻求对大的服务进行拆分。
7.架构演化的原子步伐迁移
重构教导我们要“小步前行”,但真正的意图是要保持重构步伐的原子性(atomic),这样就能保证进可攻退可守的态势。而原子性就应该保证演进的功能是最小粒度的完整,且不允许新旧代码同时存在。因为同时维护两条代码分支(新旧版本),增加了开发、测试和维护的成本,违背了微服务架构的目标(提高开发人员修改整个系统的速度,快速交付价值)。
总结
领域驱动设计对我们实施微服务非常重要,它主要表现2方面:
- 战略层面,领域驱动设计指导了微服务设计,微服务架构影响了领域驱动设计
- 战术层面,二者没有任何关系,但DDD可以是微服务的其中一种实现
微服务强调服务的独立部署,因此微服务的引入重新定义了Bounded Context的边界,服务之间的通信也突破了Context Map的集成模式。