1.1.2 软件复杂度与结构
软件规模在很大程度上决定了系统的复杂度,这是一个显而易见的结论。那么,结构为什么也会影响软件复杂度呢?这就是本节要讨论的话题。
1.结构的表现形式和关注点
关于结构,我们来看一个示例。
图1-8展示了一个常见的分布式架构。我们在图中看到了Web服务与业务服务之间的分离,并引入分布式缓存来提升数据访问性能。更进一步,还使用消息中间件来实现不同业务服务之间的消息通信,从而构建低耦合的系统架构。当然,我们还可以在图中添加搜索引擎、分库分表等技术体系。在现实中,当面临系统架构设计问题时,可以通过引入各种技术系统逐步完善架构,直至构建具有庞大体量的大型集群系统。本质上,软件架构重构的需求和动力来自对系统质量要求的不断提升,如性能需求、解耦需求、搜索查询需求等。因此,结构导致软件复杂度提升的第一个要点是质量。
图1-8 分布式架构
前面讨论的结构指的是技术结构,接下来将讨论组织结构。正如康威定律(Conway’s Law)所指出的,设计系统的组织,其产生的设计和架构等价于组织间的沟通结构。图1-9展示了不同公司所采用的组织结构。
图1-9 不同公司具有不同的组织结构
康威定律无处不在,从传统的单体架构到目前主流的微服务架构实际上都是康威定律的体现。现在很多开发团队本质上是分布式的,单体架构的开发、测试、部署、协调、沟通成本巨大,严重影响效率且容易产生冲突。将单体架构拆分为微服务,每个团队独立开发、测试和发布自己负责的微服务,互不干扰,系统效率得到提升。可见,组织和系统架构之间存在映射关系:一方面,如果组织结构和文化结构不支持,通常无法成功建立有效的系统架构;而另一方面,如果系统设计或者架构不支持,那么无法成功建立一个高效的组织。图1-10展示的就是一个不合理架构设计的示例。
图1-10 不合理架构设计
在图1-10中,我们看到了经典的软件系统三层架构模式,即用户展示层调用业务逻辑层,而业务逻辑层进一步调用数据访问层。显然,每一层的组件应该有明确的职责,用于用户交互和展示的组件不应该包含在业务逻辑层中。但在现实场景下,由于团队或部门之间的岗位职责和工作边界的不清晰,可能会出现图1-10所示的将页面控件放在业务逻辑层中的不良设计。这就是组织所带来的问题导致软件复杂度提升的一个典型案例。
2.结构的应对策略
针对结构所引起的软件复杂度,业界也有成熟的处理方案,基本思想是通过软件架构模式保证系统结构清晰有序。
图1-11展示了一种通用的分层结构。分层结构是最基本,也是最常见的架构模式,每一层次之间通过接口与实现的契约方式进行交互,可以严格限制跨层调用,也可以支持部分功能的跨层交互以提供分层的灵活性。典型的三层结构及各种在三层结构上衍生的多层结构就是这种风格的具体体现。
图1-11 分层结构
我们再来看一个更加复杂的分层结构,如图1-12所示。该图展示了一个客服系统的服务架构。整个系统可以分为前台服务层、中台服务层、集成服务及外包客服系统。其中,前台服务层提供面向用户的业务服务,中台服务层提供通用基础服务,而集成服务则用于完成与外部各个外包客服系统之间的有效集成。通过这种分层方式,各个服务层职责明确、各司其职。
图1-12 客服系统的分层架构
介绍完分层结构,我们再来看一个典型的架构模式——管道–过滤器模式。管道–过滤器模式是用于解决适配和扩展性问题的代表性架构模式。管道–过滤器模式在结构上主要包括过滤器(Filter)和管道(Pipe)两种元素,如图1-13所示。过滤器负责对数据进行加工处理。每个过滤器都有一组输入(Input)端口和输出(Output)端口,从输入端口接收数据,经过内部加工处理之后,传送到输出端口。同时,数据通过相邻过滤器之间的连接件进行传输,管道可以看作输入数据流和输出数据流之间的通路。
图1-13 管道–过滤器模式
管道–过滤器模式将数据流处理分为几个顺序的步骤分别进行,一个步骤的输出是下一个步骤的输入,每个处理步骤由一个过滤器来实现。每个过滤器独立完成自己的任务,不同过滤器之间不需要任何交互。这些特性允许将系统的输入和输出看作各个过滤器行为的简单组合,独立的过滤器不仅能够降低组件之间的耦合程度,而且可以很容易地将新过滤器添加到现有的系统之中。同样,原有过滤器也可以很方便地被改进的过滤器所替换,以扩展系统的业务处理能力。
架构模式是一个丰富而复杂的话题,业界存在一大批即插即用的架构模式,这里无意一一列举,读者可以自行参考相关书籍进行系统学习。