【架构设计】DDD 到底解决了什么问题

news2024/11/28 6:50:51

文章目录

  • 前言
  • 一、架构设计是为了解决系统复杂度
    • 1.1 架构设计的误区
      • 1.1.1 每个系统都要做架构设计/公司流程要求有架构设计
      • 1.1.2 架构设计是为了追求高性能、高可用、可扩展性等单一目标
    • 1.2 架构设计的真正目的
    • 1.3 系统复杂度的六个来源及通用解法
      • 1.3.1 高性能
        • 1.3.1.1 单机复杂度
        • 1.3.1.2 集群复杂度
          • 1.3.1.2.1任务分配
          • 1.3.1.2.2 任务分解
      • 1.3.2 高可用
        • 1.3.2.1 计算高可用
        • 1.3.2.2 存储高可用
      • 1.3.3 可扩展性
        • 1.3.3.1 预测变化
        • 1.3.3.2 应对变化
          • 1.3.3.2.1 变化层VS稳定层
          • 1.3.3.2.2 抽象层VS实现层
      • 1.3.4 低成本
      • 1.3.5 安全
        • 1.3.5.1 功能安全
        • 1.3.5.2 架构安全
      • 1.3.6 规模
        • 1.3.6.1 功能越来越多,系统复杂度指数级上升
        • 1.3.6.2 数据越来越多,系统复杂度发生质变
    • 1.4 简单的复杂度分析案例
    • 1.5 总结
  • 二、微服务架构解决了高可用、可扩展问题,但性能下降、成本&规模复杂度暴增
    • 2.1 微服务架构的优点
      • 2.1.1 高可用
      • 2.1.2 可扩展
    • 2.2 微服务的缺点
      • 2.2.1 复杂度高
      • 2.2.2 基础设施成本呈指数级增长
      • 2.2.3 性能下降
  • 三、DDD帮助微服务控制规模复杂度
    • 3.1 控制成本&规模复杂度需要明晰微服务的边界
    • 3.2 DDD能够帮我们设计出清晰的领域和应用边界
    • 3.3 微服务与DDD的异同
  • 四、参考

前言

先放结论:DDD作为架构设计思想帮助微服务控制规模复杂度,那它是怎么做到的呢?

一、架构设计是为了解决系统复杂度

谈到架构,相信每个技术人员都是耳熟能详,但如果深入探讨一下,“为何要做架构设计?”或者“架构设计目的是什么?”类似的问题,大部分人可能从来没有思考过,或者即使有思考,也没有太明确可信的答案。

1.1 架构设计的误区

1.1.1 每个系统都要做架构设计/公司流程要求有架构设计

知其然更要知其所以然,不能仅仅因为其他公司都在做架构设计而盲目跟从,而应该深入理解架构设计的目的和必要性,根据实际需求进行合理的设计。如果架构师或设计师只是为了找点事做而进行架构设计,不仅会浪费时间和人力,还会拖慢整体开发进度。此外,其他公司的架构设计并不一定适用于当前项目,如果强行引入,很可能会导致架构水土不服、运行不流畅等问题,最终需要不断重构或者推倒重来。因此,架构师或设计师应该深刻理解为何要进行架构设计,避免生搬硬套,针对具体需求进行合理的设计,才能保证项目的顺利进行。

1.1.2 架构设计是为了追求高性能、高可用、可扩展性等单一目标

持有这类观点的人看似具备一定架构经验或基础,但实际上他们无论面对什么系统或业务,都会不顾一切地追求这些目标,导致架构设计变得复杂、项目实施时间拖延、团队内部不和等问题的出现。这些问题使得整个项目进展缓慢,系统稳定性差,出现问题难以解决,甚至加入新功能也需要花费大量时间。这些情况不是危言耸听,而是广泛出现的现象。因此,架构师或设计师必须深入了解系统和业务需求,根据实际需要合理设计,不可盲目追求“高XX”目标,才能确保项目开发进度和系统的稳定性。

1.2 架构设计的真正目的

整个软件技术发展的历史,其实就是一部与“复杂度”斗争的历史。架构也是为了应对软件系统复杂度而提出的一个解决方案,其主要目的是为了解决软件系统复杂度带来的问题。

那到底什么是复杂度呢?John Ousterhout教授在 A Philosophy of Software Design 书中提到,复杂度就是任何使得软件难于理解和修改的因素。

