3.设计模式之分层思维:为什么要做代码分层架构?

Posted 卜卦丶cc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了3.设计模式之分层思维:为什么要做代码分层架构?相关的知识,希望对你有一定的参考价值。

一、代码分层架构是什么

软件程序通常有两个层面的需求:

  • 功能性需求,简单来说,就是一个程序能为用户做些什么,比如,文件上传、查询数据等;

  • 非功能性需求,这个是指除功能性需求以外的其他必要需求,比如,性能、安全性、容错与恢复、本地化、国际化等。

事实上,非功能性需求所构建起来的正是我们所熟知的软件架构。什么是软件架构?简单来说,就是软件的基本结构,包括三要素:代码、代码之间的关系和两者各自的属性。

我们都知道,软件架构非常重要,为什么重要呢?如果把软件比作一座高楼,那么软件架构就是那个钢筋混凝土的框架,代码就是那个框架里的砖石,正是因为有了那个框架,才能让每一个代码都能很好地运行起来。

其中,最为经典的软件架构就是分层架构,也就是将软件系统进行分层,现在几乎已经成为每个程序员最熟悉的思考模式之一。不过,分层架构越是流行,我们的设计越容易僵化。这背后到底有哪些值得我们深思的地方呢?

所以,今天我就从架构角度来聊聊为什么代码要做分层、主要用于解决什么问题,以及存在优势和劣势有哪些。

要想彻底理解代码分层架构,就得从软件部署分层架构说起。首先我们来看一下常见的互联网软件部署分层架构,如下图所示:

由图可以看到,软件部署分层架构主要包括以下四个核心部分。

  • 客户端层(Client):调用方,比如浏览器或 App。

  • 应用服务层的网页服务器(Web Server):实现程序的运行逻辑,并从下层获取数据,返回给上层的客户端层。

  • 应用服务层的缓存(Cache):加速访问存储的数据。

  • 数据层(DB):存储数据。

通过上面的分析,现在你应该知道什么是软件分层架构了吧?软件分层架构是通过层来隔离不同的关注点(变化相似的地方),以此来解决不同需求变化的问题,使得这种变化可以被控制在一个层里。

作为软件开发者,我们更关心的其实是应用程序里的分层架构。比如,下图展示的现在流行的一种 MVC 分层架构:

我们能明显看到,MVC 分层架构是作用于程序本身的,程序作为一个整体被发布在服务器上运行使用。而类似 DB 里也有自己的分层架构,这里我们重点介绍应用程序中的代码分层架构,其他架构就不展开讨论了。

那么问题来了,什么是代码分层架构呢?

代码分层架构就是将软件“元素”(代码)按照“层”(代码关系)的方式组织起来的一种结构。

分层架构核心的原则是:当请求或数据从外部传递过来后,必须是从上一层传递给下一层如下图,一个来自 View 层的数据,必须先通过 Controller 层、Model 层后,才能最终到达数据库层。

 那么你可能会问:“为什么不让 View 层的请求直接到达数据库呢?”这是因为会造成新的代码耦合,增加代码的复杂度。比如说,View 层直接调用 Model 层的组件,当 Model 层上的组件有变化时(比如, SQL 或逻辑修改),既会影响 Controller 层组件的使用,也会影响 View 层组件的使用(可参考下面的示意图)。

所以,分层的本质就是为了让相似变化在各自的层内变化,而不造成层与层之间的相互影响。

二、代码分层架构解决什么问题 

代码分层架构主要是为了解决两个问题:

  • 如何快速拆解功能问题?

  • 如何提升代码的可扩展性?

下面我们就来分别解释下。

1. 通过分层来拆解问题

在软件开发中,一个功能需求问题通常都是笼统的复杂问题,我们一般都会将这个笼统的复杂问题拆分为多个层次的子问题来解决。

这里来看一个简单的例子,假定你正在编写一段“通过 HTTP 向服务器发送字符串”的代码,如下所示:

//创建HTTP连接

URL url = new URL("http://xxx.test.com/sayHello");

HttpURLConnection connection = (HttpURLConnection) url.openConnection();

