# 3.4以架构为中心的开发方法

\
在本节中，我们介绍一些在架构的设计和实现中使用的当代以架构为中心的开发方法和概念。目的是简要概述以架构为中心的开发方法。但只是一部分示例，并非涵盖了所有的情况。

#### &#xD;[**3.4.1领域驱动设计**](#user-content-fn-1)[^1]

领域驱动设计（DDD）是一系列原则和模式的集合，帮助开发人员设计对象系统。这个术语是由埃里克·埃文斯（Eric Evans）提出的，由于它使得通过功能领域来构建大型系统变得更容易，所以它是更好地理解微服务的一个重要因素。使用这种方法，每个子系统都形成一个独立的单元。

**3.4.1.1作为设计的基础的功能模型**

您应该通过构建功能领域来开始您的设计\[Sta11]。领域模型应该纯粹基于功能进行构建，并且需要在整个项目中被接受。这个模型改善了领域专家和开发人员之间的沟通，并能够精确地制定需求。通过软件中的直接映射，领域模型可以极其容易地被测试。基于这个模型，创建了一种通用的、特定领域的语言，其元素应包含在项目词汇表中。

这种通用语言就是所谓的无处不在的语言，是领域驱动设计的核心概念。这种语言应该在软件开发的所有领域中使用——即，所有项目成员在源代码以及数据库和其他组件中应该使用与领域专家相同的术语。它描述了功能领域、领域模型的元素、类、方法等等。

图 3 - 5 展示了使用领域驱动设计创建的领域模型的元素。

<figure><img src="https://3692050972-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FggUOxqxsCZOTHblhkbzD%2Fuploads%2FkXzhNxixcA42Est8z0Tr%2Fimage.png?alt=media&#x26;token=8569f2b2-2e39-48a8-8bb6-bf14098ec7d9" alt=""><figcaption></figcaption></figure>

<p align="center">图3-5领域模型的组成元素</p>

**3.4.1.2领域对象的系统管理**

实体代表功能领域的核心对象，通常是持久的。在该领域内，它们具有一致的标识和明确界定的生命周期。实体是您系统中的“事物”。通常以名词的形式（即人或地点）想象实体是很有用的。

值对象描述其他对象的状态，本身没有标识。它们只是描述具有标识的对象。它们可以由其他值对象组成，但绝不是实体。与实体相反，值对象不能被修改。

服务是表示领域的过程序列的操作。它们不被实体识别，通常也没有自己的状态。这些操作的输入和输出是实体、聚合或值对象（即领域对象）。\
对于领域对象的管理，埃文斯推荐了三种不同的管理对象：\
\&#xNAN;**• 聚合**\
聚合封装了相互关联的领域对象，并且恰好有一个以根对象形式的实体，表示对聚合的唯一访问。外部对象可能只包含对根实体的引用。\
\&#xNAN;**• 工厂**\
工厂封装了非平凡、复杂的对象结构。工厂无法访问其他层，专门用于构建功能对象。\
\&#xNAN;**• 存储库**\
存储库使所有类型的对象能够为其他对象获取对象引用，并通过底层持久化技术封装功能对象的访问。

**3.4.1.3功能领域的结构化**

功能领域的结构化通常基于功能对象或用户事务进行。\
• 基于功能对象的分解是合适的\
o 重用很重要。\
o 功能逻辑复杂、广泛或灵活。\
o 面向对象范式有很好的理解。\
• 这种类型的分解或多或少对应于面向对象的分解。\
• 在以下情况下，基于用户事务的结构化是合适的\
o 简单的数据采集和简单的数据操作\
o 外部系统集成\
o 简单或有限的功能逻辑\
o 面向对象程序的经验有限

用户事务对应于系统用户可以执行的一个操作，包括所有系统内部的操作，如输入数据的检查。

这里最重要的是保持设计的完整性。在可能的情况下，您应该根据相似的方面分解系统的所有部分，并始终一致地应用（和记录）这个概念。

**3.4.1.4域的类型**

DDD将一个系统细分为以下几个领域:\
• 核心领域\
• 通用子领域\
• 支持性子领域

核心领域包含系统的核心功能，并描述了系统存在的原因。在可能的情况下，它应该只由最有经验的开发人员来实现。

通用子领域包含对业务重要但不属于核心领域的功能——例如，发票生成或信件发送。它可以购买或外包。

支持性子领域包含支持性和从属的功能，也可以由经验较少的开发人员处理。然而，它应该与核心领域严格分开——例如，使用反腐败层。

**3.4.1.5 领域的集成**

正如已经提到的，每个子系统都应该形成一个独立的单元。在 DDD 中，另一个重要的术语是“有界上下文”，它使得确定微服务的适当粒度更容易。每个模型都有一个上下文，一个复杂的功能领域很可能由几个有界上下文组成。

对于不同领域的集成，存在各种可能性。这些包括：\
\&#xNAN;**• 发布语言**\
应该有一种通用语言，通过它领域可以相互作用——例如 JSON 或 XML 。\
\&#xNAN;**• 开放主机服务**\
这是一种经典的 SOA 形式的交互。一个领域指定一个协议，其他领域可以通过它来使用——例如，RESTful Web 服务。\
\&#xNAN;**• 反腐败层**\
通过一个隔离层使用来自另一个领域的服务。\
\&#xNAN;**• 分道扬镳**\
两个领域完全相互独立，没有集成。

#### **3.4.2 MDA**

模型驱动架构（MDA）是一种能够从模型（如 UML）生成（部分）应用程序的概念，它由对象管理组织（OMG）开发。

模型驱动软件开发（MDSD）通过模型的转换自动生成软件组件\[RH06]。MDSD 使用模型和生成器来改进软件开发。

