何时使用领域驱动设计 (6)

在最开始着手软件系统的设计时,你或许不会选择微服务架构,因为在那个时候,微服务架构并不能帮你解决眼前的设计问题。但是当你的业务领域变得十分庞大,而分层架构无法继续支撑你的软件系统时,你可能会考虑采用微服务架构。在微服务架构中,各应用服务之间互相独立,它们可以由不同团队采用异构的平台和技术,以及使用不同的软件开发方法完成开发,这些服务可以使用不同的数据存储系统,甚至可以是一个仅进行数据实时处理而不存储任何数据的计算服务,微服务实例之间可以以同步或者异步的方式进行通讯。不难看出,实践微服务架构的一个难点就是如何去协调各个服务之间的协作,例如如何在分布式的环境中保证数据的一致性;然而,当你真的决定采用微服务架构时,你所遇到的第一个问题就是:如何划分微服务的边界。 在微服务架构的官方网站上给出了四种将应用程序解构成多个微服务的模式:Decompose by business capability、Decompose by subdomain、Self-contained service以及Service per team。其中与领域驱动设计所对应的模式就是Decompose by subdomain,它要求设计者能够根据软件系统的业务领域来区分子领域,然后应用相关模式来确定微服务的划分,大致流程如下:

对业务领域进行分析,通过通用语言来描述业务领域中的关键概念和业务行为,并确定整个大的业务领域由哪些子领域(subdomain)构成

根据这些子领域来确定界定上下文(Bounded Context),每一个界定上下文会有一套独立的领域模型对子领域进行描述,界定上下文中的领域模型不会存在二义性

在界定上下文中建模,设计好领域模型以及各领域对象之间的关系

基于建立好的领域模型,划分微服务

在领域驱动设计中,界定上下文(有些文章将其翻译为“有界上下文”,意思相同)是实现通用语言的重要工具,很多情况下,有些词语或者句子在不同的上下文中会有不同的含义,界定上下文就定义了这样一个边界,它能使得在边界内的词语或者句子具有唯一明确的含义而不存在二义性。例如某公司生产产品然后卖给客户(Customer),然后会有另一个团队为这些客户(Customer)提供售后服务或技术支持。那么在这里我们有两个“客户”的概念,对于整个公司来说,它们表示的是同一个概念,然而在不同的上下文中,这个“客户”的概念又有所不同:在销售子领域中,“客户”表示产品销售的对象,因此会更多地关注它对产品的需求以及信用额度、交货方式等等;而在售后服务子领域中,“客户”表示提供服务的对象,因此会更多关注它的历史订单信息以及历史服务工单。从上面的基于Decompose by subdomain的基本流程来看,一旦区分并确定了整个领域中的界定上下文,也就基本上确定了应用系统中大致会有哪些微服务。 从领域模型上分析,界定上下文也不是绝对独立的,应该说绝大多数情况下不是。领域驱动设计引入了“上下文映射(Context Mapping)”来解决跨界定上下文的领域知识的交互。常用的方式可以是使负责不同子领域的团队之间达成共识、通过抽象手段来建立跨多个界定上下文的公共模型(Shared Kernel),或者引入防腐层(Anti-corruption Layer)来达到不同界定上下文之间无缝沟通的目的。这篇文章很好地介绍了这些内容。 这里限于文章篇幅,我仅仅简单地介绍了与领域驱动设计相关的要点,上面讨论的内容中的每一个点都可以继续展开讨论继续分析研究。你是不是已经开始考虑是否真的需要微服务架构了吧?因为是否采用微服务架构风格,以及微服务如何划分,将直接影响到今后你的业务系统的开发和演进是否真的能够帮你解决庞大的业务领域体量所带来的软件开发问题,而不是让你的架构变得逐渐臃肿不堪错误百出难以维护,给你带来无穷无尽的烦恼。 或许你的应用系统并没有那么大的业务领域体量,你也已经将你的业务领域划分成了多个微服务,那么接下来就是开发技术以及开发流程和团队管理的问题了。微服务架构真的有很多优点:由于整个业务领域被划分成多个子领域,由不同的微服务实现,因此这种架构风格具有非常好的延展性,并且可以根据需要来动态调配各个服务的运行资源。另一方面,在微服务架构中,通常都会由不同的团队来负责各个微服务的开发,这些团队可以选择合适的技术,采用自己的代码托管与分支策略,使用不同的软件开发过程来开展开发任务。如果团队采用敏捷开发过程,那么一个相对较小的团队能够更加高效地实践敏捷,使得微服务的开发能够不断向前迭代。微服务架构的另一个优点就是对于云平台的支持,虽然各个服务会采用不同技术运行在不同平台上,然而现在流行的容器化技术可以屏蔽这种应用层技术实现的差异,通过将各个服务封装成容器,使得整个应用系统可以非常方便地部署到云平台,并且非常方便地调用托管的云服务。 由于这种架构上的灵活性和分布式的特点,微服务架构也存在很多挑战:配置管理、服务发现、服务间通信、分布式事务(数据最终一致性的保证)、部署和测试复杂度、安全策略的实现等等,每一个技术难点都有可能成为你成功实践微服务架构的阻力。例如,异步通信是微服务间最为常见的通信机制之一,而大多数情况下,分布式事务就需要依赖于这种异步通信机制,而它一般都是基于事件消息的,所以,除了基本的事件消息框架的实现之外,各个微服务还需要考虑如何参与到这种分布式事务之中:如何在事务成功的时候提交变更,以及如何在事务失败的时候进行补偿操作。Saga体系结构模式就是一种实现跨服务事务的模式,它有两种实现方式:编排式协调式,前者通过微服务之间互通领域事件来实现事务,而后者则是由一个中心化的协调器来接收来自各服务的领域事件,然后根据领域事件的处理结果来决定整个事务应该被接收还是被驳回。当某个事务参与的微服务比较少,并且处理逻辑不复杂的情况下,采用编排式的设计会比较简单;但如果参与的微服务和领域事件比较多,选择协调式的设计会使得结构更加清晰,而且不容易出错。目前有一些开发框架已经很好地实现了或者支持Saga模式,比如.NET下的NServiceBus框架,然而由于其过于复杂,学习成本比较高,因此应用范围也不是特别广。 值得一提的是,微服务架构之下各服务之间隔离度越高越好,虽然微服务架构本身并不强制要求每个服务都有自己的数据库,但是Database per service仍然是一个比较推荐的做法。前端的实现也是如此,开发团队可以有各自的前端开发人员来开发用于当前微服务的前端界面,然后通过某些微前端框架进行整合。 所以,微服务架构看上去比较先进、时尚,但是要想有效、正确地实践微服务架构却不是一件容易的事情。如果你的业务系统并没有大到需要拆分成多个子系统来进行设计,或者你的团队没有大到足以应对由这些微服务带来的技术复杂度,那么,你真的应该考虑一下,采用微服务的架构是否真的利大于弊。架构设计就是如此,没有对错,只有是否合理,整个过程就是平衡与取舍。 以下是微软官方的一个完整的微服务架构的案例:eShopOnContainers,代码开源,其业务领域是一个电商零售网站。它的架构图如下:

何时使用领域驱动设计

(图片来源:微软eShopOnContainers代码库,点击查看大图)

 

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zysxxd.html