connection.connect();

//发送数据

OutputStream os = connection.getOutputStream();

os.write("Hello World!".getBytes("UTF-8"));

//接收响应

InputStream is = connection.getInputStream();

BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));

//……

br.close();

is.close();

os.close();

//关闭连接

connection.disconnect();

我们将这段代码简单地抽象成一个流程图,如下所示:

这个流程图代表了我们对最初始问题的分层拆分:先创建 HTTP 连接,然后向服务器发送一串字符串,最后关闭 HTTP 连接。

于是,原先的“如何通过 HTTP 向服务器发送字符串”的问题就变成了三个新层次的子问题:

  • 如何创建 HTTP 连接?

  • 如何发送字符串?

  • 如何关闭连接?

首先,在思考如何创建 HTTP 连接这个问题的过程中,你会发现,要想通过 HTTP 发送消息,至少得打开 HTTP 连接,建立 HTTP 会话,并使用 TCP 协议,这样才能通过网络发送数据。

接着,你又发现,当成功解决了这个问题后,发送字符串和关闭 HTTP 连接还有更多的问题需要解决,于是,你开始一步一步地去分解……最后的分解结果如下图所示:

当所有子问题都被成功解决以后,最初通过 HTTP 向服务器发送字符串的总问题也就得以解决了。

你发现没有,在不知不觉中你就通过分层将一个复杂的大问题分解为多个容易解决的子层问题。而实际上,有的子层问题已经被前人解决过了,比如,如何使用 HTTP 协议来进行网络数据的通信。也就是说,最后真正需要关注的问题其实变少了

所以说,从功能性需求角度来看,代码分层本身就是一种拆解复杂问题的好方法。

2. 通过分层来提升代码可扩展性

分层架构的出现,除了解决拆分复杂问题的困境外,还解决了代码可扩展性的问题。

为什么要提升代码可扩展性?因为真实的系统数据一直在不断增加。比如说,一个电商网站的用户访问数会从一万个并发增长到十万个并发,或者从一百万增长到一千万。过去的单体架构之所以很难承载,是因为当我们需要扩展服务器和数据库功能时,一处的代码修改就会影响所有的功能。

分层架构可以将复杂的逻辑切分为多个层,这样大问题就变成了多个小问题,而我们可以很方便地解决每个小问题。每个小问题更容易被抽象为一个组件,当组件功能需要扩充或替换时,修改代码的影响也被有效地控制在有限的范围内,这样组件自身的复用性也就提高了

除了提高代码组件之间的复用性外,分层架构还让我们更容易做服务的横向扩展

什么是横向扩展?简单来说,就是用多台配置较低的服务器共同提供服务,也就是我们熟知的集群部署服务方式。比如说,将 Model 层抽取出来作为通用的数据服务部署,这样既不影响其他业务层,也能在负载增加时,快速扩展服务的承载能力。

三、代码分层架构的优势和劣势

到这里,代码分层结构的优势体现在哪儿就很清楚了,大致可总结为如下:

  • 只用关注整个结构中的其中某一层的具体实现;

  • 降低层与层之间的依赖;

  • 很容易用新的实现来替换原有层次的实现;

  • 有利于标准化的统一;

  • 各层逻辑方便复用。

总结来说,代码分层架构设计主要为了实现责任分离、解耦、组件复用和标准制定

如果不使用分层架构的话,我们的代码逻辑一定会紧紧依赖在一起,修改某一处必定影响其他很多处。从软件项目的角度看,这样会造成非常严重的影响。比如,一个上传功能需要存入下载链接到数据库,如果没有分层,那么当修改存储的路径或类型时,还得修改存储数据库的业务逻辑,想想就很麻烦。

另外,层与层之间进行划分后,也提高了组件之间的复用性,层本身就是一种组件形式,通过统一的接口来与外界进行交互,而不再是按照功能上的依赖来进行交互。而统一的接口是模块之间相互约定的统一标准,只要按照标准来进行代码实现,就不会因为代码改动而影响接口的使用。

