关于架构:人人都是架构师清晰架构-京东物流技术团队

6次阅读

共计 10585 个字符,预计需要花费 27 分钟才能阅读完成。

前言

理解清晰架构之前须要大家先相熟以下常见架构计划:

*EBI 架构 (Entity-Boundary-Interactor Architecture)
畛域驱动设计 (Domain-Driven Design)
端口与适配器架构 (Ports & Adapters Architecture, 又称为六边形架构)
洋葱架构 (Onion Architecture)
整洁架构 (Clean Architecture)
事件驱动架构 (Event-Driven Architecture)
命令查问职责拆散模式 (CQRS,即 Command Query Responsibility Segregation)
面向服务的架构(Service Oriented Architecture)*

清晰架构(Explicit Architecture,直译为显式架构)是将上述架构的局部劣势整合之后产生的另一种架构,因其 2017 年曾经呈现,曾经不算是一种新的架构,理论利用的我的项目尚且较少。以下次要介绍架构的造成及各步骤的意义。

1 架构演化过程

1.1 零碎的根本构建块

端口和适配器架构明确地辨认出了一个零碎中的三个根本代码构建块:

  • 运行用户界面所需的构建块;
  • 零碎的业务逻辑,或者利用外围;
  • 基础设施代码。

[]()

[]()

1.2 工具

在远离【利用外围】的中央,有一些利用会用到的工具,例如数据库引擎、搜索引擎、Web 服务器或者命令行控制台(尽管最初两种工具也是传播机制)。

[]()

把命令行控制台和数据库引擎都是利用应用的工具,要害的区别在于,命令行控制台和 Web 服务器通知咱们的利用它要做什么,而数据库引擎是由咱们的利用来通知它做什么。

1.3 将传播机制和工具连贯到利用外围

连贯工具和利用外围的代码单元被称为适配器(源自端口和适配器架构)。适配器无效地实现了让业务逻辑和特定工具之间能够互相通信的代码。
“告知咱们的利用应该做什么”的适配器被称为主适配器或被动适配器,而那些“由咱们的利用告知它该做什么”的适配器被称为从适配器或者被动适配器。

1.3.1 端口

适配器须要依照利用外围某个特定的入口的要求来创立,即端口。在大多数语言里最简略的模式就是接口,但实际上也可能由多个接口和 DTO 组成。
端口 (接口) 位于业务逻辑外部,而适配器位于其内部,这一点要特地留神。要让这种模式依照构想发挥作用,端口依照利用外围的须要来设计而不是简略地套用工具的 API。

1.3.2 主适配器或被动适配器

主适配器或被动适配器包装端口并通过它告知利用外围应该做什么。它们将来自传播机制的信息转换成对利用外围的办法调用。

[]()

换句话说,咱们的被动适配器就是 Controller 或者控制台命令,它们须要的接口 (端口) 由其余类实现,这些类的对象通过构造方法注入到 Controller 或者控制台命令。
再举一个更具体的例子,端口就是 Controller 须要的 Service 接口或者 Repository 接口。Service、Repository 或 Query 的具体实现被注入到 Controller 供 Controller 应用。
此外,端口还能够是命令总线接口或者查问总线接口。这种状况下,命令总线或者查问总线的具体实现将被注入到 Controller 中,Controller 将创立命令或查问并传递给相应的总线。

1.3.3 从适配器或被动适配器

和被动适配器包装端口不同,被动适配器实现一个端口 (接口) 并被注入到须要这个端口的利用外围里。

[]()

举个例子,假如有一个须要存储数据的简略利用。咱们创立了一个合乎利用要求的长久化接口,这个接口有一个保留数据数组的办法和一个依据 ID 从表中删除一行的办法。接口创立好之后,无论何时利用须要保留或删除数据,都应该应用实现了这个长久化接口的对象,而这个对象是通过构造方法注入的。

当初咱们创立了一个专门针对 MySQL 实现了该接口的适配器。它领有保留数组和删除表中一行数据的办法,而后在须要应用长久化接口的中央注入它。