复杂的系统有一些非常明显的特征,John教授将它抽象为变更放大(Change amplification)、认知负荷(Cognitive load)与未知的未知(Unknown unknowns)这3类。

变更放大(Change amplification)指得是看似简单的变更需要在许多不同地方进行代码修改。系统开发者之前没有及时重构代码,提取公共逻辑,而是省时间Ctrl-C,Ctrl-V式代码开发(这样做不会影响已有的稳定模块,不需要做比较多的回归测试,上线风险小)。当需求变化时,需要改动多处代码。

认知负荷(Cognitive load)是指系统的学习与理解成本高,开发人员的研发效率大大降低。
未知的未知(Unknown unknowns)是指不知道修改哪些代码才能使系统功能正确的运行,也不知道这行代码的改动是否会引发线上问题。这一项是复杂性中最糟糕的一个表现形式。

1.3 系统复杂度的六个来源及通用解法

  1. 高性能
  2. 高可用
  3. 可扩展性
  4. 低成本
  5. 安全
  6. 规模

1.3.1 高性能

软件系统中高性能带来的复杂度主要体现在两方面:

  1. 一方面是单台计算机内部为了高性能带来的复杂度;
  2. 另一方面是多台计算机集群为了高性能带来的复杂度。

1.3.1.1 单机复杂度

计算机内部复杂度最关键的地方就是操作系统,计算机性能的发展本质上是由硬件发展驱动的,尤其是 CPU 的性能发展。而将硬件性能充分发挥出来的关键就是操作系统,所以操作系统本身也是随硬件的发展而发展的,操作系统是软件系统的运行环境,操作系统的复杂度直接决定了软件系统的复杂度。
操作系统和性能最相关的就是进程和线程。

  • 进程:用进程来对应一个操作系统执行的任务,每个任务都有自己独立的内存空间,进程间互不相关,由操作系统来进行调度。
  • 多进程:为了达到多进程并行运行的目的,采取了分时的方式,即把 CPU 的时间分成很多片段,每个片段只能执行某个进程中的指令。
  • 进程间通信:为了解决进程在运行时相互通信的问题,人们设计了各种进程间通信,包括管道、消息队列、信号量、共享存储等。
  • 多线程:多进程让多任务能够并行处理任务,但本身还有缺点,单个进程内部只能串行处理,而实际上很多进程内部的子任务并不要求是严格按照时间顺序来执行的,也需要并行处理。为了解决这个问题发明了线程,线程是进程内部的子任务,但这些子任务都共享同一份进程数据。为了保证数据的正确性,又发明了互斥锁机制。有了多线程后,操作系统调度的最小单位就变成了线程,而进程变成了操作系统分配资源的最小单位。

操作系统发展到现在,如果要完成一个高性能的软件系统,需要考虑如多进程、多线程、进程间通信、多线程并发等技术点,而且这些技术并不是最新的就是最好的,也不是非此即彼的选择。
在做架构设计的时候,需要花费很大的精力来结合业务进行分析、判断、选择、组合,这个过程同样很复杂。例如,下面的系统都实现了高性能,但是内部实现差异很大:

  • Nginx 可以用多进程也可以用多线程
  • JBoss 采用的是多线程
  • Redis 采用的是单进程
  • Memcache 采用的是多线程

1.3.1.2 集群复杂度

让多台机器配合起来达到高性能的目的,是一个复杂的任务,常见的方式有:
任务可以指完整的业务处理,也可以指某个具体的任务。
1.任务分配:每台机器都可以处理完整的业务任务,不同的任务分配到不同的机器上执行。
2.任务分解:业务越来越复杂,单台机器处理的性能会越来越低。为了能够继续提升性能,采用任务分解。

1.3.1.2.1任务分配

在这里插入图片描述

  • 增加一个任务分配器,可以是硬件(F5、交换机)、软件(LVS)、负载均衡软件(Nginx、HAProxy)、自己开发的系统。
  • 任务分配器与业务服务器之间的连接和交互。
  • 任务分配器增加分配算法(轮询、权重、负载)。

