一、架构的尽头是哲学
工作时间久了以后,发现对框架(Spring)的了解还停留在一个基本会使用的阶段,对它的一些设计演进并没有一个全面的认识,在笔者经历过的团队中其实还存在一大部分程序员对分层的思想还是不甚了解,更别谈对项目的架构设计分层设计理念了,其实一个架构师尤其是一个有理想有追求的架构师一定是追求其框架设计演进过程和思想,然后转变成自己的设计和架构功底,这才是我们真正能够借鉴内化的。所以说,学会项目的架构分层是一个区别一个程序员是否合格的分水岭,更是一个架构师必须要掌握的基本功。
那么,肯定有一些刚入门的程序员会说,项目中为什么要分层呢?分层有什么好处呢?那笔者明确告诉你,分层有很多的好处,也有一些坏处,计算机的世界没有绝对的正确与错误,对于架构来说这一点尤为重要。一个架构师,重要的责任是做出优秀且正确的方案。一个优秀的架构师,是懂得取舍的,所有的架构方案不是最优的,但是一定是在特殊的阶段对投入和收益做出的折衷最正确选择。架构如此,人生亦如此,20多岁的女孩风华正茂,20多岁的男孩穷困潦倒,但是不能放弃对梦想的追求,重要的是一定要遵循客观规律基础之上,尽自己最大的努力。最后你会发现,无论你多么成功,我们的人生何尝不是在取舍中渡过呢!所以说架构的尽头是哲学!
二、架构分层的好处
那咱们首先看一下项目分层的好处:
三、架构分层的缺点
到这,老多程序员说,你说的很有道理啊,觉得都是优点啊,哪有缺点?笔者在上面已经说过了,在计算机的世界里,引入一个新方案必然带来一个新问题,就像咱们初中物理学习的能量守恒定律那样,那笔者就系说一下引入分层带来的缺点:
那么,读者肯定会看出来。缺点绝对是大大少于优点的,所以说这个方案可行,那么咱们就需要分层,那么分层有哪几种呢?一般主要有2种分层架构思想,MVC分层设计和DDD领域建模分层设计。当然我也会介绍其他分层架构,但是没有MVC和DDD那么常用,我会一笔带过。MVC一般是3层,DDD一般是大四层设计,咱们不着急,一点点来,先说MVC的分层设计。
四、MVC架构分层
大家最熟悉的一定是MVC三层分层设计,因为目前绝大部门项目都是采用这种分层设计的,如果我们尝试把编程的复杂架构缩小到最容易理解的程度,那么MVC编程开发其实只做3件事:“定义属性、创建方法、调用展示”。但因为同类所需的内容较多,如一系列的属性,一堆的方法实现,一组的接口封装,那么就需要合理的把这些内容分配到不同的层次中去实现,因此有了分层架构的设计。
MVC架构模型适合提供HTTP服务的工程架构,适合简单的小场景开发使用。特点;轻便、简单、学习成本低。MVC 是一种非常常见且常用的分层架构,主要包括;M - mode 对象层,封装到 domain 里。V - view 展示层,但因为目前都是前后端分离的项目,几乎不会在后端项目里写 JSP 文件了。C - Controller 控制层,对外提供接口实现类。DAO 算是单独拿出来用户处理数据库操作的层。
(一)、MVC常见架构分层
MVC分层架构设计是将应用程序分为三个核心层次,即模型层、视图层和控制器层。这三个层次各自负责不同的功能,相互独立又相互联系。
模型层负责处理业务逻辑和数据持久化,视图层负责页面的布局和交互操作,控制器层负责业务逻辑和数据处理。这种分层架构设计可以实现代码的模块化、可维护性和可扩展性,提高开发效率和代码质量。
1.模型层的设计思路和实现方式
模型层是MVC分层架构设计中的核心层次之一,它负责处理业务逻辑和数据持久化。在模型层的设计中,我们需要关注以下几个方面:
2.视图层的设计思路和实现方式
视图层是MVC分层架构设计中的另一个核心层次,它负责页面的布局和交互操作。在视图层的设计中,我们需要关注以下几个方面:
3.控制器层的设计思路和实现方式
控制器层是MVC分层架构设计中的最后一个层次,它负责业务逻辑和数据处理。在控制器层的设计中,我们需要关注以下几个方面:
(二)、MVC常见架构调用流程
(三)、阿里巴巴架构分层规范
其实MVC是一种框架模式,而非设计模式,GOF把MVC看做是3中设计模式:《观察者模式》、《策略模式》,《组合模式》三者的合体。其核心是《观察者模式》。
笔者曾经见过很多种MVC的封层设计,总体来说大部分的设计都有一些问题,笔者结合多年的架构经验和设计经验,总结出来一套比较完整的设计方案,后来笔者在《阿里巴巴Java开发手册中》看到对分层的建议,有异曲同工之妙。
1.架构分层设计
对第三方平台封装的层,预处理返回结果及转化异常信息。
对Service层通用能力的下沉,如缓存方案/中间件通用处理。
与DAO层交互,对多个DAO的组合复用。
2.架构分层模型转换
以上的层级只是在原来三层架构的基础上进行了细分,而这些细分的层级仅仅是为了满足业务的需要。千万不要为了分层而分层。过多的层会增加系统的复杂度和开发难度。
那么分层有了,我们会引出另外一个问题,层和层之间的分层领域模型都有哪些呢,怎么做转换呢?
我们还是来看看《阿里开发手册》提供的分层领域模型规约参考:
在给出的参考中并没有对模型对象进行非常明确的划分,特别是对BO、AO、DTO的界限不是非常明确。这也是因为系统处理的业务不同、复杂度不同导致的。所以在设计系统分层和建模的时候,需要综合考虑实际应用场景。
可能有些小伙伴会觉得麻烦,为什么要弄出这么多O?转来转去的多累!笔者举一个例子,拿笔者负责的订单商城个人订单列表举例子,相信大家对该业务都不陌生,比如,笔者个人中心总共有6个订单,每个订单会显示商品名称、商品价格、商品数量、商品图片等信息。
对于显示层来说,这些信息相对于分层的设计理念,是可以封装成一个VO对象的,因为咱们得购物车页面只显示这些信息,为了更好更方便的显示这些信息,我们可以将所有的属性都设计成字符串类型。
public class OrderVO {
Long orderId;// 该订单的编号
String orderTime;// 下订单的时间
String orderStatus;// 订单状态
String goodsNum;// 商品数量
String goodsImage;// 商品图片
String totalMoney;// 总金额
String userName;// 用户姓名
List<ItemsVO> orderItems; // 订单商品明细集合
}
那么对于MVC来讲,进一层的业务层怎么设计呢,这一层他关心的逻辑是什么?肯定的是跟咱们刚才设计的VO关注点肯定是不一样,他更加多住的是内部的业务逻辑关系,和页面显示是没有关系,大家这一点一定要好好理解哈。
public class OrderDTO {
Long orderId;// 该订单的编号
Integer orderTime;// 下订单的时间
EnumOrderStatus orderStatus;// 订单状态
Integer goodsNum;// 商品数量
String goodsImage;// 商品图片
BigDecimal totalMoney;// 总金额
UserDTO userInfo;//用户信息
List<ItemsDTO> orderItems;// 订单商品明细集合
}
可以看到,下单日期使用的Integer类型,金额使用BigDecimal,订单状态使用枚举值表示,用户名称变成了用户信息对象,明细集合中的商品也变成了DTO类型的对象。
在业务逻辑层面,更多的是关注由多种信息组合而成的关系。因为它在系统中起到信息传递的作用,所以它携带的信息也是最多的。
好,那我们再来看看数据持久层。
上面也提到了,数据持久层与数据库是一一对应的关系,而上一层的订单信息其实可以拆解为多个持久层对象,其中包含:订单持久层对象(OrderDO),商铺持久层对象(ShopDO),用户持久层对象(UserDO)还有一堆的商品持久层对象(ProductDO)。
所以分层/拆分的本质还是简化我们思考问题的方式,各层只关注自己感兴趣的内容。
可这样的拆分确实增加了许多工作量,不同模型之间转来转去的确实头疼。
当然,Java丰富的生态给我们提供了很多转换的工具,在这里笔者就不一一细说了,大家感兴趣可以学习一下。
(四)、你用 MVC 写代码,遇到过最大的问题是什么?
简单、容易、好理解,是 MVC 架构的特点,但也正因为简单的分层逻辑,在适配较复杂的场景并且需要长周期的维护时,代码的迭代成本就会越来越高。
1.代码角度
2.项目管理角度
五、DDD架构分层
从最早接触 DDD 架构,到后来用 DDD 架构不断的承接项目开发,一次次在项目开发中的经验积累。对 DDD 有了不少的理解。DDD 是一种思想,落地的形态和结构会有不同的方式,甚至在编码上也会有风格的差异。但终期目标就一个:“提供代码的可维护性,降低迭代开发成本”。也是康威定律所述:”任何组织在设计一套系统时,所交付的设计方案在结构上都与该组织的沟通结构保持一致。“
但 DDD 与 MVC 相比的概率较多,贸然用理论驱动代码开发,会让整个工程变得非常混乱,甚至可能虽然是用的 DDD 但最后写出来了一片四不像的 MVC 代码。
如果你接触过较大型且已经长期维护项目的 MVC 架构,你就会发现这里的 DAO、PO、VO 对象,在 Service 层相互调用。那么长期开发后,就导致了各个 PO 里的属性字段数量都被撑的特别大。这样的开发方式,将”状态”、“行为“分离到不同的对象中,代码的意图渐渐模糊,膨胀、臃肿和不稳定的架构,让迭代成本增加。
而 DDD 架构首先以解决此类问题为主,将各个属于自己领域范围内的行为和逻辑封装到自己的领域包下处理。这也是 DDD 架构设计的精髓之一。它希望在分治层面合理切割问题空间为更小规模的若干子问题,而问题越小就容易被理解和处理,做到高内聚低耦合。这也是康威定律所提到的,解决复杂场景的设计主要分为:分治、抽象和知识。
(一)、DDD的专业名词
DDD的包含其中的一些基本概念,在此处大家先有个印象,先介绍几个主要的概念,后面会有专门的章节去介绍这些专业术语。
(二)、DDD的架构分层
└── src
├── Core
│ └── Domain
│ └── Entities
│ └── ValueObjects
│ └── Aggregates
│ └── DomainEvents
│ └── Repositories
│ └── Services
├── Application
│ └── Services
│ └── DTOs
└── Infrastructure
└── Persistence
└── EntityConfigs
└── Migrations
└── Seeders
└── Repositories
└── Factories
(三)、DDD的优点
DDD(Domain-Driven Design,领域驱动设计)是一种软件开发方法论,旨在帮助开发者创建清晰和一致的领域模型。它提供了一种设计软件的方法,旨在解决复杂性问题,提高系统的可维护性和可理解性。
(四)、DDD的问题
六、其他分层架构思想
(一)两层架构
1.出现时间:1960+,从个人电脑兴起时。
2.定义:一个服务器、多客户端。
3.架构图:
4.分层简介:
5.适用场景:
(二)三层架构/多层架构
1.出现时间:1990+:
2.定义:
3.架构图
4.适用场景
(三). MVC架构
1.出现时间:1980+
2.定义:
3.架构图:
4.适用场景:
C#: ASP.NET
Python: Django
JS:Angular.js
PHP: …
(四)CQRS命令查询分离架构
1.出现时间:1985+
2.定义:
(1).展现接口层
(2).Cmd/Query处理总线
(3).应用层
用例,业务场景。
完成事务型操作。
(4).领域层
(5).持久层
3.架构图:
4.适用场景:
(五)、六边形/端口-适配器架构
(1).业务领域层
领域模型包含了所有的应用逻辑与规则。
领域层中不会直接引用技术实现,例如 HTTP 上下文或数据库调用,这样就能够确保在技术方面的改动不会影响到领域层面。
(2).端口层
负责接收与用例相关的所有请求,这些请求负责在领域层中协调工作;
端口层在端口内部作为领域层的边界,在端口外部则扮演了外部实体的角色;
(3).适配器层
这一层的技术实现负责以某种格式接收输入、及产生输出;
在适配器层不存在领域逻辑,它的唯一职责就是在外部世界与领域层之间进行技术性的转换;
两种类型的适配器:
3.架构图
4.适用场景:
(六)、洋葱架构
(1).业务实体(Entities)
(2). 用例(Use Case)
一个用例规则有多个不同的业务规则组成;依赖业务实体;
(3). 接口适配器层
包含了网关(Gateways)、控制器(Controllers)与展示器(Presenters),它们皆属于适配器(Adapter),用于打通应用业务逻辑与外层的框架和驱动器,实现逻辑的适配以访问外部资源;
(4). web接口、资源层
包括页面、接口;
三方接口、数据库等;
3.架构设计图
4.适用场景:
六、本节总结
一个项目的架构一定是不断演进优化出来的,而不是在设计之初就一步到位的。这样作为一个架构师,才能够感受到业务需求对于项目多样性和复杂性的深刻理解,这样成长出来的架构师才是优秀的,这样的做出来的项目才有感情。就像我们的人生,没有人一开始就成熟,而是在不断地经历和学习中不断成长的,就像我们成长路上会经历很多的故事。
或许街头的红灯,见过最多的匆忙。或许医院的走廊,见过最多的悲伤。或许酒店的床,听过最多的谎话。或许佛前的香,听过最多的欲望。或许城市的霓虹,见过最多的迷茫。或许路边摊的酒杯,听过最多的梦想。或许伪装的坚强,最怕突然的关心。或许听歌的人,最怕唱的是自己。或许凌晨的耳朵,最怕家中的电话。或许清晨的眼睛,最怕见不到梦中的她。或许结痂的伤口,最怕再一次结痂。或许放下的记忆,最怕再一次放下。
我想,这就是被大家称作为成长的故事吧!人生如此,架构亦如此!欢迎大家和我一块走进DDD的世界,取其精华,去其糟粕!让DDD为我们的复杂业务项目插上梦想的翅膀!