如果将来咱们决定更换数据库供应商,比方换成 PostgreSQL 或者 MongoDB,咱们只用创立一个专门针对 PostgreSQL 实现了该接口的适配器,在注入时用新适配器代替旧适配器。

1.3.4 管制反转

这种模式有一个特色,适配器依赖特定的工具和特定的端口(它须要提供接口的特定实现)。但业务逻辑只依赖依照它的需要设计的端口(接口),它并不依赖特定的适配器或工具。

换句话说,适配器依据应用的工具不同能够灵便变更,然而业务逻辑产生的接口根本不会变动。

[]()

这意味着依赖的方向是由内向内的,这就是架构层面的管制反转准则。

再一次强调,端口依照利用外围的须要来设计而不是简略地套用工具的 API。

1.4 利用外围的构造

洋葱架构采纳了 DDD 的分层,将这些分层交融进了端口和适配器架构。这种分层为位于端口和适配器架构“六边形”内的业务逻辑带来一种构造组织,和端口与适配器架构一样,依赖的方向也是由内向内。

1.4.1 应用层

在利用中,由一个或多个用户界面触发的利用外围中的过程就是用例。例如,在一个 CMS 零碎中,咱们能够提供普通用户应用的利用 UI、CMS 管理员应用的独立的 UI、命令行 UI 以及 Web API。这些 UI(利用)能够触发的用例可能是专门为它设计的,也能够是多个 UI 复用的。

用例定义在应用层中,这是 DDD 提供的第一个被洋葱架构应用的层。

[]()

这个层包含了应用服务(以及它们的接口),也包含了端口与适配器架构中的接口,例如 ORM 接口、搜索引擎接口、音讯接口等等。如果咱们应用了命令总线和查问总线,命令和查问别离对应的处理程序也属于这一层。

1.4.2 畛域层

持续向内一层就是畛域层。这一层中的对象蕴含了数据和操作数据的逻辑,它们只和畛域自身无关,独立于调用这些逻辑的业务过程。它们齐全独立,对应用层齐全无感知。

[]()

1. 畛域服务

咱们偶然会碰到某种波及不同实体的畛域逻辑,当然,无论实体是否雷同,直觉通知咱们这种畛域逻辑并不属于这些实体,这种逻辑不是这些实体的间接责任。

所以,咱们的第一反馈兴许是把这些逻辑放到实体外的应用服务中,这意味着这些畛域逻辑就不能被其它的用例复用:畛域逻辑应该远离应用层。

解决办法是创立畛域服务,它的作用是接管一组实体并对它们执行某种业务逻辑。畛域服务属于畛域层,因而它并不理解应用层中的类,比方应用服务或者 Repository。另一方面,它能够应用其余畛域服务,当然还能够应用畛域模型对象。

2. 畛域模型

在架构的正核心,是齐全不依赖内部任何档次的畛域模型。它蕴含了那些示意畛域中某个概念的业务对象。这些对象的例子首先就是实体,还有值对象、枚举以及其它畛域模型中用到的任何对象。

畛域事件也“活在”畛域模型中。当一组特定的数据发生变化时就会触发这些事件,而这些事件会携带这些变动的信息。换句话说,当实体变动时,就会触发一个畛域事件,它携带着发生变化的属性的新值。这些事件能够完满地利用于事件溯源。

1.5 组件

目前为止,咱们都是应用档次来划分代码,但这是细粒度的代码隔离。依据 Robert C. Martin 在尖叫架构中表白的观点,依照子域和限界上下文对代码进行划分这种粗粒度的代码隔离同样重要。这通常被叫做“按个性分包”或者“按组件分包”,和“按层次分包”相响应。

[]()

[]()

[]()

我是“按组件分包”形式的动摇拥护者,在此我厚着脸皮将 Simon Brown 按组件分包的示意图做了如下批改:

[]()

这些代码块在后面形容的分层根底上再进行了“横切”,它们是利用的组件(译)。