业务量继续提升,需要增加任务分配器的数量。
在这里插入图片描述

  • 任务分配器增加为多台,这样需要将不同的用户请求分配到不同的任务分配器上(DNS轮询、智能- DNS、CDN、GSLB全局负载均衡)。
  • 任务分配器和业务服务器之间从一对多变成多对多的网状结构。
  • 业务服务器继续扩增,状态管理和故障处理复杂度更大。
1.3.1.2.2 任务分解

微服务架构就采用了这种思路,通过任务分配的方式,能够突破单台机器处理性能的瓶颈,通过增加更多的机器来满足业务的性能需求,但如果业务本身也越来越复杂,单纯只通过任务分配的方式来扩展性能,收益会越来越低。
在这里插入图片描述

通过这种任务分解的方式,能够把原来大一统但复杂的业务系统,拆分成小而简单但需要多个系统配合的业务系统。从业务的角度来看,任务分解既不会减少功能,也不会减少代码量(事实上代码量可能还会增加,因为从代码内部调用改为通过服务器之间的接口调用),任务分解能够提升性能的主要原因是:

  1. 简单的系统更容易做到高性能:系统的功能越简单,影响性能的点就越少,就更加容易进行有针对性的优化。
  2. 可以针对单个任务进行扩展:当各个逻辑任务分解到独立的子系统后,整个系统的性能瓶颈更加容易发现,而且发现后只需要针对有瓶颈的子系统进行性能优化或者提升,不需要改动整个系统,风险会小很多。

最终决定业务处理性能的还是业务逻辑本身,业务逻辑本身没有发生大的变化下,理论上的性能是有一个上限的,系统拆分能够让性能逼近这个极限,但无法突破这个极限。

1.3.2 高可用

系统无中断地执行其功能的能力,代表系统的可用性程度,是进行系统设计时的准则之一。
本质上都是通过“冗余”来实现高可用。高可用的“冗余”解决方案,单纯从形式上来看,和高性能是一样的,都是通过增加更多机器来达到目的,但其实本质上是有根本区别的:高性能增加机器目的在于“扩展”处理性能;高可用增加机器目的在于“冗余”处理单元。
通过冗余增强了可用性,但同时也带来了复杂性。

1.3.2.1 计算高可用

计算的特点是无论从哪台机器上进行计算,同样的算法和输入数据,产出的结果都是一样的,所以将计算从一台机器迁移到另一台对业务没有影响。
在这里插入图片描述

  • 需要增加一个任务分配器
  • 任务分配器和真正的业务服务器之间有连接和交互
  • 任务分配器需要增加分配算法(主备【冷备、温备、热备】、主主、多主多倍【2主2备、4主0备】)

1.3.2.2 存储高可用

对于需要存储数据的系统而言,整个系统的高可用设计的难点和关键点在于“存储高可用”。存储和计算的本质区别在于将数据从一台机器搬移到另一台机器时需要通过线路进行传输,而线路传输是存在延迟的,速度在毫秒级别,距离越远,延迟越高。加之各种异常情况(如传输中断、丢包、拥塞),会导致延迟更高。对于高可用系统来说,在某个时间点通信中断就意味着整个系统的数据不一致。按照“数据 + 逻辑 = 业务”的公式,数据不一致将导致最终业务表现不同。如果不做冗余备份,系统的整体高可用性无法保证。因此,存储高可用的难点不在于如何备份数据,而在于如何减少或规避数据不一致对业务造成的影响。

分布式领域内著名的 CAP 定理从理论上证实了存储高可用的复杂度。存储高可用不可能同时满足“一致性、可用性、分区容忍性”,最多只能满足其中两个。因此,在进行架构设计时需要结合业务进行取舍。

1.3.3 可扩展性

可扩展性指系统为了应对将来需求变化而提供的一种扩展能力,当有新的需求出现时,系统不需要或者仅需要少量修改就可以支持,无须整个系统重构或者重建。
在软件开发领域,面向对象思想的提出,就是为了解决可扩展性带来的问题;设计模式更是将可扩展性做到了极致。
设计具备良好可扩展性的系统,有两个基本条件:

  • 正确预测变化
  • 完美封装变化

1.3.3.1 预测变化

“唯一不变的是变化”,按照这个标准衡量,架构师每个设计方案都要考虑可扩展性。预测变化的复杂性在于:

  • 不能每个设计点都考虑可扩展性
  • 不能完全不考虑扩展性
  • 所有的预测都存在出错的可能性

