分层场景无处不在,从平时最常见的企业入手来谈。
之前读过关于企业文化的一本书,里面提到了一个常见的企业员工分层模型,见下图。
关于该员工分层模型,书中大概意思描述:一个企业的基层有责任心、中层有上进心、高层有事业心,则该企业将百年长青;不同员工处于不同的层次,员工有自己明确和清晰的职责,关注自己的职责落地,大大提高工作效率;上层是下层的战略,下层是上层的战术,上层依赖下层的输出;每一层的人都是可以被替换的,“铁打的营盘,流水的兵”,所谓地球离开谁都会正常运转;中层将高层与基层隔离,不相邻的两层工作上很难对接,所以工作中一定要找对干系人,才能更好推进。
上述员工分层模型的价值与软件分层的意义恰好不谋而合,软件分层给我们带来了什么:
-
关注点分离,职责清晰,简化了复杂度,提高了研发效率;
-
软件上层基于下层输出实现本层逻辑,任何一层都可以按需更换实现;
-
软件中不相邻的两层通过中间一层很好地进行了解耦,屏蔽了复杂度的扩散。
上述三句话,有点啰嗦,简单描述就是:
-
关注点分离,职责清晰;
-
任何一层都可按需更换实现;
-
中间层屏蔽了相邻层复杂度的扩散。
网上介绍软件分层好处的文章,琳琅满目,不计其数;今天谈软件分层,重点是聊软件应该分几层?
每次做最常见的软件三层架构时,就会问自己:为什么要分三层呢?为什么不分两层或四层呢?难道软件分两层,就不能正常运行吗?业务需求就不能实现吗?这背后到底是存在着一个什么原理或放之四海而皆准的标准呢?这个 “标准” 一直影响着软件必须这样来分层才可以。
对所有研发过的软件,分析其架构和设计规律,并对共性 “标准” 进行抽象,发现:软件分层的原则是为了封装与外部系统之间的交互。见下图。
-
当前系统需要与外部系统A 进行交互,所以与外部系统A交互的所有事情由当前系统独立出一个 “分层1” 来完成;
-
当前系统需要与外部系统B 进行交互,所以与外部系统B交互的所有事情由当前系统独立出一个 “分层2” 来完成;
-
分层1 与 分层2 负责完成与外部系统的交互,剩下的核心层负责完成当前系统的核心逻辑;
-
这应该就是放之四海而皆可应用的软件分层的 “标准”。
下面分别描述服务内部逻辑分层和软件分层架构的两个实际案例。
一、逻辑分层
在用 Java 技术栈开发软件的后端服务时,服务内部最常使用的是 controller—service—dao 三层的逻辑分层架构,我们对该架构做了一下扩展,形成了一个普适性的逻辑分层架构,见下图。
-
整体划分为 流量入口层、业务逻辑层 和 数据交互层;
-
流量入口层中的 controller 层负责对前端提供访问接口,封装与前端之间的交互;
-
流量入口层中的 consumer 负责从 MQ 中消费消息,封装与 MQ 之间的读交互;
-
数据交互层中的 dao 负责访问数据库,封装与数据库之间的交互;
-
数据交互层中的 si 负责访问第三方服务或其他软件系统,封装与第三方服务之间的交互;
-
数据交互层中的 producer 负责向 MQ 生产消息,封装与 MQ 之间的写交互;
-
那么最关键的业务逻辑层,service 就从流量入口层和数据交互层按标准协议读写数据然后实现本服务最核心的业务逻辑就可以了。
我们对这个逻辑分层架构做一下分析:
-
周边的每一个分层(在Java工程中可以落地为一个 package 包)就负责封装与外部系统之间交互的细节,为 service 层的业务逻辑处理免去了无关的繁琐细节,职责清晰,方便维护,扩展性好;
-
外部系统的任何变化,不会影响到核心业务逻辑层 service 的实现,比如:数据库从 MongoDB 切换到了MySQL,由 dao 层负责处理即可,service 不受任何影响;消息中间件从 RabbitMQ 切换到了 RocketMQ,由 consumer 和 producer 层处理即可,service 也不受任何影响;
-
通过封装与外部系统之间的交互而产生的所有分层,将外部系统与本服务的 service 层进行了彻底解耦。
二、分层架构
当服务要处理的访问流量非常高时,可以将逻辑分层架构中的每一层落地为一个独立的服务,以此来提升服务的吞吐量。上述软件逻辑分层架构中的每一层独立为服务后,便形成了软件分层架构。以之前文章(见 《分层架构 IM 系统之架构解读》)曾分析过的 IM 分层架构系统为例来描述,见下图。
-
IM 后端系统整体包括三层:入口层、业务逻辑层和数据访问层,每一层都是一个独立的服务;
-
入口层 Entry 负责维护与终端 app 之间的长连接,不处理任何业务逻辑;本质上 Entry 封装了与外部 app 系统之间的交互;
-
数据访问层 Das 负责对 MySQL 数据库进行访问(CRUD);本质上 Das 封装了与外部 MySQL 系统之间的交互;
-
最关键的 IM 后端逻辑由业务逻辑层 Logic 进行实现,Logic 无需关注终端是 app 还是小程序,也无需关注数据库是 MySQL 还是 TiDB;
-
这就是软件分层架构中,封装与外部系统之间交互性的分层原则。
上述分层架构 IM 系统是一个真实的企业案例(在 《IM 专题文章系列合集》 中有详细介绍),在实践中,Das 通过封装与数据库之间的交互,真真切切带来了益处。
随着 IM 用户量和访问规模的扩大,在存储层发生了诸多变化:
-
MySQL 数据库表由单表扩展到了 256 张表;
-
对 MySQL 数据库表的读写,对于系统消息业务,由读写一体演变成了读写分离;
-
后期引入了 Redis 缓存,帮助 MySQL 抗住了大部分的高并发的读流量;
-
存储介质将 MySQL 替换成了 TiDB 。
这些存储层的复杂性都被封装在了 Das 内,没有扩散到 Logic;而且,存储层的每一次变化,都不是几天短期内可以完成的,在存储层变化的同时,Logic 也在进行业务逻辑的更新迭代,两者同步进行,没有影响到系统的正常发版,这就是 Das 层的好处。
最后,总结文中关键:
-
员工分层模型:基层要有责任心、中层要有上进心、高层要有事业心;
-
软件分层带来了什么: 关注点分离,职责清晰;任何一层都可按需更换实现;中间层屏蔽了相邻层复杂度的扩散;
-
软件分层的原则或标准:是为了封装与外部系统之间的交互;
-
软件服务逻辑分层通常包括: controller、consumer、service、dao、si、producer;
-
IM 分层架构包括:入口层 Entry、业务逻辑层 Logic、数据访问层 Das,路由层 Router。