软件架构设计是一门划分边界的艺术,其作用是将软件分割成各个组件,以达到约束边界两侧的依赖关系。
一些边界是项目开始时划分好的,一些是之后才划分的。项目初期的划分边界,目的是为了尽量将一些决策延后进行,并保证未来的决策不会影响到核心业务逻辑。
之前有提到过架构师追求的目标之一就是最大限度的降低系统所需的人力资源。而最消耗人力资源的就是系统的耦合度。尤其是那些过早做出不成熟的决策所导致的耦合。
只有让决策,和业务的业务需求无关,才能解决过早做出不成熟的决策。比如,要采用的框架,数据库,Web服务器,工具库,依赖注入等,这些都是决策,和业务无关,它们都是可以延后进行的。一个设计良好的系统,不应该依赖于这些细节。
几个悲伤的故事
P公司
P公司
有一个桌面软件很火,后来Web
潮流来了,客户要求提供Web
版。于是招了一群二十几岁的Java
程序员,这群小子满脑子就是想将大规模服务器集群应用起来,它们将系统应用的各层(3个)应用分不到一个大型集群中,还设计了一套各个服务交互调用的系统。
导致后续的需求修改,经常需要调整多个服务。而实际上,开发过程中是没有大型服务器集群可用的,最讽刺地是该公司从来就没有销售过一个需要服务器集群的系统。它们部署过的系统都是在一台服务器上运行的。
然而它们还是需要在这一台服务器上,部署3
个系统 一个交互系统。花费了大量的成本。
悲剧在于,架构师将开发成本放大了数倍。
W公司
W公司
采用了SOA架构
,以服务划分系统,相互调用,导致测试及部署麻烦。这种架构并不是错误,错误在于过早的采用这种架构,这种错误让该公司消耗了大量的人力成本。
FitNesse
作者和他儿子开了一家公司叫FitNesse
,他们开发了一个系统,将服务器,数据库的选型决策全部延后了,到最后发现甚至数据库和服务器都没必要用,自己开发一个简单点的就够用了,并且隔离做得很好,后期要换成Mysql也会很快。这让他们在项目开始期间,避免了服务器调试,数据库管理等工作。
应该在何时、何处画这些线
边界应该画在不相关的事情中间。比如GUI
和业务逻辑
无关,那么中间就应该有一条边界线,数据库
与GUI
无关,中间也应该有一条边界线,数据库
与业务逻辑
无关,所以中间也应该有一条边界线。
数据库
与业务逻辑
无关,这个会遭到一些人反对,大部分人都已经习惯认为数据库和业务逻辑是密不可分的,有些人甚至认为数据库相关逻辑部分本身就是业务逻辑的体现。
这种想法是错误的,数据库
应该只是业务逻辑
间接使用的工具,业务逻辑
不需要了解数据库
的表结构,查询语言,或者数据库内部的实现细节。业务逻辑唯一需要知道的是,有一组可以用来查询和保存的数据函数。只有这样,才能将数据库隐藏在接口后面。
隐藏在接口背后的数据库:
可以看到,DatabaseInterface
和Database
两个类,都可以不知道DatabaseAccess
的存在。这种结构可以使得业务可以有更多的可能,可以使用多种数据库的实现。
输入和输出怎么办
对系统架构的划分边界来说,IO
是无关紧要的,通常体现在GUI
上,因为即使没有GUI
,核心的业务逻辑依然可以运行,所以GUI
和BusinessRules
之间,也要有一条分界线。这样以后切换GUI
也很方便,BusinessRules
不需要了解GUI
的细节,即GUI
依赖于BusinessRules
,业务并不依赖于GUI
。
插件式架构
软件的发展历史,就是如何想方设法方便的增加插件,从而构建一个可扩展,可维护的系统架构的故事。系统的核心逻辑必须和其他组件保持独立。那些其他的组件,要么是可以去掉的,要么是可以有多种实现的。比如GUI
和数据库
。
插件式架构的好处
我们希望修改其他组件时,可以尽量的不影响核心业务逻辑组件,同时也不希望修改一个组件,会有另一个无关的组件受到影响,系统不该如此脆弱。
如果是插件式架构,就等于构建起了一道防火墙。因为组件隔离是以不同原因的更改,和不同速率作为依据的。这其实就是SRP
单一职责的具体实现,SRP
的作用就是告诉我们应该在哪里画边界线。
本章小结
在软件架构中,应该画边界线,将系统分割成组件,其中有一部分是核心逻辑组件。然后让非核心逻辑组件,依赖于核心逻辑组件。而不是相反。
其实这也是一种对依赖反转原则(DIP)
和稳定抽象原则(SAP)
的具体应用,依赖箭头应该由底层具体实现,指向高层抽象的方向。