如何构建基于 DDD 领域驱动的微服务?(1)

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
MSE Nacos/ZooKeeper 企业版试用,1600元额度,限量50份
注册配置 MSE Nacos/ZooKeeper,182元/月
简介: 如何构建基于 DDD 领域驱动的微服务?

尽管微服务中的“微”一词表示服务的规模,但它并不是使用微服务的唯一标准。当团队转向基于微服务的架构时,他们旨在提高敏捷性以及自主且频繁地部署功能。很难确定这种架构风格的简单定义。我喜欢Adrian Cockcroft的关于微服务的简短定义: “ 面向服务的体系结构,它由松散耦合的、具有上下文边界的元素组成。”


尽管这定义了高级设计启发式技术,但微服务架构具有一些独特的特性,使其有别于以往的面向服务的架构。以下是其中一些特征。这些以及其他一些文档都有据可查-Martin Fowler的文章和Sam Newman的Building Microservices,仅举几例。


  • 服务具有围绕业务上下文而不是任意技术上抽象的明确定义的边界
  • 通过意图公开界面隐藏实现细节并公开功能
  • 服务不会共享超出其边界的内部结构。例如,不共享数据库。
  • 服务可以抵抗故障。
  • 团队独立拥有职能,并能够自主发布变更
  • 团队拥护自动化文化。例如,自动化测试,持续集成和持续交付


简而言之,我们可以将这种体系结构样式总结如下:


松耦合的面向服务的体系结构,其中每个服务都包含在定义明确的有界上下文中,从而可以快速,频繁且可靠地交付应用程序。


领域驱动设计和有界上下文


微服务的力量来自明确定义其职责并划分它们之间的边界。此处的目的是在边界内建立高凝聚力,并在边界外建立低耦合(banq注:高凝聚低耦合)。也就是说,趋于一起改变的事物应该属于同一事物。就像在许多现实生活中的问题一样,这说起来容易做起来难,业务在不断发展,逻辑的假设前提也会随之改变。因此,重构的能力是设计系统时要考虑的另一关键问题。


领域驱动设计(DDD)是关键,在我们看来,这是设计微服务时必不可少的工具,无论是打破整体还是实施未开发项目。领域驱动的设计(Eric Evans在他的书中提出)是一组思想、原理和模式,可帮助基于业务领域的基础模型设计软件系统。开发人员和领域专家共同合作,以通用的通用语言创建业务模型。然后,他们将这些模型绑定到有意义的系统,并在这些系统与从事这些服务的团队之间建立协作协议。更重要的是,他们设计了系统之间的概念轮廓或边界。


微服务设计从这些概念中汲取了灵感,因为所有这些原理都有助于构建可以相互独立变化和发展的模块化系统。


在继续进行之前,让我们快速了解一下DDD的一些基本术语。域驱动设计的完整概述超出了本博客的范围。我们强烈建议任何尝试构建微服务的人推荐Eric Evans的书籍。


领域:代表组织的工作。例如它是零售或电子商务。


子域:组织或组织内的业务部门。域由多个子域组成。


无所不在的语言:这是用于表达模型的语言。在下面的示例中,Item是一个模型,属于这些子域中每个子域的通用语言。开发人员,产品经理,领域专家和业务涉众都同意使用相同的语言,并在其工件(代码,产品文档等)中使用该语言。


有界上下文:域驱动的设计将有界上下文定义为“单词或语句能确定其含义的设置”。简而言之,这意味着模型是有意义的边界。在上面的示例中,“项目”在每种上下文中的含义不同。在目录上下文中,项目表示可售产品,而在购物车上下文中,则表示客户已将其添加到购物车中的项目。在“运输”上下文中,它表示将要运送给客户的仓库物料。这些模型中的每一个都是不同的,并且每个都有不同的含义,并且可能包含不同的属性。通过将这些模型分离并隔离在它们各自的边界内,我们可以自由地表达模型而没有歧义。