如何把握预测的程度和提升预测结果的准确性,是一件很复杂的事情,而且没有通用的标准,更多是靠经验、直觉。

1.3.3.2 应对变化

预测变化是一回事,采取什么方案来应对变化,又是另外一个复杂的事情。即使预测很准确,如果方案不合适,则系统扩展一样很麻烦。

微服务架构中的各层进行封装和隔离也是一种应对变化的解决方式。

1.3.3.2.1 变化层VS稳定层

第一种应对变化的常见方案是将“变化”封装在一个“变化层”,将不变的部分封装在一个独立的“稳定层”。
无论是变化层依赖稳定层,还是稳定层依赖变化层都是可以的,需要根据具体业务情况来设计。
无论采取哪种形式,通过剥离变化层和稳定层的方式应对变化,都会带来两个主要的复杂性相关的问题。

1.系统需要拆分出变化层和稳定层(如何拆分)

2.需要设计变化层和稳定层之间的接口(稳定层接口越稳定越好,变化层接口从差异中找到共同点)

1.3.3.2.2 抽象层VS实现层

第二种常见的应对变化的方案是提炼出一个“抽象层”和一个“实现层”。

抽象层是稳定的,实现层可以根据具体业务需要定制开发,当加入新的功能时,只需要增加新的实现,无须修改抽象层。这种方案典型的实践就是策略模式。

1.3.4 低成本

在设计高性能、高可用的架构方案时,如果涉及到数百、数千甚至数万台服务器,成本就会成为一个非常重要的考虑点。为了控制成本,需要减少服务器的数量,这与增加更多服务器来提升性能和可用性的通用做法相冲突。因此,低成本往往不是架构设计的首要目标,而是一个附加约束。为了解决这个问题,需要先设定一个成本目标,然后根据高性能和高可用的要求设计方案,并评估是否能够满足成本目标。如果不能,就需要重新设计架构;如果无论如何都无法设计出满足成本要求的方案,那只能找老板调整成本目标了。 低成本给架构设计带来的主要复杂度体现在,往往只有"创新"才能达到低成本目标。"创新"的含义是开创一个全新的技术领域,或者引入新技术来解决问题。如果没有找到能够解决自己问题的新技术,那么就需要自己创造新技术了。例如,NoSQL(如Memcache、Redis等)是为了解决关系型数据库无法应对高并发访问带来的访问压力;全文搜索引擎(如Sphinx、Elasticsearch、Solr)是为了解决关系型数据库like搜索的低效问题;Hadoop则是为了解决传统文件系统无法应对海量数据存储和计算的问题。创造新技术的主要复杂度在于需要创造全新的理念和技术,并且新技术需要与旧技术相比有质的飞跃。

1.3.5 安全

从技术的角度来讲,安全可以分为两类:

  • 一类是功能上的安全,
  • 一类是架构上的安全。

1.3.5.1 功能安全

常见的 XSS 攻击、CSRF 攻击、SQL 注入、Windows 漏洞、密码破解等,本质上是因为系统实现有漏洞,黑客有了可乘之机,功能安全其实就是“防小偷”。

从实现的角度来看,功能安全更多地是和具体的编码相关,与架构关系不大。开发框架会内嵌常见的安全功能,但是开发框架本身也可能存在安全漏洞和风险。

所以功能安全是一个逐步完善的过程,而且往往都是在问题出现后才能有针对性的提出解决方案,我们永远无法预测系统下一个漏洞在哪里,也不敢说自己的系统肯定没有任何问题。

换句话讲,功能安全其实也是一个“攻”与“防”的矛盾,只能在这种攻防大战中逐步完善,不可能在系统架构设计的时候一劳永逸地解决。

1.3.5.2 架构安全

如果说功能安全是“防小偷”,那么架构安全就是“防强盗”。

架构设计时需要特别关注架构安全,尤其是互联网时代,理论上来说系统部署在互联网上时,全球任何地方都可以发起攻击。

传统的架构安全主要依靠防火墙,防火墙最基本的功能就是隔离网络,通过将网络划分成不同的区域,制定出不同区域之间的访问控制策略来控制不同信任程度区域间传送的数据流。

防火墙的功能虽然强大,但性能一般,所以在传统的银行和企业应用领域应用较多。但在互联网领域,防火墙的应用场景并不多。