组件的例子包含认证、受权、账单、用户、评论或帐号,而它们总是都和畛域相干。像认证和受权这样的限界上下文应该被看作内部工具,咱们应该为它们创立适配器,把它们暗藏在某个端口之后。

[]()

1.5.1 组件解耦

与细粒度的代码单元 (类、接口、特质、混合等等) 一样,粗粒度的代码单元 (组件) 也会从高内聚低耦合中受害。

咱们应用依赖注入(通过将依赖注入类而不是在类外部初始化依赖)以及依赖倒置(让类依赖形象,即接口和抽象类,而不是具体类)来解耦类。这意味着类不必晓得它要应用的具体类的任何信息,不必援用所依赖的类的齐全限定类名。

以同样的形式齐全解耦组件意味着组件不会间接理解其它任何组件的信息。换句话说,它不会援用任何来自其它组件的细粒度的代码单元,甚至都不会援用接口!这意味着依赖注入和依赖倒置对组件解耦是不够用的,咱们还须要一些架构层级的构造。咱们须要事件、共享内核、最终一致性甚至发现服务!

[]()

1. 触发其它组件的逻辑

当一个组件 (组件 A) 中有事件产生须要另一个组件 (组件 B) 做些什么时,咱们不能简略地从组件 A 间接调用组件 B 中的类 / 办法,因为这样 A 就和 B 耦合在一起了。

然而咱们能够让 A 应用事件派发器,派发一个畛域事件,这个事件将会投递给任何监听它的组件,例如 B,而后 B 的事件监听器会触发冀望的操作。这意味着组件 A 将依赖事件派发器,但和 B 解耦了。

然而,如果事件自身“活在”A 中,这将意味着 B 晓得了 A 的存在,就和 A 存在耦合。要去掉这个依赖,咱们能够创立一个蕴含利用外围性能的库,由所有组件共享,这就是共享内核。这意味着两个组件都依赖共享内核,而它们之间却没有耦合。共享内核蕴含了利用事件和畛域事件这样的性能,而且还蕴含规格对象,以及其它任何有理由共享的货色。记住共享内核的范畴应该尽可能的小,因为它的任何变动都会影响所有利用组件。

而且,如果咱们的零碎是语言异构的,比方应用不同语言编写的微服务生态,共享内核须要做到与语言无关的,这样它能力被所有组件了解,无论它们是用哪种语言编写的。例如,共享内核应该蕴含像 JSON 这样无关语言的事件形容 (例如,名称、属性,兴许还有办法,只管它们对规格对象来说更有意义) 而不是事件类,这样所有组件或者微服务都能够解析它,还能够主动生成各自的具体实现。

[]()

这种办法既实用于单体利用,也实用于像微服务生态系统这样的分布式应用。然而,这种办法只实用于事件异步投递的状况,在须要即时实现触发其它组件逻辑的上下文中并不实用!组件 A 将须要向组件 B 发动间接的调用,例如 HTTP。这种状况下,要解耦组件,咱们须要一个发现服务,A 能够询问它得悉申请应该发送到哪里能力触发冀望的操作,又或是向发现服务发动申请并由发现服务将申请代理给相干服务并最终返回响应给申请方。这种办法会把组件和发现服务耦合在一起,但会让组件之间解耦。例如 jsf。

2. 从其它组件取得数据

原则上,组件不容许批改不“属于”它的数据,但能够查问和应用任何数据。

1)组件之间共享数据存储

当一个组件须要应用属于其它组件的数据时,比如说账单组件须要应用属于账户组件的客户名字,账单组件会蕴含一个查问对象,能够在数据存储中查问该数据。简略的说就是账单组件晓得任何数据集,但它只能通过查问只读地应用不“属于”它的数据。

2)按组件隔离的数据存储

这种状况下,这种模式同样无效,但数据存储层面的复杂度更高。

