本文档详细介绍了构建复杂后端系统常用的设计方法论,包括领域驱动设计(DDD)、Actor 模型、响应式编程以及其他相关方法。这些方法论为设计可维护、可扩展并符合业务需求的系统提供了系统化的框架。有关具体设计模式的信息,请参阅设计模式,分布式系统设计原则请参考分布式系统设计。
设计方法论为系统架构提供了结构化的方法,有助于弥合业务需求与技术实现之间的差距。每种方法论都有其自身的侧重点、优势和适用场景。
来源:README.md:297-308
领域驱动设计是一种专注于理解和建模业务领域的方法论,旨在创建能够准确反映业务需求的软件。DDD通过共享语言和概念模型,弥合了领域专家和开发人员之间的沟通鸿沟。
来源:README.md:298-300
| 组件 | 描述 | 用途 |
|---|---|---|
| 实体 | 具有独特标识的对象 | 客户、订单、产品 |
| 值对象 | 没有标识的不可变对象 | 地址、金额、日期范围 |
| 聚合 | 具有边界的实体和值对象集群 | 订单(包含订单行) |
| 仓库 | 为聚合提供存储和检索 | OrderRepository, CustomerRepository |
| 服务 | 包含不适合放入实体的业务逻辑 | PaymentService, ShippingService |
| 工厂 | 创建复杂对象 | OrderFactory, ReportFactory |
| 限界上下文 | 定义模型适用的边界 | 电子商务上下文,物流上下文 |
来源:README.md:298-300
CQRS 是一种模式,将读写操作分离到不同的模型中,从而针对各自的特定目的进行优化。
来源:README.md:299
DDD 通常提倡充血领域模型,其中业务逻辑封装在领域对象中,而不是贫血模型中,其中对象仅是数据容器。
| 充血领域模型 | 贫血领域模型 |
|---|---|
| 业务逻辑在领域对象中 | 业务逻辑在服务中 |
| 对象强制执行不变性 | 对象是数据结构 |
| 行为和数据在一起 | 行为和数据分离 |
| 更高的面向对象内聚性 | 披着面向对象外衣的程序式风格 |
| 更符合DDD原则 | 不太符合DDD原则 |
来源:README.md:300
Actor模型是一个并发计算的概念框架,将“actor”视为计算的通用原语。每个actor都可以做出本地决策、创建更多actor、发送消息并确定如何响应收到的下一条消息。
来源:README.md:301
| 实现 | 语言/平台 | 显著特点 |
|---|---|---|
| Akka | Scala, Java | 完整的actor框架、集群、持久化 |
| Erlang/OTP | Erlang | 内置语言支持actor(“进程”) |
| Orleans | .NET | 虚拟actor模型,专为分布式系统设计 |
| Pony | Pony语言 | 类型系统保证无数据竞争 |
来源:README.md:301
响应式编程是一种专注于数据流和变化传播的编程范式,它允许构建非阻塞、事件驱动的应用程序,从而以更少的线程实现更好的扩展性。
来源:README.md:302-305
响应式宣言定义了响应式系统的四个关键原则
| 库/框架 | 平台 | 主要功能 |
|---|---|---|
| Reactor | Java | 非阻塞响应式流,被Spring WebFlux使用 |
| RxJava | Java | JVM的响应式扩展,可组合异步事件 |
| Project Reactor | Java | 响应式流实现 |
| Vert.x | Java | 事件驱动、非阻塞工具包 |
| Akka Streams | Scala/Java | 响应式流规范的实现 |
来源:README.md:303-305
事件驱动架构(EDA)是一种设计范式,程序流程由事件(如用户操作、传感器输出或其他程序的消息)决定。
来源:README.md:148-156
| 模式 | 描述 | 用例 |
|---|---|---|
| 事件通知 | 信息最简单的事件 | 状态更新、通知 |
| 事件携带状态传输 | 事件包含所需的状态信息 | 数据同步 |
| 事件溯源 | 事件作为真相来源,存储所有变更 | 审计、复杂状态重建 |
| CQRS | 分离读写操作 | 性能优化、复杂领域 |
来源:README.md:299
无服务器架构是一种设计模式,应用程序由第三方服务托管,开发者无需管理服务器软件和硬件。
来源:README.md:307
| 优点 | 挑战 |
|---|---|
| 无需服务器管理 | 冷启动延迟 |
| 自动伸缩 | 执行时长有限 |
| 按使用量付费 | 厂商锁定 |
| 降低运营成本 | 调试复杂 |
| 专注于代码,而非基础设施 | 本地测试受限 |
| 更快的上市时间 | 无状态要求 |
来源:README.md:307
服务网格是微服务架构中处理服务间通信的专用基础设施层,提供流量管理、安全性、可观察性等功能。
来源:README.md:308
| 实现 | 主要功能 |
|---|---|
| Istio | 流量管理、安全性、策略执行、遥测 |
| Linkerd | 超轻量、注重安全、开销极小 |
| Consul Connect | 服务发现、配置、分段 |
| AWS App Mesh | AWS原生服务网格,集成ECS/EKS |
来源:README.md:308
设计方法论与开发方法论相辅相成,两者在实践中相互影响。
来源:README.md:309-321
| 设计方法论 | 兼容的开发方法论 | 关键集成点 |
|---|---|---|
| 领域驱动设计 | 敏捷、SCRUM、XP | 迭代领域建模,领域专家参与 |
| Actor模型 | 敏捷、TDD | 测试actor交互,增量式actor开发 |
| 响应式编程 | TDD、结对编程 | 流测试策略,协作式事件流设计 |
| 事件驱动架构 | SCRUM、看板 | 冲刺规划中的事件风暴,事件流可视化 |
| 无服务器 | 敏捷、DevOps | 函数的CI/CD,自动化测试 |
| 服务网格 | DevOps、看板 | 基础设施即代码,可观察性 |
来源:README.md:309-321
选择正确的设计方法论取决于多种因素,包括问题领域、团队专业知识、组织背景和技术要求。
| 方法论 | 何时使用 | 何时避免 |
|---|---|---|
| DDD | 复杂领域,长期运行系统 | 简单的CRUD应用,时间紧迫 |
| Actor模型 | 高并发,弹性系统 | 顺序处理,简单领域 |
| 响应式编程 | I/O密集型系统,事件处理 | CPU密集型计算,简单工作流 |
| 事件驱动 | 解耦系统,异步处理 | 强一致性要求,简单流程 |
| 无服务器 | 可变工作负载,初创公司,MVP | 可预测的高负载,长时间运行进程 |
| 服务网格 | 大型微服务部署 | 小型应用,单体应用,简单部署 |
来源:README.md:297-308