互联网系统的架构安全目前并没有太好的设计手段来实现,更多地是依靠运营商或者云服务商强大的带宽和流量清洗的能力,较少自己来设计和实现。

1.3.6 规模

规模带来复杂度的主要原因就是“量变引起质变”,当数量超过一定的阈值后,复杂度会发生质的变化。常见的规模带来的复杂度有:

  1. 功能越来越多,系统复杂度指数级上升
  2. 数据越来越多,系统复杂度发生质变

1.3.6.1 功能越来越多,系统复杂度指数级上升

例如,某个系统开始只有 3 大功能,后来不断增加到 8 大功能,虽然还是同一个系统,但复杂度已经相差很大了,具体相差多大呢?我以一个简单的抽象模型来计算一下,假设系统间的功能都是两两相关的,系统的复杂度 = 功能数量 + 功能之间的连接数量,通过计算我们可以看出:3个功能的系统复杂度为3+3=6

8个功能的系统复杂度为8+(7+0)*8/2=36 可以看出,具备8个功能的系统的复杂度不是比具备 3 个功能的系统的复杂度多5,而是多了30,基本是指数级增长的,主要原因在于随着系统功能数量增多,功能之间的连接呈指数级增长。下图形象地展示了功能数量的增多带来了复杂度。
在这里插入图片描述

1.3.6.2 数据越来越多,系统复杂度发生质变

随着数据量的不断增长,传统的数据处理和管理方式已经无法适应,因此“大数据”这一概念应运而生。大数据的诞生主要是为了解决数据规模变得越来越大时,传统的数据收集、存储、分析等方式无法胜任的问题。Google的三篇技术论文,即Google File System、Google Bigtable和Google MapReduce则分别开创了大数据文件存储、列式数据存储和大数据运算的技术领域。即便数据规模没有达到大数据的水平,数据增长仍然可能会给系统带来复杂性。例如,在使用关系数据库存储数据时,当单表数据达到一定规模时,就会导致添加索引、修改表结构等操作变得很慢,可能需要几个小时,这就会对业务造成不良影响。因此,必须考虑将单表拆分为多表来解决这个问题,但这个过程也会引入更多的复杂性。

1.4 简单的复杂度分析案例

我们来分析一个简单的案例,一起来看看如何将“架构设计的真正目的是为了解决软件系统复杂度带来的问题”这个指导思想应用到实践中。

当我们设计一个大学的学生管理系统时,我们需要考虑该系统的复杂度以及如何解决这些复杂度带来的问题。首先,我们可以将该系统的复杂度分为以下几个方面:

性能 该系统的访问频率并不高,因此性能并不是一个很大的问题。我们可以使用MySQL作为存储,Nginx作为Web服务器,无需考虑缓存。

可扩展性 该系统的功能比较稳定,可扩展的空间并不大,因此可扩展性方面也不是一个很大的问题。
高可用 数据丢失是不可接受的,故该系统的高可用性方面需要考虑多种异常情况,如机器故障、机房故障等。为此,我们需要设计MySQL同机房主备方案和MySQL跨机房同步方案。

安全性 该系统存储的信息涉及到学生的隐私,因此需要考虑安全性。我们可以使用Nginx提供的ACL控制、用户账号密码管理和数据库访问权限控制来保证系统的安全性。

成本 由于该系统比较简单,基本上几台服务器就可以搞定,因此成本方面并不需要太多关注。
规模 同上,规模复杂度无需过度关注。

总体来说,我们需要在架构设计中充分考虑系统的复杂度,同时根据不同问题选择合适的解决方案,以提高系统的可靠性和安全性。

1.5 总结

第一章提出了架构的根本目是解决系统复杂度,并简要说明系统复杂度的六个来源及通用解法,为我们设计架构提供了清晰可执行的操作思路。

二、微服务架构解决了高可用、可扩展问题,但性能下降、成本&规模复杂度暴增

我们知道,这些年来随着设备和新技术的发展,软件的架构模式发生了很大的变化。软件架构模式大体来说经历了从单机、集中式到分布式微服务架构三个阶段的演进 。随着分布式技术的快速兴起,我们已经进入到了微服务架构时代。
在这里插入图片描述

2.1 微服务架构的优点

