软件架构分层-你的软件有没有分层?

一、前言

系统架构思想是软件开发工程师的工作必备知识。大到大型互联网应用系统的设计,小到一个软件功能函数的设计,都需要拥有架构设计思想。软件架构分层就是架构设计中的一个子领域,更着重强调软件的分层概念。
本篇文章就带大家简单的了解一下软件架构的分层,学习完毕,你就会明白,为什么系统要分层,架构分层可以带来什么好处。

二、软件架构分层的发展背景

计算机编程在“上古时代”开始时,是用二进制来编程,后面逐渐发展成熟。到了20 世纪 60 年代第一次软件危机时,引出了“结构化编程”,并创造了“模块”概念;20 世纪 80 年代第二次软件危机引出了“面向对象编程”,创造了“对象”概念;到了 20 世纪 90 年代, 随着软件规模的不断增大,以下问题就开始显现。
  • 系统规模庞大,内部耦合严重,开发效率低;
  • 系统耦合严重,牵一发动全身,后续修改和扩展困难;
  • 系统逻辑复杂,容易出问题,出问题后很难排查和修复。
 
于是在Rational 和 Microsoft 内部,软件架构的概念开始越来越流行了。“组件”概念随着软件架构的流行也逐渐清晰。
我们可以看到,“模块”“对象”“组件”本质上都是对达到一定规模的软件进行拆分,差别只是在于随着软件的复杂度不断增加,拆分的粒度越来越粗,拆分的层次越来越高。
随着90年代互联网的迅速崛起,软件架构分层也随着软件架构的兴起而逐步兴起。

三、软件分层的概念

软件架构分层就是将软件模块按照水平切分的方式分成多个层,一个系统由多层组成,每层由多个模块组成。每层有自己独立的职责,为上一层提供服务,使用下一层的服务,每层只能看到处自己相临的层。多个层次协同提供完整的功能。通过分层结构,可以将大的问题分解为若干个渐进的小问题来解决,可以隐蔽问题的复杂度。修改某一层,最多影响其相邻的两层(通常只能影响上层)。
这里引申到层间隔离的概念。分层架构中的每一层可以是封闭的或者开放的,封闭意味着当一个请求自顶向下在层间传递时,它不能跳过任意的一层。所谓的层间隔离,旨在降低一个层次上的变化对其他层次的组件的影响。简单来说,就是每个层次对其他层次的功能知道的越少越好。但是在某些的场景,将特定的层次置为开放的状态也不失为一件好事。还有某些不想被其它层看到的代码也可以通过层间隔离的手段来实现。

四、软件分层的特点

分层设计的本质其实就是将复杂问题简单化,基于单一职责原则让每层代码各司其职,基于“高内聚,低耦合”的设计思想实现相关层对象之间的交互。从而,提升代码的可维护性和可扩展性。系统架构分层之后,一般要具有以下特点:
  • 高内聚:分层设计可以简化系统设计,让不同层专注做本层相关的事,同时更利于系统开发工作的分配,让“专业的人做专业的事”。这也体现了软件设计思想的“单一职责原则”
  • 低耦合:层与层之间通过接口或API来交互,依赖方不用知道被依赖方的细节。这样即使某一层发生较大的变化,其它层也不需要做较多改动就可以适配。软件设计思想的“迪米特法则”在这里得到了体现;
  • 复用/可移植性:分层之后可以做到代码或功能的复用;
  • 扩展性/易裁剪:分层架构可以让代码更容易横向扩展或者裁剪。这里体现了软件设计思想的开闭原则”。
 
任何事物都不可能是尽善尽美的,分层架构虽有优势也会有缺陷,比如分层可能会增加代码量。通过层层调用会降低了代码效率。
下面列用几种常见的分层架构来说明以上作用。

五、常见的分层架构

TCP/IP协议的四层架构:它把网络简化成了四层,即链路层、网络层、传输层和应用层。每一层各司其职又互相帮助,网络层负责端到端的寻址和建立连接,传输层负责端到端的数据传输等,同时相邻两层还会有数据的交互。这样可以隔离关注点,让不同的层专注做不同的事情。

Linux 文件系统分层:从下图你可以清晰地看出文件系统的层次。在文件系统的最上层是虚拟文件系统(VFS),用来屏蔽不同的文件系统之间的差异,提供统一的系统调用接口。虚拟文件系统的下层是 Ext3、Ext4 等各种文件系统,再向下是为了屏蔽不同硬件设备的实现细节,我们抽象出来的单独的一层——通用块设备层,然后就是不同类型的磁盘了。

我们可以看到,某些层次负责的是对下层不同实现的抽象,从而对上层屏蔽实现细节。比方说VFS 对上层(系统调用层)来说提供了统一的调用接口,同时对下层中不同的文件系统规约了实现模型,当新增一种文件系统实现的时候,只需要按照这种模型来设计,就可以无缝插入到 Linux 文件系统中。