注意:必须了解子域和有界上下文之间的区别。子域属于问题空间,即您的企业如何看待问题,而受限上下文属于解决方案空间,即我们将如何实施问题的解决方案。从理论上讲,每个子域可能具有多个有界上下文,尽管我们努力为每个子域提供一个有界上下文。


微服务与有限上下文如何相关


现在,微服务在哪里适合?可以说每个有界上下文都映射到微服务吗?是的,我们将明白为什么。在某些情况下,有界上下文的边界或轮廓可能很大。


image.png


考虑上面的例子。定价绑定上下文具有三个不同的模型-价格,定价项目和折扣,每个模型负责目录项目的价格,计算项目列表的总价格并分别应用折扣。我们可以创建一个包含以上所有模型的系统,但是它可能会成为一个不合理的大型应用程序。如前所述,每个数据模型都有其不变性和业务规则。随着时间的流逝,如果我们不小心的话,系统可能会变成一个泥泞的大球,边界模糊,职责重叠,甚至可能回到我们开始的地方—一个整体。


对系统进行建模的另一种方法是将相关模型分离或分组为单独的微服务。在DDD中,这些模型(价格,定价项目和折扣)被称为聚合Aggregates。聚合是组成相关模型的独立模型。您只能通过已发布的界面更改聚合的状态,并且聚合可确保一致性,并且不变量保持良好状态。


聚合是关联对象的集群,被视为数据更改的单元。外部引用仅限于AGGREGATE的一个成员,称为根。一组一致性规则适用于AGGREGATE的边界。


image.png


同样,没有必要一定要将每个聚合建模为一个独特的微服务。图中的服务(聚合)是一对一关系,但这不一定是规则。在某些情况下,在单个服务中托管多个聚合可能是有意义的,尤其是当我们不完全了解业务领域时。需要注意的重要一点是,只能在单个聚合中保证一致性,并且只能通过已发布的界面修改聚合。任何违反这些规定的行为都有变成大泥球的风险。


上下文映射—精确划分微服务边界的一种方法


整体结构通常由不同的模型组成,大多数模型是紧密耦合的-模型可能知道彼此的亲密细节,更改一个模型可能会对另一个模型产生副作用,依此类推。分解整体时,确定这些模型(在这种情况下为集合)及其关系至关重要。上下文映射可以帮助我们做到这一点。它们用于标识和定义各种有界上下文和聚合之间的关系。在上面的示例中,有界上下文定义了模型的边界(价格,折扣等),而上下文映射定义了这些模型之间以及不同有界上下文之间的关系。


上下文映射的完整探索不在本博客的讨论范围之内,但我们将通过一个示例进行说明。下图显示了处理电子商务订单付款的各种应用程序。


购物车上下文负责订单的在线授权;订单上下文流程过帐付款后的付款流程;联络中心会处理所有例外情况,例如重试付款和更改用于订单的付款方式

为了简单起见,让我们假设所有这些上下文都是作为单独的服务实现的

所有这些上下文封装了相同的模型。

请注意,这些模型在逻辑上是相同的。也就是说,它们都遵循相同的通用域语言-付款方式,授权和结算。只是它们是不同上下文的一部分。

重新定义服务边界—将聚合映射到正确的上下文


错误案例如下图:


image.png


电子商务中所有模型都直接与单个支付聚合的网关上下文(payment gateway context)集成,支付需要保证事务性,但是由于与多个服务集成,支付的事务性就不能通过在各种服务之间强制执行不变性和一致性来实现,(banq注:当然有人就提出分布式事务的概念来在这些不同服务之间实现支付过程的事务性,这其实是在错误设计基础上的伪概念)。


请注意,支付网关中的任何更改都将迫使更改多个服务,并可能更改多个团队,因为不同的组可以拥有这些上下文。


进行一些调整并使聚合与正确的上下文对齐,我们可以更好地表示这些子域如下图。发生了很多变化。让我们回顾一下更改:


image.png


通常,整体(单体)或遗留应用程序具有许多聚合,通常具有重叠的边界。创建这些聚合及其依赖关系的上下文地图有助于我们了解将要从这些整体中摆脱出来的任何新微服务的轮廓。请记住,微服务架构的成功或失败取决于聚合之间的低耦合以及这些聚合之间的高内聚性。


