商旅系统一做就是三年,这期间系统由初建到流畅运行,架构上经历了许多次的重构和整理。重新回首,整理一下系统,谈谈自己对逻辑架构的设计实践的一些感想。
商旅系统前后逻辑图如下所示:
这张图是商旅系统最新整理系统逻辑图,与最初相比,有比较多有趣的地方,值得评鉴。
分层架构的清晰。
原有三层的分层架构依旧保留,进行了更进一步的抽象,比如系统管理平台和作业平台细分内容没有进行展示;将应用服务层CC的子系统框架模糊化,定义CC作业平台为渠道端子系统,其服务层和支撑层是子系统内部技术架构,不在总体逻辑架构图展示;对外联和业务系统所属服务进行了适当合并;数据层几乎全部重新定义。由于整个系统庞大,子系统众多,在每个分层类,进行了适当的主题与的划分,这些变更使得逻辑架构对于分层、子系统、模块的职责更为清晰。
使用层的本质思想有:1)将系统的大型逻辑结构组织为独立的、职责相关的离散层,清晰、内聚,并且关注分离;2)协作和耦合从较高层到较低层进行,避免从较低层到较高层的耦合。
使用层有助于解决下面的问题:1)源码的变更波及整个系统;2)应用逻辑和界面显示交织在一起,难以复用和分布;3)技术逻辑和业务逻辑交织在一起,难以复用、分布和替换;4)领域之间的高度耦合,难以为不同开发者清晰地界定和分配任务
关于分层的优劣也是一直存在争论,这里不做赘述。刀可伤人也可助人,关键看怎么用。架构设计上,使用分层是有助于进一步理解系统,就是达到了目的。后面的逻辑架构图,也还需要进一步完善。分层内一个个方框代表的子系统,粗细粒度大致相当,但还有一些较粗的可进一步展开,如作业平台和网站,规模是比较大的,可以再次横向分层;业务数据等数据库,可以通过Schema纵向划分。
数据层的问题。
从前后的架构图相比,数据层新增的东西比较多,变动最多的地方往往也是问题的最多的地方。运行过程中数据库的许多问题,与逻辑架构设计关联的有以下几个。
第一个问题是业务数据和非业务数据的划分,定义不够明确,也就是数据的纵向分层规划。这可能是由于项目组织架构的关系,我们是分组开发,每个开发组依照自己的经验和习惯,对业务数据和非业务数据进行分解存放;还有对公用数据的引用、关联和映射,比较混乱,使得在数据库这块有不少的关联结。
第二个问题是在数据层的划分中,遗漏了文件数据、缓存数据和报表结算数据的分离。首先是文件数据,开发、测试过程都是单点,而部署是多点,这样很多存放于应用服务器的文件没有办法被其他节点共享;然后访问的方式有的是通过流的方式读取,有的是通过HTTP的站点访问,还需要一些专门的文件同步工具在前后端子系统之间进行文件同步。这些处理过于复杂,导致运维同事经常报故障说XXX文件又点不开了,或者无法下载。缓存和报表、结算库的分离是与性能有关的,最初设计中的忽略,于是大家在开发的时候,复杂的低频率而又需要长时间计算运行的业务逻辑和简单的逻辑一起放到了主库中,业务高峰时段会导致CPU消耗达到100%,耗掉硬件资源,从而影响正常业务运行。
职责间协作。
逻辑架构关心的不仅仅是如何将系统分为不同部分,还关心各部分之间是如何交互的。而识别协作,并将具有共性的协作抽象成通用机制,是逻辑架构设计的重点和难点。前面的图里未做描述,从最新的图来看,渠道层和服务层之间,是通过远程调用,走的是Hessian协议;服务层和数据层之间是方法调用,利用了JDBC等中间件技术。层与层之间的分离是比较清晰的。实际的运行过程中,服务层的各个子系统,也需要相互调用,数据层各单元也存在着同步数据的链接,层内部的协作,较难抽象出新的连接件逻辑单元,目前通用的方法是如果存在交叉调用,就继续往下放到数据层。比如营销和客户两个服务需要相互调用,那么往往委托给存储过程实现,分层和分离难以协调的少部分逻辑,放在数据库实现里解决,取一个平衡。
关于逻辑架构设计。
逻辑架构重在描述系统的职责划分和职责间的协作关系,它是软件的宏观组织结构。之所以称为逻辑架构,是因为并未决定如何在不同的操作系统进程或网络中物理的计算机上对这些与元素进行部署。
层和子系统的粗细粒度,需要考虑在建系统的特点。根据系统大小,逻辑结构可以大到分层和子系统,也可以小到模块或者一个个的类。但不管如何划分,需要针对系统的主要组成部分,强调内聚的职责。还需要描述层次的调用原则,如较高层可以调用较低层,反之则不然。
严格的分层架构中,层只能调用与其相邻的下层的服务,一般用于网络协议应用。而在商旅这样的信息系统中,我们采用了比较宽松的分层架构,较高的层可以调用其下任何层的服务。
逻辑架构的最佳来源是用例分析。基于用例的分析方法会使逻辑架构的设计变得有序——通过对每个关键用例的分析,从逻辑上将用例实现为一组功能块的特定组合,最后综合这些用例分析成果,将一个个独立的协作归纳合并成整个软件系统的逻辑架构。而在用例分析方法产生之前,功能模块的确定多多少少带有些“硬”想出来的味道,特别是并不直接承载业务功能的模块有时比较容易遗漏,直到大规模编程实现阶段才发现。