与传统单体应用架构相比,微服务架构有很多优点,具体表现如下:

2.1.1 高可用

当架构中的某一组件发生故障时,在单一进程的传统架构下,故障很有可能在进程内扩散,导致整个应用不可用。在微服务架构下,故障会被隔离在单个服务中。若设计良好,其他服务可通过重试、平稳退化等机制实现应用层面的容错。

2.1.2 可扩展

单个服务应用也可以实现横向扩展,这种扩展可以通过将整个应用完整的复制到不同的节点中实现。当应用的不同组件在扩展需求上存在差异时,微服务架构便体现出其灵活性,因为每个服务可以根据实际需求独立进行扩展。

2.2 微服务的缺点

2.2.1 复杂度高

与单体式架构相比,微服务会导致复杂性上升,因为多个团队会在更多地方创建更多服务。若管理不当,则会导致开发速度和效率降低。

2.2.2 基础设施成本呈指数级增长

每个新的微服务都有自己的成本,例如测试工具、托管基础架构和监控工具等方面。

2.2.3 性能下降

微服务之间通过REST、RPC等形式进行交互,通信的时延会受到较大的影响。

三、DDD帮助微服务控制规模复杂度

3.1 控制成本&规模复杂度需要明晰微服务的边界

由1.3.6.1 所述,随着微服务数量上升,规模复杂度呈指数级上升,那么我们理应控制微服务的数量,这就带来了争论和疑惑:微服务的粒度应该多大呀?微服务到底应该如何拆分和设计呢?

长期以来,微服务架构缺乏一套系统的理论和方法来指导其拆分,这导致了一些人对微服务架构的理解出现了一些曲解。有人简单地认为微服务只是将原来的单体应用程序拆分为多个部署包或更换为支持微服务架构的技术框架,便可成为微服务。还有人认为,微服务越小越好。

然而,在过去的几年中,一些项目由于在前期过度拆分微服务,导致项目复杂度过高,无法进行上线和运维的情况已经出现。综合来看,我认为微服务拆分困境的根本原因在于不清楚业务或微服务的边界在哪里。换句话说,只有确定了业务边界和应用边界,这个困境才能迎刃而解。

3.2 DDD能够帮我们设计出清晰的领域和应用边界

DDD 包括战略设计和战术设计两部分。战略设计主要从业务视角出发,建立业务领域模型,划分领域边界,建立通用语言的限界上下文,这些限界上下文可以作为微服务设计的参考边界。而战术设计则从技术视角出发,着重于领域模型的技术实现,包括聚合根、实体、值对象、领域服务、应用服务和资源库等代码逻辑的设计和实现。

在战略设计过程中,领域模型的建立是重中之重。为此,DDD 提出了事件风暴这一建立领域模型的方法。事件风暴是一个从发散到收敛的过程,通常采用用例分析、场景分析和用户旅程分析,全面分解业务领域,梳理领域对象之间的关系,这是一个发散的过程。在事件风暴过程中,会产生很多实体、命令、事件等领域对象,我们将这些领域对象从不同的维度进行聚类,形成如聚合、限界上下文等边界,建立领域模型,这就是一个收敛的过程。

因此,DDD 可以帮助软件工程师建立清晰的领域模型,划分业务和应用边界,以指导微服务的设计和拆分。事件风暴是建立领域模型的主要方法,通过其发散的过程和聚合的过程,建立起合理的领域模型,从而实现高效的软件开发和落地。
在这里插入图片描述

我们可以用三步来划定领域模型和微服务的边界。

  • 在领域驱动设计的战略设计中,我们采用事件风暴方法梳理业务过程中的用户操作、事件以及外部依赖关系等,从而梳理出领域实体等领域对象。
  • 然后,我们根据领域实体之间的业务关联性,形成聚合,并确定聚合中的聚合根、值对象和实体。
  • 接着,根据业务及语义边界等因素,将一个或多个聚合划定在一个限界上下文内,形成领域模型。这个过程中,我们建立了领域模型,划定了业务领域的边界,建立了通用语言和限界上下文,确定了领域模型中各个领域对象的关系。这些限界上下文可以作为微服务设计的参考边界,从而确定了应用端的微服务边界。