网络服务架构分层:下图是目前常见的网络服务架构,分为部署的硬件环境、操作系统、所需的中间件、承载业务的应用程序以及软件接入层等。不同的层次也产生了对应在职位,比如运维工程师、中间件工程师、产品经理、开发工程师、测试工程师等工种。而我们在实践过程中,接触最多,使用最多的分层要属应用软件层了,其次是中间件层。

嵌入式软件架构分层:在嵌入式系统中,软件的分层同样很重要。嵌入式系统中的核心是芯片,以及围绕着芯片展开的一系列硬件电路,但是不同的嵌入式项目之间硬件差异很大,为了让硬件能够按指定的方式工作,就需要为相应的硬件“量身定制”硬件层代码。虽然硬件之间有差异,但还是存在一些共同点,比如驱动LCD工作,外接几个IO口来支持按键或控制小灯的开关等功能。为了屏蔽硬件底层的差异,同时提供统一的功能硬件接口,于是硬件抽象层就产生了。业务代码则实现嵌入式系统指定的业务功能,定义了系统在什么条件下做什么反应,或者定期执行一些什么动作。为了让业务代码开发更方便,不重复“造轮子”,需要收集很多现成的“轮子”,比较典型的“轮子”就是操作系统,它是众多“轮子”的集合,给应用程序提供了多任务,中断,任务间通信等功能,这一层我姑且叫它“功能层”。

六、怎么分

在做架构分层时,开发团队需要做到以下几点:

1. 让团队深入理解软件分层的义意,清晰软件分层的目的

分层的作用在上面已经列出来,只有充分认识到软件分层带来的好处,才会有动力去设计与实现分层。

2. 合理设计分层,清晰定义每层的职责

基于“高内聚,低耦合”的设计思想,定义每层的职责,每层再设计不同的模块。层次数量可以根据实际需要来调整。建议最多不要超过7层,3到4层最佳。

3.避免掉进sinkhole反模式的陷阱

所谓sinkhole反模式指的是请求只是简单地路过各个层次,并没有做一些业务处理。
比如,表现层接收到一个获取基本用户数据(姓名、地址等)的请求后将它传递到业务层;然而,业务层并没有做任何的业务处理,直接将请求传递到持久层;持久层也仅仅是构造了一个简单的SQL语句,向数据层查询用户数据;最后,数据按照原路返回到表现层,中途没有经过任何的数据汇聚、转换等操作。sinkhole反模式会导致很多不必要的对象实例化开销,从而增大了系统的内存消耗,并且影响了性能。
利用80-20原则可以帮助确定架构是否陷入sinkhole反模式。大概有百分之二十的请求仅仅是做简单的穿透,百分之八十的请求会做一些业务逻辑操作是正常的情况。然而,如果这个比例反过来,大部分的请求都是仅仅穿过层,不做逻辑操作,架构就陷入了sinkhole反模式,可以对一些架构层进行开放或者减少层级关系。

4. 通过技术手段守护架构

当定义清楚了分层架构,必然也要有守护架构的一些原则,在《演进式架构》中推荐尽早确定系统的适应度函数,并定期的审查,根据业务和技术的需求修改当前的适应度函数或增加新的适应度函数,以保证架构能够按照设计的方向发展。
java进程内,有一些自动化工具可以通过测试的方式来验证代码的架构是否遵循了预先设计的原则(如ArchUnit),可以高效的帮团队识别出在开发过程中破坏原则的代码实现。

5.提升团队整体认知水平和协作水平

在团队不断成熟的过程中,很难保证所有的开发人员都能够有能力守护架构,因此除了通过技术手段守护架构,也非常有必要采取一定的手段来提升整个团队的认知水平和协作水平。
首先可以在团队内建立架构评审委员会,关于架构的关键决策需要由委员会来拍板,而不是每个开发人员都可以决策。另外在做详细的实现设计时,要由有经验有能力守护架构的同学进行设计,并将工作内容拆分成更加可操作的Task。通过这种方式将整个团队的认知水平底线提升到可以守护架构的程度。
上面分享了分层的特点和分层的方法,相信你已经对软件架构分层有了更深入的了解。希望软件架构分层可以融入到我们的开发设计工作中。

七、结语

本篇文章为大家分享了市面上常见的架构分层。架构分层的目的是通过关注点分离来降低系统的复杂度,同时满足单一职责、高内聚、低耦合、提高可复用性和降低维护成本。但分层架构同样也有一定的缺点,比如开发成本高、性能略低等问题。实践中,架构分层并不能解决所有问题,每个项目可能都有其独特的需求和背景,选择什么样的架构模式,还是要根据实情况考虑。

注:部分资料、图片来源于网络,如有侵权,请联系后删除。

发表评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注