虽然代码分层有很多好处,但不可避免地也会有一些劣势。

  • 开发成本变高:因为不同层分别承担各自的责任,如果是高层次新增功能,则需要多个低层增加代码,这样难免会增加开发成本。

  • 性能降低:请求数据因为经过多层代码的处理,执行时长加长,性能会有所消耗。

  • 代码复杂度增加:因为层与层之间存在强耦合,所以对于一些组合功能的调用,则需要增加很多层之间的调用。

四、总结

软件分层架构是通过层来隔离不同的关注点(变化相似的地方),以此来解决不同需求变化的问题,使得这种变化可以被控制在一个层里。

代码分层架构的核心作用有两个:

  • 对于功能性需求,将复杂问题分解为多个容易解决的子层问题;

  • 对于非功能性需求,可以提升代码可扩展性。

总结来说,代码分层架构是一种软件架构设计方法。

  • 从软件的功能性需求角度看,分层是为了把较大的复杂问题拆分为多个较小的问题,在分散问题风险的同时,让问题更容易被解决,也就是我们常说的解耦。

  • 从架构(非功能性需求)角度看,分层能提升代码可扩展性,帮助开发人员在相似的变化中修改代码。

其实,复杂的设计概念和简单的代码之间存在一种平衡,这就是分层架构。

  • 代码分层架构设计的思维模型是简化思维,本质是抽象与拆解。

  • 代码分层架构设计的目的是将复杂问题拆分为更容易解决的小问题,降低实现难度。

  • 代码分层架构设计的原则和方法是通用方法,可以应用到其他需要分层设计的地方。

所以,分层架构从来不是目的,只是让我们的软件变得更好的其中一种思维方法而已。

五、课后思考

除了分层架构外,你还熟悉哪些其他架构设计模式?有哪些优势和劣势?

软件架构设计分层模型和构图思考

今天谈下架构设计中的分层思维和分层模型以及基于分层思维下的架构构图逻辑。

架构思维概述

对于架构思维本身仍然是类似系统思维,结构化思维,编程思维等诸多思维模式的一个合集。由于架构的核心作用是在业务现实世界和抽象的IT实现之间建立起一道桥梁,因此架构思维最核心的就是要理解到业务驱动技术,技术为最终的业务服务。要真正通过架构设计来完成业务和技术,需求和实现,软件和硬件,静态和动态,成本和收益等多方面的平衡。

软件架构设计分层模型和构图思考


在前面多篇文章已经提出,架构设计中有两个重点,一个是分解,一个是集成。

分解是最基础的,架构的重点就是要对复杂问题进行分而治之,同时保证分解后的各个部分还能够高内聚,松耦合,最终又集成为一个完整的整体。分解核心是定义问题,因此架构首先仍然需要理解清楚需求。

集成是配合分解完成的动作,最终分解完成的各个组件或子系统,通过合适的接口设计,最终还能够集成为一个完整的整体,分解仅仅是加速开发和降低问题复杂度,如果分解后的内容无法集成在一起,那么分解就没有任何意义。

分解+集成可以理解为架构最核心的思考方式和方法。

在分解完成后,一个大的系统已经拆分为了诸多的小模块,或者一个小模块实现本身又分为了多个步骤阶段。那么零散的节点必须向上汇集和归纳,形成一个完整的架构。

而这个架构的形成要给关键就是要又分层思维。架构分层是谈架构绝对绕不开的一个点,通过架构分层可以更好地全面理解业务系统或功能实现。

云平台三层架构:资源-平台-应用

在规划大架构的时候,常会参考云计算的标准三层架构,即IaaS层,PaaS层,SaaS层。对于IaaS层重点是IT基础设施和虚拟化;PaaS层重点是构建平台层服务能力;而对于SaaS层则是具体的应用。

对于资源层从物理资源,再到虚拟化逻辑资源,从虚拟机到现在更加轻量的容器资源。而对于平台层原来只谈技术平台,但是当前又进一步拆分出业务平台,也可以理解成当前说得比较多的中台层。

同时在平台层和应用层之间增加了服务层,实现资源和服务的解耦。