在从业务模型向微服务落地的过程中,即从战略设计向战术设计的实施过程中,我们会将领域模型中的领域对象与代码模型中的代码对象建立映射关系,将业务架构和系统架构进行绑定。当调整业务架构和领域模型以响应业务变化时,系统架构也会同时发生调整,并同步建立新的映射关系。这种方式可以帮助我们实现高效的软件开发和落地,从而更好地应对业务需求变化。

因此,通过领域驱动设计的战略设计和战术设计,我们可以清晰地划定领域边界,建立领域模型,帮助我们实现微服务的设计和拆分,同时也能够有效地响应业务变化,提高软件开发和落地的效率。

3.3 微服务与DDD的异同

DDD 是一种面向复杂业务领域的设计方法论,而微服务是一种面向分布式系统的架构风格。它们的共同目标都是通过将系统分解为更小,更易于管理的组件来提高系统的可维护性和可扩展性。

  • 在 DDD 中,我们关注的是业务领域的划分和领域模型的设计,以便更好地理解业务需求,并将其转化为可执行的代码。
  • 在微服务中,我们主要关注的是运行时的通信,容错和故障隔离,以及服务治理等问题,以确保各个微服务可以独立开发、测试、构建和部署。
  • 通过结合 DDD 和微服务,我们可以更好地实现高效的业务逻辑,同时也可以充分利用微服务的优点,提高应用程序的可扩展性和可维护性。因此,DDD 和微服务是互补的,可以共同用于构建可靠且高效的应用程序。

四、参考

架构、复杂度与三原则

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/524504.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaWeb-一篇文章带你入门CSS(笔记+案列)

目录 CSS是什么基本语法 CSS的引入方式内部样式表行内样式表外部样式表 选择器基础选择器标签选择器类选择器id选择器通配符选择器 复合选择器后代选择器子选择器 常用元素属性字体属性文本属性背景属性圆角矩形 元素的显示模式块级元素行内元素 我们可以使用display属性来修改…

对称加密/非对称加密

古典密码学 起源于古代战争:在战争中,为了防止书信被截获后重要信息泄露,人们开始对书信进行加密。 移位式加密 如密码棒,使用布条缠绕在木棒上的方式来对书信进行加密。 加密算法:缠绕后书写 密钥: 木棒的尺寸 替…

[笔记]深入解析Windows操作系统《四》管理机制

文章目录 前言4.1注册表查看和修改注册表注册表用法注册表数据类型注册表逻辑结构HKEY_CURRENT_USERHKEY_USERS 实验:观察轮廓加载和卸载HKEY_CLASSES_ROOTHKEY_LOCAL_MACHINE 实验:离线方式或远程编辑BCDHKEY_CURRENT_CONFIGHKEY_PERFORMANCE_DATA 前言 本章讲述了…

day3_垃圾回收器

文章目录 Serial回收器ParNew回收器Parallel Scavenge回收器Serial Old回收器Parallel Old回收器CMS(Concurrent Mark Sweeping)回收器G1 主要有7种垃圾回收器,如下所示: 其中有直线关联的表示,这2种垃圾回收器可以配合使用的。 S…

大模型之PaLM2简介

1 缘起 大模型时代。 时刻关注大模型相关的研究与进展, 以及科技巨头的商业化大模型产品。 作为产品&技术普及类文章,本文将围绕PaLM2是什么、特点、如何使用展开。 想要了解更多信息的可以移步官方网站提供的参考文档,后文会给出相关链…

Oracle11g服务说明

一、服务说明 1.OracleDBConsoleorcl:非必须启动 Oracle数据库控制台服务,orcl是Oracle的实例标识,默认的实例为orcl。在运行Enterprise Manager(企业管理器OEM)的时候,需要启动这个服务。 2.OracleJobS…

一文了解异步编程

promise 什么是promise promise是异步编程的一种解决方案,从语法上来说,Promise是一个对象,从它可以获取异步操作的消息 ES6规定,Promise对象是一个构造函数,接受一个函数作为参数,这个函数会立即执行&a…

Long类型返回前端精度丢失

【1】给前端返回Long会出现精度丢失问题 在《阿里巴巴Java开发手册》中,有一条关于前后端超大整数返回的规约,具体内容如下: 【2】问题复现 后端直接用postman测试接口,返回数据没有问题。但是前端访问接口的时候,发…

系统化了解Micrometer

本文从官方文档整理出一篇系统化全面了解的文章, 后续可能会慢慢补上源码层面的解析: https://micrometer.io/docs 学习本文的目的在于深入了解中间件的监控模块的设计, 先看看主流的做法于核心思想 本文的引用来的笔者的理解于备注 需要做的是: 先理解功能存在的理由设计模式…

kibana 代码执行 (CVE-2019-7609)

漏洞原理 “原型污染”是一种针对JS语言开发的攻击方法。JS语言中,每一个对象都是有其原型(proto)的,而该原型又有其自己的原型对象,直到某个对象的原型为null。而如果JS对其中定义的对象原型的属性读写缺乏控制&…

进程程序替换+简易版shell实现

索引 进程程序替换如何进行程序替换不同程序替换函数之间的区别系统接口调用其他语言的函数替换函数execle 简易版shell实现 进程程序替换 什么是进程程序替换? 指在一个正在运行的进程中,将原来的程序替换成新的程序的过程。 eg:如果我们想…

I IntelliJ IDEA 2023.1 最新解锁方式,支持java20,让Coding飞一会儿

IntelliJ IDEA 2023.1 最新变化 在 IntelliJ IDEA 2023.1 中,我们根据用户的宝贵反馈对新 UI 做出了大量改进。 我们还实现了性能增强,从而更快导入 Maven,以及在打开项目时更早提供 IDE 功能。 新版本通过后台提交检查提供了简化的提交流程…

Android不基于第三发依赖包解析shp文件(2)

接着上篇文章继续 2)Point (点)   一个 Point 由一对双精度坐标组成,存储顺序为 X,Y。    /*** PointGeometry记录读取* */static Geometry renderPointGeometry(byte[] recordContent,GeometryFactory geometryFactory) {int shapetype2

N轴机械臂的MDH正向建模,及python算法

目录 一、前言二、三维空间的坐标系变换三、MDH建模要点四、MDH的变换矩阵推导五、机械臂MDH的python模型六、python源码 一、前言 如果机器人工程师缺乏机器人学理论的支撑和足够的认识,那么随着机器人项目的深入推进,可能会越走越艰难,所谓…

【Lambda】集合的Lambda表达式

【Lambda】集合的Lambda表达式 【一】Stream的方法介绍【1】Stream里常用的方法【2】collect(toList()) & filter【3】map【4】flatMap【5】max&min【6】reduce 【二】常用案例总结【1】准备方法查询数据库获取List结果【2】取值【3】分组【4】去重【5】排序【6】list的…

uniapp制作水印相机给图片添加水印并且保存图片至本地

uniapp保存文件的三种方式 文件主要分为两大类: 1.代码包文件:代码包文件指的是在项目目录中添加的文件。 2.本地文件:通过调用接口本地产生,或通过网络下载下来,存储到本地的文件。 其中本地文件又分为三种&#…

【K8s】Service详解

文章目录 一、Service介绍1、认识Service2、kube-proxy的三种工作模式3、Service的类型 二、Service的使用1、实验数据准备2、Service类型为ClusterIP补充:Endpoints补充:负载分发策略 3、Service类型为HeadLiness3、Service类型为NodePort4、Service的类…

Ovito渲染技巧之体系边界识别

关注 M r . m a t e r i a l , \color{Violet} \rm Mr.material\ , Mr.material , 更 \color{red}{更} 更 多 \color{blue}{多} 多 精 \color{orange}{精} 精 彩 \color{green}{彩} 彩! 主要专栏内容包括: †《LAMMPS小技巧》: ‾ \textbf…

性能、性能还是性能---下一代Android性能框架分析

性能优化的重要性和难度毋庸置疑,事实上,性能优化也是一个永无止境的游戏,总会发现有一些东西需要优化。但是不可避免的是,这也是一个边际效益递减的事情,项目或应用获得的收益在一定程度上也会逐渐降低。What’s new …

ReentrantLock 原理

目录 ReentrantLock 概述 ReentrantLock 的原理 什么是 AQS ? 获取锁资源(以⾮公平锁为例) tryAcquire addWaiter acquireQueued 释放锁资源 ⾮公平锁体现在哪⾥? 调试代码 总结 ReentrantLock 概述 ReentrantLock是Lock接⼝的默认实现,是⼀…