同样重要的是要注意,有界上下文本身就是合适的内聚单元。即使上下文具有多个聚合,整个上下文及其聚合也可以组成一个微服务。我们发现这种启发式方法对于有些晦涩的领域特别有用-考虑组织正在涉足的新业务领域。您可能对分离的正确边界没有足够的了解,并且聚集体的任何过早分解都可能导致昂贵的重构。想象一下,由于数据迁移,不得不将两个数据库合并为一个,因为我们偶然发现两个聚合属于同一类。但是请确保通过接口将这些聚合充分隔离,以使它们不知道彼此的复杂细节。



相关文章
|
存储 前端开发 API
DDD领域驱动设计实战-分层架构
DDD分层架构通过明确各层职责及交互规则,有效降低了层间依赖。其基本原则是每层仅与下方层耦合,分为严格和松散两种形式。架构演进包括传统四层架构与改良版四层架构,后者采用依赖反转设计原则优化基础设施层位置。各层职责分明:用户接口层处理显示与请求;应用层负责服务编排与组合;领域层实现业务逻辑;基础层提供技术基础服务。通过合理设计聚合与依赖关系,DDD支持微服务架构灵活演进,提升系统适应性和可维护性。
|
6月前
|
缓存 Java Spring
IDEA中配置HTML和Thymeleaf热部署的步骤
以上就是在IntelliJ IDEA中配置HTML和Thymeleaf热部署的步骤。这样的配置可以大大提高你的开发效率,让你更专注于代码的编写,而不是等待应用的重启。希望这个指南对你有所帮助!
362 21
|
6月前
|
缓存 安全 测试技术
|
11月前
|
消息中间件 弹性计算 运维
一图看懂云消息队列 RabbitMQ 版对比开源优势
一张图带您快速了解云消息队列 RabbitMQ 版对比开源版本的显著优势。
177 81
|
11月前
|
前端开发 测试技术 数据库
DDD架构中assembler和converter的区别
在 DDD 四层架构模式中,assembler 和 converter 常用于对象转换,但两者在实际项目中的使用较为随意。本文从英文释义、语义区分和模型层区分三个方面探讨了两者的区别,建议按模型层区分,即 Interface 和 Application 层使用 assembler,Infrastructure 层使用 converter,以避免混淆和随意使用。此外,将转换代码抽离为独立方法有助于保持代码整洁和可测试性。
|
架构师
DDD建模系列(一)
DDD建模系列(一)
|
机器学习/深度学习 数据采集 人工智能
数据工作中的自动化与AI融合实践
【8月更文第13天】随着大数据和人工智能(AI)技术的发展,数据处理和分析变得越来越重要。本文将探讨如何通过自动化工具和AI技术来优化数据处理流程,包括数据清洗、特征工程、模型训练以及结果可视化等步骤。我们将使用Python编程语言及其相关库(如Pandas、Scikit-learn和TensorFlow)作为实现手段。
842 0
|
监控 NoSQL JavaScript
《Redis服务监控神器:用RedisLive轻松掌控实时数据》
【8月更文挑战第13天】确保Redis稳定运行可通过监控工具RedisLive实现,它实时监控服务状态以便及时处理问题。RedisLive是款开源工具,具备易用、实时与可视化特性,透过Web界面展示内存使用及命令统计等关键指标。使用前需确认已安装Redis、Node.js环境,并配置MongoDB用于数据持久化。安装步骤包括克隆项目、安装依赖、配置连接信息,最后启动应用并通过浏览器访问监控页面。
453 0
|
机器学习/深度学习 人工智能 安全
「随笔」开源大模型与闭源大模型,你更看好哪一方?
开源与闭源AI模型各有利弊。开源促进创新、透明度和学习,但可能有安全风险和质量不一;闭源则保护IP、提供定制服务,但可能限制创新和透明度。混合策略,如基础开源加高级服务闭源,成为平衡点。选择取决于创新、产权、透明度和商业目标。
571 0