软件架构设计分层模型和构图思考


如果涉及到物联网类应用,一般还会在底层增加网络层和感知层,比如一个智慧城市标准平台和应用的架构图类似如下:

软件架构设计分层模型和构图思考


在平台+应用构建模式下,一般在平台和应用之间还会有一个单独的服务层来实现接口服务对外的能力开放。资源+服务+应用也是我们常说的SOA分层架构模式,因此对于服务层也可以单独拆分出来作为一个小分层。

软件架构设计分层模型和构图思考


问题1:数据库和数据层

在构建一个完整的总体架构的时候,实际上没有数据层这个概念,数据层是在表达单个应用系统的分层架构实现的时候才会出现的内容。

在总架构图里面把类似结构化数据库,非结构化数据等全部列出单独一层这个也不对,这个应该是在技术架构里面体现。

还有一种是单独分出一个数据层,将大的公共基础数据列出,比如上面谈的智慧城市架构图。如果这些基础数据存在共性能力朝上提供,那么可以归纳到PaaS平台层,在PaaS平台层单独分出一个数据平台域来进行体现。

问题2:服务层和服务

在构建整体架构的时候可以单独出一个能力开放平台或服务层,但是不用体现具体有哪些业务服务能力。因为单独出业务服务能力本质已经属于应用层内容,即应用又细化拆分为了业务中台和前台应用,中间衔接的服务。

我们可以参考网上的另外一个构图,如下:

软件架构设计分层模型和构图思考


这个构图既不像云平台中的分层架构,也不像应用功能实现中的分层架构。实际可以看到如果体现单独的支撑层,支撑层已经类似现在经常说到的业务中台和能力提供。

那么整个架构应该为 技术平台+中台+应用 方式来进行构图。

SOA分层:组件-服务-流程

对于SOA架构分层,重点要体现的就是服务,对于组件本身是属于逻辑资源层的概念,而对于服务则是资源对外暴露的能力抽象。

软件架构设计分层模型和构图思考


SOA架构分层重点就是要体现出独立的服务层,注意不是画服务总线,这里可以单独画出具体提供哪些业务服务能力,技术服务能力。在采用SOA架构进行开发的时候,整体业务系统拆分为4个组件,10类服务域,5类流程,那么在构建的时候重点就是将上述组件,服务域和流程类体现出来。

对于参考SOA架构来进行的构图,参考如下:

软件架构设计分层模型和构图思考


这里的数据层最好改为标准的组件层,更加贴近SOA架构模型。在图中的服务层已经可以看到一个个独立的API服务接口。如果服务接口数据大,一般只会划分到服务域,比如用户中心服务,采购类服务等。在这种方式下构图参考如下:

软件架构设计分层模型和构图思考


在上图中结合了云和SOA两种架构融合在一起,对于上图中的服务层实际可以理解为组件资源层和服务接口层的融合。更好的构图方式应该是拆分为标准的中台资源层-服务层-应用层。

云和SOA架构融合

软件架构设计分层模型和构图思考


注意对于云分层架构重点强调的是基础设施,平台和应用三层架构。而对于SOA架构强调的是资源,服务和应用三层。而对于对于传统的应用系统的构建一般又包括了IT基础设施,技术平台,数据库,中间件和应用。再到应用系统本身的分层架构可能又是标准的三层架构模式等。

这些架构分层方法都帮助我们进一步融合分层架构模式。

架构分层有很多方法,包括基础设施层,平台层,组件层,支撑层,服务层,应用层,数据层,展现层等。多种分发导致分层模型反而出现歧义和模糊。

在这里我们从技术架构和应用架构两个层面来谈,技术架构沿用云计算的三层模型;而对于应用架构则采用eTOM模型标准的资源,服务,应用三层模型。那么两种分层架构模型的融合则是一个完整的云和SOA融合的分层架构模型。

即云计算的三层中,每一个层次本身又可以进一步拆分为资源,服务和应用三层。

