系统设计 — 分层

大概可以认为现在所有的软件架构设计都遵循了分层的思想,在互联网的基础建设中也是按照这个思路,HTTP(应用层)是构建于TCP之上,TCP(传输层)是构建于IP之上,IP(网络层)层是架构在以太网(链路层)之上。
上层使用了下层提供的服务,下层对于上层一无所知。每一层都对自己的上层隐藏细节。

优点:

  • 每个层都是独立的,上层无需关心下层的细节和实现,只是当做一个有机整理来理解。
  • 职责清晰
  • 在服务接口不变的情况下,可以随意的替换下层的实现。并将层次间的依赖降到最低,例如我们的电脑无论接入的是电信还是网通都不需要关心,只要接入的以太网层的协议是不变的
  • 分层有利于标准化工作。TCP和IP就是关于它们各自层次如何工作的标准
  • 基础组件层构建完后,就可以为上层搭建符合自己的应用,例如HTTP,SSH,FTP都是构建于TCP层的,若是没有一个公共的TCP层,那么所有的高层协议都需要编写各自的底层协议

缺点:

  • 层次并不是封装所有的东西,所以总存在部分相互耦合的东西,这种耦合的部分的变化就会带来级联的改动,相信写过代码的人都知道。
  • 过多的层次会影响性能,因为每一层都做到互相的隔离的话,那么对于每一层都对应自己的表现形式,转化就无法避免。

对于分层架构中,最难的还是决定建立哪些层次以及每个层次的职责是什么。

自己上大学的时候,学的是VB和delphi,因为那个年代流行的分层架构是物理层面上的分层,也就是说客户端和服务端的分层(C/S结构),将界面和业务逻辑写在客户端中,服务端就仅仅是一个数据库,若是业务常见较为简单的场景,仅仅对于数据的查询和修改,那么这些是非常实用的,拖拽式的界面设计,然后通过组件连接到服务端的数据库。
但是,随着业务模型的越来越复杂,若还是简单的将逻辑嵌入到界面中,带来的就是代码的难以复用,会生成很多的冗余代码,一个业务逻辑的改变,就需要照顾到所有拥有这个业务的界面。
这个时候冒出来的一种解决方案就是将逻辑写到服务端,也就是数据库的存储过程中,但是存储过程作为一种半标准化产品,只提供了有限的结构化机制,如此将再次导致冗余,散落的代码,同时这种半标准化的存储过程中做数据库迁移的时候会导致致命的问题,因为不同厂商的语法标准存在不一致的情况。这个也是为什么存储过程在我们的业务开发中慢慢消失的原因。

后来随着WEB的兴起,这个时候出现的就是我们熟悉的三层模型了,在原来客户端/服务端的物理分层基础上,将系统从逻辑上分为三层:
1.表现层:实现具体用户界面,只关心用户和软件之间的交互
2.领域层:实现领域业务逻辑,系统中真正的核心
3.数据源层:与数据库、消息系统、事物管理器及其他软件通信包的通信

三层之间,表现层主要职责是将用户那里获取的信息解释成领域层或是数据源层上的各种动作,领域层对于表现层的数据进行校验,以及根据根据从表现层接受的命令来确定调度哪些数据源层,根据数据源层的具体是:消息系统还是数据库来做进一步操作,更多的是持久化数据。

理论上来说,领域层应该屏蔽表现层对于数据源层的操作,但是实际的工作上,我们可以看见很多表现层直接操作数据源层的情况,若是在一个较简单的系统中,如此做法是可取的,也是效率比较高的,并没有死的规律,只有灵活的系统。

对于一个系统而言,应该都尽量采用三层模型,至于怎么分层取决于系统的复杂度,不同复杂度可以通过放在不同的类,或是不同包中等手段来操作。
伴随着分层就有一个关于依赖的原则:领域层和数据源层不要依赖于表现层,就是这两个层中代码不要出现在表现层中,如何可以方便的替换表现层的实现,至于领域层和数据源之间的依赖可参考一个OO原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。

在这三层模型中,最难处理的就是领域层的划分,那些改放入该层,哪些该放入表现层。一个判断原则是,新增一个表现层,在需求数据不变的情况下,是否还需要重复实现某些功能,若是存在的话,表明一些原本领域层代码放在了表现层。

上面提到了在客户端/服务端的物理分层前提下,将业务系统的实现划分了三层,那么各层应该在物理上该归属到哪里呢?
表现层,这个层面的归属是争议比较大的,在过往的历史中,早期都是将表现层放在客户端(C/S)的,后来随着WEB的兴起,慢慢转向放在服务端的(B/S),尽量保持一个瘦客户端(浏览器),但若是某些业务对于用户太过复杂,通过目前的WEB GUI 不能满足我们的场景,就会选择胖客户端,但是趋势还是BS,尽量将表现层放在服务端。

领域层有放在客户端,也有放在服务端,放在客户端的优势是减少系统交互,提高响应速度,同时在断网的情况也能使用。缺点是升级和维护的代价太高,因为你可能存在多个不同展示方式的客户端,将业务逻辑散落在不同地方。
还有种是将领域层一分为二,一部分放在客户端,一部分放在服务端,当然这种是最差的设计,因为你无法确认任意一块的逻辑放在哪里。
最后种当然是将所有的业务逻辑收敛到服务端,如此可以方便的维护逻辑,同时更方便的水平扩展。但是我们在实际操作过程中,为了性能,会将一部分简单的非核心逻辑放到客户端,例如JS中。

作者: inter12

在这苦短的人生中,追求点自己的简单快乐

发表评论

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