模型是 MDA 的核心，通常借助特定领域语言（DSL）来制定。DSL 可以是基于文本的，也可以是图形的。然而，DSL 不是强制的。MDA、MDSD 和其他类似方法与 DSL 无关。

有两种创建可执行应用程序的方法。可执行模型要么由虚拟机直接解释（例如对象管理组织的可执行 UML），要么通过一个或多个转换被转换为可执行应用程序。

由 OMG 定义的模型驱动架构（MDA）不过是 MDSD 方法的一个特殊实例，与架构无关。虽然模型驱动软件开发提供了建模语言的选择，并且对转换为可执行应用程序没有限制，但 MDA 在这方面有更具体的要求。例如，要使用的 DSL 应该符合 MDA（即使用 OMG 的元对象设施（MOF）定义，MOF 形成了元模型）。在实践中，主要使用 UML 概要。

平台无关的方面在平台无关模型（PIM）的范围内建模。PIM 随后被映射到一个或多个特定于平台的模型（PSM）。因此，PSM 创建了与特定平台的链接，然后可以从此生成代码。

MDSD方法本质上具有以下优点:\
• 提高开发效率\
• 能够更好地整合专业专家\
• 软件更容易修改\
• 改进软件架构的实现\
• 功能逻辑可以相对容易地移植到其他平台

然而，模型驱动软件开发需要建立由 DSL、建模工具、生成器、平台等组成的基础设施，并且在创建模型时需要大量的规范。规范工作也更大，通常只有模型的一部分可以自动转换为构件。

#### **3.4.3参考架构**

**3.4.3.1系统构建模块的生成式创建**\
如果特定的操作必须以相同或相似的方式反复进行，那么可以借助生成技术实现自动化\[VA++09]。软件系统的创建常常在一些细节上与其他系统有所不同，但具有功能性或技术性的共同特征。软件开发中的一个首要目标是尽可能多地复用系统构建模块。

基于模板的生成器是软件生成的一种常见手段。在此背景下的模板主要是基于文本的。模板的一部分访问输入数据，这些输入数据也主要是基于文本的。借助模式定义了可以应用模板的情况。使用预定义的规则，对模板进行修改，输出基于生成器的输入。这种方法的成熟示例有 Java 发射器模板（JET）或 XSLT 转换语言，它是可扩展样式表语言 XSL 的一部分，用于转换 XML 文档。

另一种生成技术使用基于 API 的生成器，这些生成器（除其他用途外）用于生成 PDF 文档。在这种情况下，要生成的文档的整个结构通过 API（应用程序编程接口）进行描述。

模型驱动的软件开发（MDSD）是在软件开发中使用生成器的一个很好的例子。

**3.4.3.2 横切关注点**

一个程序可能包含在代码的几个独立位置出现的任务。如果要记录活动，必须在活动之前和之后包含特定的代码。如果日志设计要出现在程序的多个位置，开发人员会在不同的地方编写或复制相同的代码。其他例子包括数据库访问、事务管理和身份验证的重复。这种代码的多次出现与“不要重复自己”（DRY）的概念不一致。面向切面编程能够封装此类任务，以便任务只需编程一次，但可以在多个地方执行。

面向切面编程实现了“横切关注点”的关注点分离原则。横切关注点——也称为系统级关注点——影响整个系统或技术约束，并且不容易被封装。它们对于特定的功能（如日志记录）实际上并非必要。

横切关注点的例子有:\
• 日志记录\
• 性能分析\
• 验证\
• 会话\
• 同步\
• 安全\
• 错误处理\
• 事件驱动编程(例如PropertyChangeEvents)\
• 软件测试

已建立的面向切面编程的实现有 AspectJ、JBoss AOP 和 AspectWerkz。

**3.4.3.3 面向对象**

在面向对象的上下文中，过程被称为操作或方法。面向对象背后的思想是将现实世界的概念映射到对象中——例如，一辆汽车。这个对象还可以存储其数据（类型、颜色等），并提供编辑和查询此数据所需的操作。

面向对象的一个重要特征是分类。让我们继续以汽车为例。汽车经销商不只有一辆车，而是有许多不同的汽车。因此，“汽车”类可以被视为一种抽象。面向对象的代码定义这样一个类一次，但允许它被实例化多次。

如果可以从一个类实例化一系列对象，则需要在运行时区分它们。因此，每个对象都有其自己唯一的对象 ID，可用于调用对象的操作。\
图 3 - 6 展示了“汽车”类的 UML 类图和该类的两个实例化对象的对象图。

为了更精确地指定对象之间可能的交互，有一系列关系可用，如关联、聚合、继承、接口和抽象类。\
通过这些额外的抽象，面向对象的架构比过程式架构提供了更好的模块化支持。一般来说，实现上述原则更容易，但这并不自动使面向对象的架构成为一个好的架构。在这里，架构师也必须开发一个适当的面向对象模型，并正确应用相关的技术和方法。

<figure><img src="https://3692050972-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FggUOxqxsCZOTHblhkbzD%2Fuploads%2F3JOXZf2jtDpg6iVKuU0l%2Fimage.png?alt=media&#x26;token=52798247-e616-4511-9764-a50575ddaddc" alt=""><figcaption></figcaption></figure>

<p align="center">图3-6Example类图及其相关对象图</p>

**3.4.3.4 过程式方法**

架构结构化的一种经典（并且仍然流行）的方法是使用过程。过程能够将一个复杂的算法分解为可重用的子算法，这构成了实现关注点分离原则的基础。

许多编程语言，如 C 或 Cobol ，都是基于过程的，面向对象的系统（如 Java 的静态方法）也支持过程式抽象。

[^1]:
