分层架构是运用最为广泛的架构模式,把一个软件系统进行分层,是我们目前做工程项目的一个共识,我们最初学习的分层架构就是经典的三层架构了。它自顶向下分成三层:
- 用户界面层(User Interface Layer)
- 业务逻辑层(Business Logic Layer)
- 数据访问层(Data Access Layer)
在传统的单体应用中,因为业务不算复杂,这种分层并没有什么问题,把数据的渲染交给用户界面层,把核心业务逻辑放到业务逻辑层,然后将数据库的访问交给数据访问层。
但是随着业务越来越复杂,问题也随之而来:
- 需要依赖的基础设施也不仅仅只有数据库这样单一了
- 很多参数的校验,我们开始纠结是放到用户界面层还是业务层。
- 缓存是放到哪里去控制
......
代码开始变得复杂,很快只有上帝能看懂了,然后写代码往往就是牵一发而动全身。
我们都知道在设计模式中有一个很重要的原则就是依赖倒置,他包含了三层含义:
- 高层模块不应该依赖低层模块,两者都应该依赖其抽象
- 抽象不应该依赖细节
- 细节应该依赖抽象
所以设计模式中产生了一个模式——适配器模式:
将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
在中间件软件爆发的今天,同一种功能的中间件可能会有非常多的选择。比如:
- MQ: RabbitMQ, Kafka, ActiveMQ, RocketMQ......
- SQL: MySQL, PostgreSQL......
- NoSQL: Redis,MongoDB, ElasticSearch......
- Job: Elastic-Job, XXL-JOB.....
......
除此之外还有各种供应商的需要有备用通道:短信,邮件,推送,业务供应商......
如果我们在业务逻辑中去关注这些东西,毫无疑问,我们的业务逻辑就会很繁琐:
if ( config == 'A' ) {
// statement 1
} else if ( config == 'B' ) {
// statement 2
} else if ( config == 'C' ) {
// statement 3
} else if ( config == 'D' ) {
// statement 4
} else {
// default statement
}
特别是业务中去选择供应商的时候,我们通常是要有好几个备用通道的,但是我们的业务逻辑本身只是关心:这件事做了没,而不是到底用哪种方式去做。
所以六边形架构被提出了。六边形架构提倡用一种新的视角来看待整个系统,该架构中存在两个区域,分别是“外部区域”和“内部区域”。在外部区域中,不同的客户均可以提交输入;而内部的系统则用于获取持久化数据,并对程序输出进行存储(比如数据库),或者在中途将输出转发到另外的地方(比如消息)。
我们在设计系统的时候,往往过于关注数据库,Http接口等基础设施的设计,而忽略了我们需要关注的业务。在复杂系统中,最容易变化的也是业务形态,产品经常会要求改来改去,因为业务本身就在不断地演进,如果我们一开始就基于数据库作所有的设计,那么势必一旦遇上业务的修改,库表肯定也需要对应先进行变化。假如我们融入六边形架构,将数据库和暴露的Controller都视为是基础设施,先去关注业务的模型和代码,Class的修改比要数据库改起来要简单的多。另外一方面,也大大提高了程序的可测试性:在没有准备一堆基础设施(数据库,接口,异步通知等等)情况下,可以先测试逻辑的完整性。
另外,有时候随着业务增长有的基础设施是会需要进行替换的,采用六边形架构之后,这种更换的成本就会降低。另外如果出现需要使用Web Service的客户,我们也不必纠结于之前的HTTP接口,直接开出一套新的协议代码供客户使用,而不会纠结领域部分代码有逻辑上的缺失。
采用六边形架构之后,我们的领域模型也会更加独立,更精简,在适应新的需求时修改也会更容易。