组件领有各自的数据存储意味着每个数据存储都蕴含:

  • 一组属于它的数据,并且只容许它本人批改这些数据,让它成为繁多事实起源;
  • 一组其它组件数据的正本,它本人不能批改这些数据,但组件的性能须要这些数据,而且一旦数据在其所属的组件中产生了变动,这些正本须要更新。

每个组件都会创立其所需的其它组件数据的本地正本,在必要时应用。当数据在其所属的组件中产生了变动,该组件将触发一个携带数据变更的畛域事件。领有这些数据正本的组件将监听这个畛域事件并相应地更新它们的本地正本。

1.6 控制流

如前所述,控制流显然从用户登程,进入利用外围,到达基础设施工具,再返回利用外围并最终返回给用户。但这些类到底是是如何配合的?哪些类依赖哪些类?咱们怎么把它们组合在一起?

1.6.1 没有命令 / 查问总线

如果没有命令总线,控制器要么依赖应用服务,要么依赖查问对象。

[]()

上图中咱们应用了应用服务接口,只管咱们会质疑这并没有必要。因为应用服务是咱们利用代码的一部分,而且咱们不会想用另外一种实现来替换它,只管咱们可能会彻底地重构它。

1.6.2 有命令 / 查问总线

如果咱们的利用应用了命令 / 查问总线,UML 图根本没有变动,惟一的区别是控制器当初会依赖总线、命令或查问。它将实例化命令或查问,将它们传递给总线。总线会找到适合的处理程序接管并解决命令。

在下图中,命令处理程序接下来将应用应用服务。然而,这不总是必须的,实际上大多数状况下,处理程序将蕴含用例的所有逻辑。只有在其它处理程序须要重用同样的逻辑时,咱们才须要把处理程序中的逻辑提取进去放到独自的应用服务中。

[]()

总线和命令查问,以及处理程序之间没有依赖。这是因为实际上它们之间应该相互无感知,能力提供足够的解耦。只有通过配置能力设置总线能够发现哪些命令,或者查问应该由哪个处理程序解决。

如你所见,两种状况下,所有逾越利用外围边界的箭头——依赖——都指向外部。如前所述,这是端口和适配器架构、洋葱架构以及整洁架构的根本规定。

[]()

1.7 共享内核

共享内核由 DDD 之父 Eric Evans 定义,它是多个限界上下文之间共享的代码,由开发团队决定:

*[…] 两个团队批准共享的畛域模型的子集。当然,和模型子集一起共享还包含代码的子集,还有和这部分模型无关的数据库设计。这部分明确要共享的内容有着非凡的状态,而且在没有和其余团队达成统一的状况下不应该批改。
Shared Kernel(http://ddd.fed.wiki.org/view/shared-kernel), Ward Cunningham 的 DDD wiki*

所以基本上,它可能是任何类型的代码:畛域层代码、应用层代码、库,轻易什么代码。

[]()

然而,在这份心智地图里,咱们将它当做一些特定类型的代码的子集。共享内核蕴含的是畛域层和应用层的代码,这些代码会在限界上下文之间共享,让这些上下文能够相互通信。
这意味着,例如,一个或多个限界上下文触发的事件能够在其它的限界上下文里被监听到。须要和这些事件一起共享的还有它们用到的所有数据类型,例如:实体 ID、值对象、枚举,等等。事件不应该间接应用像实体这样的简单对象,因为将它们序列化到队列中或是从队列中反序列化时都会遇到一些问题,所以共享的代码不应该太宽泛。

当然,如果咱们手中的是一个由不同语言开发的微服务组成的多语言零碎,共享内核必须是描述性的语言,格局是 json、xml、yaml 或者其它,这样所有的微服务都能了解。

因而,共享内核就齐全和其余的代码以及组件齐全解耦了。这样很好,因为这意味着只管组件耦合了共享内核,但组件之间不再耦合。共享代码能够被清晰地辨认进去,并轻松地提取到一个独立的库中。

如果咱们决定将一个限界上下文从单体中分离出来并提取成一个微服务,这也会很不便。我对共享代码了然于心,能够轻松地将共享内核提取到一个库中。而这个库即能够装置到单体中,也能够装置到微服务中。

2 用代码体现架构

2.1 两张脑图

第一张脑图由一系列同心圆层级组成,它们最终依照业务维度的利用模块切分,造成组件。在这张图里,依赖的方向由内向内,意味着内层对外层可见,而外层对内层不可见。

[]()

第二张则是一组立体的层级,其中最下面的一层就是后面这张同心圆,下一层是组件之间共享的代码(共享内核),再下一层使是咱们本人对编程语言的扩大,最上面一层则是理论应用的编程语言。这里的依赖方向是自上而下的。

[]()

2.2 体现架构的代码格调

应用体现架构的代码格调,意味着代码格调(编码标准、类 / 办法 / 变量命名约定、代码构造…)某种程度上能够和浏览代码的人交换畛域和架构的设计用意。要实现体现架构的代码格调,次要有两种思路。

*“[…] 体现架构的代码格调能让你给代码的阅读者留下提醒,帮忙他们正确地推断出设计用意。”
—George Fairbanks(https://links.jianshu.com/go?to=https%3A%2F%2Fresources.sei.c…)*

第一种思路是通过代码制品的名字(类、变量、模块…)来传播畛域和架构的含意。因而,如果一个类是解决收据(Invoice)实体的仓库(Repository),咱们就应该将它命名成 InvoiceRepository,从这个名字咱们就能够看出,它解决的是收据畛域的概念,而它在架构中被当做一个仓库。这能够帮忙咱们了解它应该放在哪个中央,何时应用它以及如何应用它。然而,我认为代码仓库中并不是每个代码制品都须要这样做,例如,我感觉不用为每个实体(Entity)都加上后缀 Entity,这样做就有些画龙点睛,徒增乐音。

*“[…] 代码应该体现架构。换句话说,我一看到代码,就应该可能清晰地区分出各种组件[…]”
—Simon Brown(https://links.jianshu.com/go?to=http%3A%2F%2Fwww.codingthearc…)*

第二种思路是让代码仓库中的顶级制品明确地区分出各个子域,即畛域维度的模块,也就是组件。

第一种思路应该很分明,无需赘述。但第二种思路有点儿奥妙,咱们得深入探讨一下。

2.3 让架构清晰的展示进去

在我的第一张图里,咱们曾经看到,在最粗粒度的层级上,咱们只有三种不同用处的代码:

  • 用户界面,这里的代码就是为了适配某个用例的传播机制;
  • 利用外围,这里的代码就是用例和畛域逻辑;
  • 基础设施,这里的代码就是为了适配利用外围所需的工具 / 库。

[]()

因而,在源代码的根目录下咱们能够创立三个文件夹来体现这三类代码,一个文件夹对应一个类别的代码。这三个文件夹示意三个命名空间,稍后咱们甚至能够创立测试来断言外围对用户界面和基础设施可见,反过来却不可见,也就是说,咱们能够测试由内向内的依赖方向。

2.3.1 用户界面

一个 Web 企业应用通常领有多套 API,例如,一套给客户端应用的 REST API,还有一套给第三方利用应用的 web-hook,业务还有一套须要保护的遗留 SOAP API,或者还有一套给全新挪动利用应用的 GraphQL API…

这样的应该通常还有一些 CLI 命令,用于定时作业(Cron Job)或按需的保护操作。
当然,还有普通用户能够应用的网站自身,但兴许还有另一个供给用管理员应用的网站。
这些全都是同一个利用的不同视图,全都是同一个利用的不同用户界面。

实际上咱们的利用可能领有多个用户界面,其中有些还是供非人类用户(第三方利用)应用的。咱们通过文件 / 命名空间来辨别并隔离这些用户界面,来展现出这一点。

用户界面次要有三类:API、CLI 和网站。所以咱们在 UserInterface 根命名空间里为每个类别创立一个文件夹,将不同界面的类型清晰地区离开来。

[]()

下一步,如果有必要的话,咱们还能够持续深刻每种类型的命名空间,再创立更细分类的用户界面的命名空间(CLI 可能不须要再细分了)。

2.3.2 基础设施

和用户界面一样,咱们的利用应用了多种工具(库和第三方利用),例如 ORM、音讯队列、SMS 提供商。

此外,上述每一种工具都能够有不同的实现。例如,思考一家公司业务扩张到另一个国家的状况,因为价格的因素,不同的国家最好采纳不同的 SMS 提供商:咱们须要端口雷同的适配器的不同实现,这样应用时能够相互替换。另一个例子是对数据库 Schema 进行重构或者切换数据库引擎,须要(或决定要)切换 ORM 时:咱们会在利用中注入两种 ORM 适配器。

[]()

因而,在 Infrastructure 命名空间来说,咱们先给每一种工具类型创立一个命名空间(ORM、MessageQueue、SmsClient),而后再每一种工具类型外部为每一种用到的供应商(Doctrine、Propel、MessageBird、Twilio…)的适配器在创立一个命名空间。

2.3.3 外围

在 Core 命名空间下,能够依照最粗粒度的层级划分出三类代码:组件(Component)、共享内核(Shared Kernel)和 端口(Port)。为这三个类别创立文件夹 / 命名空间。

1. 组件

在 Component 命名空间下,咱们为每个组件创一个命名空间,而后在每个组件命名空间下,咱们再别离为利用(Application)层和畛域(Domain)层别离创立一个命名空间。在 Application 和 Domain 命名空间下,咱们先将全副类放在一起,随着类的数量一直减少,再来思考必要的分组(我感觉一个文件夹下就放一个类有些矫枉过正,所以我宁愿在必要时再进行分组)。

这是咱们就要思考是依照业务主题(收据、交易…)分组还是依照技术作用(仓库、服务、值对象…)分组,但我感觉无论怎样分组影响都不大,因为这曾经是整个代码组织树的叶子节点了,如果须要,在整个组织构造的最底端进行调整也很简略,不会影响代码仓库的其它局部。

2. 端口

和 Infrastructure 命名空间一样,Port 命名空间里外围应用的每一种工具都有一个命名空间,外围通过这些代码能力应用底层的这些工具。

这些代码还会被适配器应用,它们的作用就是端口和真正工具之间的转换。这种模式简略得不能再简略了,端口就是一个接口,但很多时候它还须要值对象、DTO、服务、构建起、查问对象甚至是仓库。

3. 共享内核

咱们把在组件之间共享的代码放到 Shared Kernel 命名空间下。尝试了几种不同的共享内核内部结构之后,我无奈找到一种实用于所有状况的构造。有些代码和 Core\Component 一样按组件划分很正当(例如 Entity ID 显然属于一个组件),有些代码这样划分却不适合(例如,事件可能被多个组件触发或监听)。兴许要联合应用两种划分的思路。

[]()

2.3.4 用户区里的编程语言扩大

最初,咱们还有一些本人对编程语言的扩大。这个系列中后面一篇文章曾经探讨过,这些代码本能够放在编程语言中,却因为某些起因没有。比方,在 PHP 中咱们能够想到的是 DateTime 类,它基于 PHP 提供的类扩大,提供了一些额定的办法。另一个例子是 UUID 类,只管 PHP 没有提供,然而这个类人造就是纯正的、对畛域无感,因而能够在任意我的项目中应用,并且不依赖任何畛域。

[]()

这些代码用起来和编程语言本人的提供的性能没啥区别,因而咱们要齐全掌控这些代码。然而,这并不是意味着咱们不能应用第三方库。咱们能用而且应该用,只有正当,然而这些库应该用咱们本人的实现包装起来(这样的话咱们能够不便的切换背地的第三方库),而利用代码应该间接应用这些包装代码。最终,这些代码能够自成我的项目,应用本人的 CVS 仓库,被多个我的项目应用。

3 通过文档形容架构

咱们有哪些可供选择的文档工具来表白整个利用的构建块以及利用如何工作?!

*UML
4+1 架构视图模型
架构决策记录
C4 模型
依赖图
利用地图 *

3.1 C4 模型

C4 模型是 Simon Brown 创造的,是我目前看到的对于软件架构文档的最好思路。我会疾速地用本人的语言来论述次要的思路,但应用的还是他的图例。

其思路是用四种不同粒度(或者“缩放”)层级来记录软件的架构:

第一级:零碎上下文图
第二级:容器图
第三级:组件图
第四级:代码图

3.1.1 第一级:零碎上下文图

这是最粗粒度的图。它的细节很少但其次要指标是形容利用所处的上下文。因而,这幅图中只有一个方块代表整个利用,其它围绕着利用的方块代表了利用要进行交付的内部零碎和用户。

[]()

3.1.2 第二级:容器图

当初,咱们将利用放大,也就是上一级图中的蓝色方块,在这一级它对应的是下图中的虚线框。

在这个粒度级别,咱们将看到利用得容器,一个容器就是一个利用中技术上独立的一小部分,例如一个挪动 App,一个 API 或者一个数据库。它还形容了利用应用的次要技术和容器之间的通信形式。

[]()

3.1.3 第三级:组件图

组件图展现的是一个容器内的组件。在 C4 模型上下文里,每个组件就是利用的一个模块,不光是畛域维度的模块(如账单、用户…)还包含纯正的功能模块(如 email、sms…)。因而这个层级的图向咱们展现了一个容器的次要齿轮和齿轮之间的啮合关系。

[]()

3.1.4 第四级:代码图

这是最细粒度的图,目标是形容一个组件外部的代码构造。在这个层级,咱们应用的是示意类级别制品的 UML 图。

[]()

4 总结

清晰架构集百家之长,人造有很多劣势:

  • 从外向内,越向内越偏外围准则,外围准则绝对稳固。外围准则就是惯例的畛域层,提供外围能力
  • 外层基于外围准则适配不同的业务场景,组装内层的能力。这里的外层就是惯例的接口层到应用层,次要应用被动适配器模式,重点关注 BFF(BackendsForFrontends) 及对内层能力的聚合
  • 内层不依赖外层,不受业务变动而变动。关注能力的扩大,实现外围策略实现
  • 边界显著,尤其是畛域层与应用层之间
  • CQRS 机制,耦合度低,通过外层组装内层能力动静适配业务变动,扩展性高

这只是一份指南!利用才是你的疆域,现实情况和具体用例才是使用这些常识的中央,它们能力勾画出理论架构的轮廓!

咱们须要了解所有这些模式,但咱们还时常须要思考和了解咱们的利用须要什么,咱们应该在谋求解耦和内聚的路线上走多远。这个决定可能受到许多因素的影响,包含我的项目的性能需要,也包含构建利用的工夫期限,利用寿命,开发团队的体验等等因素。

利用遵循某种畛域构造组成,也遵循某种技术构造(即架构)组成。这两种构造才是一个利用的不同凡响之处,而不是它应用的工具、库或者传播机制。如果咱们想让一个利用能够长时间的保护,这两种构造都要清晰的体现在代码仓库中,这样开发者能力晓得、了解、遵循,并在须要时改良。

这种清晰度让咱们能够在编码的同时了解边界,这能反过来帮忙咱们放弃利用的模块化设计,做到高内聚低耦合。

附录:

  1. 软件架构编年史(译):https://www.jianshu.com/p/b477b2cc6cfa
  2. The Software Architecture Chronicles:https://herbertograca.com/2017/07/03/the-software-architectur…
  3. 技术案例—基于 DDD 思维的技术架构策略调整:https://www.6aiq.com/article/1648170246451
  4. 中文图

[]()

作者:京东物流 李国梁

起源:京东云开发者社区 自猿其说 Tech

正文完
 0