The Clean Architecture - 清晰的架构设计

架构的目标基本相同,即通过分层将关注点的分离,通过模块化或服务化将业务进一步细分。

可以联想到的内容有分治、KISS、DIY等思想和原则,另外分层的对标OSI和TCP/IP网络模型分层、计算机OS(计算、存储、IO),都是在做职责关注和分离的事情。

1. 架构基本素养

除了框架选型外,一直还有一个点一直在思考的,那就是在大型软件架构时候,如何保证项目的架构设计足够清晰。

因为如果仅有框架,没有内部业务的组织,无论是前期的单体应用,还是后期的分布式微服务应用,维护难度绝非易事。

1.1. 软件工程师

  1. 软件能力:软件=数据结构+算法,以及编写软件的语言;
  2. 工程能力:涵盖软件生命周期 - 业务、需求、设计、开发、测试、部署、维护,围绕信息和能量两个角度考虑:
    • 节省能量:流程标准化和工具化的能力(CI/CD、SRE、DevOPS、服务化、容器云…)
    • 信息传递:有效沟通

1.2. 架构设计的目标

  • 简单:不过度设计,代码平实简单
  • 高效:提高业务迭代的效率
  • 稳定:程序持续运行稳定,不会轻易随系统外界情况影响,鲁棒性高
  • 健壮:通过良好的基础库程序设计,减少错用,各类异常情况的覆盖
  • 分层:应用高层依赖低层,每层足够简单,维护方便
  • 模块化:代码是高内聚,低耦合的,代码复用度较高,业务呈现模块化特征
  • 易扩展:良好的接口设计,来扩展实现
  • 易维护:系统应用和维护的成本低
  • 高性能:性能高,但不过渡优化
  • 标准化:数据调用方式、通信数据格式、命名编码方式等

2. 12-Factor方法

构建网络应用程序,或软件即服务(SaaS)的方法论:

  1. 基准代码:一份基准代码,多次部署
  2. 依赖:基于包或模块系统,显式的声明应用的依赖
  3. 配置:基于环境独立存储配置(开发、测试、预发布、生产)
  4. 服务:将后端服务当成资源
  5. 部署:构建、发布、运行,严格分离构建和运行
  6. 进程:以一个或多个无状态进程运行应用
  7. 端口绑定:由客户端指定端口,绑定服务
  8. 并发:基于进程模型扩展
  9. 易处理:快速启动和优雅终止可最大化健壮性
  10. 环境等价:尽可能的保持开发,预发布,线上环境相同(工具、版本等)
  11. 日志:将日志作为事件流(问题排查、度量、监控、告警)
  12. 进程管控:后台管理任务当作一次性进程运行

3. 如何保证项目的架构设计足够清晰 ?

在企业中,技术如果没有服务于业务或者帮助到业务成长,则失去了其价值

4. 设计原则

  • 独立于UI:UI可以轻松更改,而无需更改系统的其余部分。例如,可以使用控制台UI替换Web UI,而无需更改业务规则。
  • 独立于框架:该体系结构不依赖于某些特征库软件库的存在。这允许你将此类框架用作工具,而不必将你的系统塞入其有限的约束中。
  • 独立于数据库:你可以换掉Oracle或SQL Server,用于Mongo,BigTable,CouchDB或其他东西。你的业​​务规则未绑定到数据库。
  • 独立于任何外部机构:事实上,你的业务规则根本不了解外部世界。
  • 可测试:可以在没有UI,数据库,Web服务器或任何其他外部元素的情况下测试业务规则。

5. 设计实现

依赖规则: 同心圆代表软件的不同领域,内圈中的任何东西都不能知道外圈中的某些东西。特别是,外圈中声明的内容的名称不得被内圈中的代码提及。这包括功能,类,变量或任何其他命名的软件实体。

  • 依赖规则始终适用,源代码依赖性始终指向内部,当你向内移动时,抽象级别会增加,最内圈是最普遍通用的。
  • 使用依赖性倒置原则来解决跨边界问题,禁止内圈中对外圈中的程序调用
  • 当我们跨越边界传递数据时,它始终采用最方便内圈的形式。

5.1. 实体

实体封装了企业范围的业务规则,实体可以是具有方法的对象,也可以是一组数据结构和函数。

5.2. 用例

此层中的软件包含特定于应用程序的业务规则,它封装并实现了系统的所有用例。

这些用例协调与实体之间的数据流,并指示这些实体使用其企业范围的业务规则来实现用例的目标。

如果用例的细节发生变化,那么这一层中的一些代码肯定会受到影响。

5.3. 控制器 - 接口适配层

此层中的软件是一组适配器,可将数据从内部格式转换为某些外部机构(如数据库或Web)最方便的格式(比如JSON或HTML),或者将数据从某些外部表单(如外部服务)转换为用例和实体使用的内部所需。

此圈子内的任何代码都不应该知道有关数据库的任何内容。

5.4. 框架和驱动程序

最外层通常由框架和工具组成,例如数据库,Web框架等。通常,除了与下一个圆圈内部通信的粘合代码之外,不会在此层中编写太多代码。

5.5. 结论

通过将软件分成多个层,并符合依赖性规则和职责分离,你将创建一个本质上可测试的系统,具有所暗示的所有好处。

当系统的任何外部部件变得过时时,例如数据库或Web框架,你可以用最少的麻烦来替换那些过时的元素。

6. 参考