我们讨论了充血模型、聚合、领域事件、集成事件等概念,需要一个软件架构把这些组件组合在一起,才能形成一个完整的微服务。
分层架构是把各个组件按照”高内聚、低耦合”的原则组合在一起的非常有效的形式。
各层代码的内部紧密地聚合在一起,而各层之间通过清晰的边界划分和低耦合的协作来共同完成应用程序的目标。
经典的软件分层架构是三层架构,应用程序分为用户界面层、业务逻辑层、数据访问层,如下图所示:

在三层架构中,对数据库进行访问的代码被放到数据访问层中,业务逻辑的代码被放到业务逻辑层中,而数据校验、界面交互、数据显示等代码被放到用户界面层中。
各层只能调用下一层的代码,也就是业务逻辑层只能调用数据访问层的代码,而不能直接调用数据访问层的代码。
三层架构能够带来高内聚、低耦合的效果,但是有以下几个缺点:
- 三层架构仍然采用的是面向数据库的思维方式,尽管数据访问层屏蔽了数据库的实现细节,但是各层服务的实现仍然是站在数据库的角度进行的。
- 对于一些简单的、不包含业务逻辑的增、删、改、查类操作,仍然需要业务逻辑层进行转发,因此业务逻辑层中存在大量的对数据访问层的操作进行简单转换的代码。
- 各层之间都要进行DTO的转换,增加了不必要的工作量,并且降低了性能。
- 因为依赖关系是单向的,所以下一层中的代码不能使用上一层中的逻辑。
2012年,罗伯特·塞西尔·马丁(Robert Cecil Martin)提出了洋葱架构,如下图所示:

在洋葱架构中,不同的同心圆代表软件的不同部分,内层的部分比外层的部分更加抽象,也就是内层表达抽象,外层表达实现。
外层的代码只能调用内层的代码,内层的代码可以通过依赖注入的形式来间接调用外层的代码。
洋葱架构这样的同心圆结构非常类似洋葱,因此被称为”洋葱架构”。
洋葱架构拥有如下的优点:
- 洋葱架构的内层使用领域驱动的思想进行设计,因此架构的抽象程度更高。
- 外层的代码可以调用所有内层的代码,也就是可以跨层直接调用,不要所有的调用都逐层进行,因此代码更灵活、更简洁。
- 服务的接口定义在内层,服务的实现定义在外层,内层的代码可以通过依赖注入的服务接口的形式调用外层提供的服务,实现了抽象定义和具体实现的分离。
无论是传统的三层架构还是洋葱架构,它们要解决的问题和思路都是类似的,具体实现的代码结构也有很多相似的地方。
这就造成很多实践过三层架构的开发人员在应用洋葱架构的时候,容易出现仍然在应用三层架构的传统思想的问题。
因此,在应用洋葱架构的时候,一定要用DDD的思维方式去思考,把抽象级别高的问题放到高优先级考虑,不要过早考虑具体的实现细节。
需要注意的是,洋葱架构中的每一层并不一定直接对应代码中的一个项目,这些层之间是逻辑上的划分。
在实际技术实现的时候,可能会出现一层的代码位于多个项目中、一个项目中包含多层的代码这样的情况。
在进行项目开发的时候,我们除了要用到数据库之外,还会用到一些外部服务,比如我们要发送短信就要调用短信发送服务,要保存文件就要调用存储服务。
这些外部服务实现的变化会比较频繁,甚至我们会进行服务供应商的切换。
为了屏蔽这些服务实现的变化,我们把这些服务定义为接口,在内层代码中只定义和使用接口,在外层代码中定义接口的实现。
这样我们在需要修改服务的实现的时候,只要修改外层代码中接口的实现代码即可,内层的代码不需要改动。
这种对变化较大的外部服务进行屏蔽的层称为防腐层。