拿IaaS层来说,最底层的物理资源虚拟机等是属于资源层内容,通过IaaS层资源能力提供API接口作为技术服务进行能力开放,即是服务层;最终基于资源能力,构建了一个公有云的面向公众的运营服务平台,本身又属于应用层的内容。而对于SaaS层,则底层的业务组件是资源,抽象的API接口是服务层,最终的前端业务或流程是应用功能实现。

应用架构分层

回到单个应用的架构分层,谈得最多的就是常说的三层架构模式。在软件架构中,经典三层架构自顶向下由用户界面层(User Interface Layer)、业务逻辑层(Business Logic Layer)与数据访问层(Data Access Layer)组成。

在整个实现过程中,可能还会增加独立的Facade层,或独立的API接口服务提供层,统一的DTO数据传输对象层等,但是这些都不影响整体的三层逻辑结构。

软件架构设计分层模型和构图思考


三层架构本身也和一个业务功能实现的完整对应,在数据访问层处理数据获取和持久化操作,在业务逻辑层对业务规则进行处理,在界面展现层进行相应的前端展现和用户交互。

而谈到领域建模的时候,又引入了领域模型中的分层架构,如下:

软件架构设计分层模型和构图思考


领域驱动设计在经典三层架构的基础上做了进一步改良,在用户界面层与业务逻辑层之间引入了新的一层,即应用层(Application Layer)。同时,一些层次的命名也发生了变化。将业务逻辑层更名为领域层自然是题中应有之义,而将数据访问层更名为基础设施层(Infrastructure Layer),则突破了之前数据库管理系统的限制,扩大了这个负责封装技术复杂度的基础层次的内涵。

当然,也有融合了领域模型和传统三架构思路后的技术架构如下:

软件架构设计分层模型和构图思考


领域层和业务逻辑层

在领域建模的一个核心是领域模型,领域模型不再是一个个独立的数据库表或数据对象,而是一个业务对象或领域对象。因此领域层是面向领域对象而设计实现,而业务规则能力本身也是属于领域对象对外提供的能力接口。即业务规则本身也是领域对象暴露的能力。

传统业务逻辑层实现往往是一个数据对象对应一个DAO,一个Service和一个Interface。而领域模型下DAO可以是分开的,但是Service逻辑层往往则更多应该按领域模型思路对DAO层的能力进行组装和聚合。

独立应用层拆分

软件架构设计分层模型和构图思考


在我原来理解里面,领域层提供领域模型和领域服务能力接口,而应用层更多的是对领域层多个领域对象模型提供的服务能力进一步进行组装和编排,然后再暴露给前端应用。

谈到应用层的概念,实际上可以理解为前端应用中存在的共性能力的进一步下沉。即应用本身只是用户业务功能实现的承载,但是这个功能的实现可以通过多种前端展现形式,比如传统的CS桌面应用,BS应用,或手机端APP。

在电商里面,一个商品订购就是一个独立的应用,用户可以在APP完成,也可以在BS端完成,但是不论在哪里完成最终应用层提供的能力都应该一样。比如完成一个商品订购需要同时和底层的订单,库存,支付多个服务进行交付和协同。那么这个逻辑显然不适合同时在BS端应用和APP端应用中进行重复编写和开发。那么这个内容就应该在应用层实现。

如果回到微服务和中台架构下,这个应用层拆分更加必要,即通过应用层来下沉共性的服务组合和组装逻辑,这个逻辑和协同不应该属于任何一个前端应用。

界面层还是接口层

在开发一个聚合能力的中台微服务模块的时候,可以看到这个微服务模块本身并没有界面展现层,那么该微服务的最上层仅仅是提供API接口的接口服务层。

该API接口服务能力既可以提供给APP前端,也可以提供给BS端使用。

软件技术架构分层

软件技术架构构图,分层仍然可以沿用软件三层分层模型,重点是说明清楚各层用到的关键技术组件或技术服务能力。比如软件开发三层模型的技术架构分层如下:

软件架构设计分层模型和构图思考


如果本身就是一个技术平台,类似大数据平台,那么我们在整体构图的时候仍然需要考虑先进行分层,再详细说明每层里面的技术内容。

比如对应一个大数据平台,包括了大数据采集,大数据存储,大数据处理,大数据分析和应用,那么这个就是关键的分层,可以基于这个分层再来考虑各层采用的关键技术。

软件架构设计分层模型和构图思考


对于技术栈构图基本也可以参考技术架构构图模式进行。

软件架构设计分层模型和构图思考


技术架构重点需要回答的就是你在进行软件架构设计过程中,究竟会用到哪些关键技术,哪些开源产品或工具等。可以细化到具体的技术产品,也可以仅细化到产品类型。

比如消息中间件,你可以细化到采用RabbitMQ,也可以在技术架构中只体现采用消息中间件。

技术架构和软件功能分层架构唯一相同的就是分层,技术架构在各个分层里面都没有具体的业务功能点和实现内容,仅仅是关键技术点说明。

单个应用功能架构

注意应用功能架构完全是重点描述应用系统具备哪些功能,一个功能究竟是采用什么三层技术架构实现并不用关心。因此功能架构不应该体现数据层,逻辑层,技术点这些内容。

那么对于一个应用系统的功能如何分层?

我们可以参考业务分层分类,将业务分为基础支撑层,执行层,决策管理层。这样基本的分层模式就出来了,基于该方式可以完成一个功能架构构图。

软件架构设计分层模型和构图思考


对于单个应用来说一般不会自身有云平台,PaaS平台这类概念。但是单个应用构建一定存在共性技术支撑平台能力,比如有自己的流程管理,各自共性技术功能组件等。因此单应用构建还可以采用基础技术支撑层+应用层+门户层的方式进行构图。

在应用层再按具体的业务域或业务阶段进行进一步细分。

软件架构设计分层模型和构图思考


架构图的分层构图逻辑

软件架构设计分层模型和构图思考


在前面基本给出了不同类型的架构图的核心分层逻辑,可以看到在画架构图的时候尽量不要混合使用不同场景下的构图方式,否则就导致整体架构图混乱。

在画整体架构的时候一般需要重点参考云三层架构,SOA三层架构的构图模式进行构图。而在细化到某一个应用系统的时候,仍然还需要分清是构建技术架构图还是功能架构图,两者本身的分层逻辑也存在很大的差别而不能混用。

架构图的构图逻辑

要完成一个完整的架构图构图,可以先拆分为两边+中间。两边一般是放具体的标准,规范等,比如安全管理,质量管理,技术标准规范,开发运维规范等。

中间即是重点需要考虑进行分层构建的地方。

在前面也谈到了中间部分重点参考云计算和SOA的架构分层逻辑。一般来说核心的还是资源层,平台层,应用层,门户层。而对于应用层本身又可以考虑业务域进一步拆分,或者根据价值链或业务生命周期拆分为多个阶段域再展开描述。

在云和SOA下,更加强调平台+应用构建模式。

而两者之间一般是服务层,通过SOA平台或API能力开放平台来统一接入和发布服务,以形成一个完整的资源+服务+应用的松耦合架构。

同时一个完整的架构本身就是多视角的,如下:


功能架构往往可以给具体用户和业务人员看,而对于技术架构往往更多是内部团队开发人员研讨使用。而设计到资源和平台的架构图往往又是运维工程人员进行部署架构搭建的重要参考。因此不同维度的架构分层属性本身不能随意融合使用,而导致架构图混乱。




来源:

https://www.toutiao.com/a6897095696332177923/

“IT大咖说”欢迎广大技术人员投稿,投稿邮箱:aliang@itdks.com



来都来了,走啥走,留个言呗~




 IT大咖说  |  关于版权 

感谢您对IT大咖说的热心支持!



相关推荐


推荐文章








以上是关于3.设计模式之分层思维:为什么要做代码分层架构?的主要内容,如果未能解决你的问题,请参考以下文章

14.软件架构设计:大型网站技术架构与业务架构融合之道 --- 业务架构思维

软件架构设计分层模型和构图思考

软件架构思想系列分层架构

软件架构之分层架构理解

解构领域驱动设计:领域驱动设计的核心之分层架构

测试驱动设计和分层架构