1 分层思想
计算机领域有一句话:计算机中任何问题都可通过增加一个虚拟层解决。这句体现了分层思想重要性,分层思想同样适用于Java工程架构。
分层优点是每层只专注本层工作,可以类比设计模式单一职责原则,或者经济学比较优势原理,每层只做本层最擅长的事情。
分层缺点是层之间通信时,需要通过适配器,翻译成本层或者下层可以理解的信息,通信成本有所增加。
我认为工程分层需要从五个维度思考:
(1) 单一
每层只处理一类事情,满足单一职责原则
(2) 降噪
信息在每一层进行传输,满足最小知识原则,只向下层传输必要信息
(3) 适配
每层都需要一个适配器,翻译信息为本层或者下层可以理解的信息
(4) 业务
业务对象可以整合业务逻辑,例如使用充血模型整合业务
(5) 数据
数据对象尽量纯净,尽量不要聚合业务
1.2 九层结构
综上所述SpringBoot工程可以分为九层:
- 工具层:util
- 整合层:integration
- 基础层:infrastructure
- 服务层:service
- 领域层:domain
- 门面层:facade
- 控制层:controller
- 客户端:client
- 启动层:boot
2 分层详解
创建测试项目user-demo-service:
user-demo-service
-user-demo-service-boot
-user-demo-service-client
-user-demo-service-controller
-user-demo-service-domain
-user-demo-service-facade
-user-demo-service-infrastructure
-user-demo-service-integration
-user-demo-service-service
-user-demo-service-util
复制代码
2.1 util
工具层承载工具代码
不依赖本项目其它模块
只依赖一些通用工具包
user-demo-service-util
-/src/main/java
-date
-DateUtil.java
-json
-JSONUtil.java
-validate
-BizValidator.java
复制代码
2.2 infrastructure
基础层核心是承载数据访问,entity实体对象承载在本层。
2.2.1 项目结构
代码层分为两个领域:
- player:运动员
- game:比赛
每个领域具有两个子包:
- entity
- mapper
user-demo-service-infrastructure
-/src/main/java
-player
-entity
-PlayerEntity.java
-mapper
-PlayerEntityMapper.java
-game
-entity
-GameEntity.java
-mapper
-GameEntityMapper.java
-/src/main/resources
-mybatis
-sqlmappers
-gameEntityMappler.xml
-playerEntityMapper.xml
复制代码
2.2.2 本项目间依赖关系
infrastructure只依赖工具模块
<dependency>
<groupId>com.test.javafront</groupId>
<artifactId>user-demo-service-util</artifactId>
</dependency>
复制代码
2.2.3 核心代码
创建运动员数据表:
CREATE TABLE `player` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`player_id` varchar(256) NOT NULL COMMENT '运动员编号',
`player_name` varchar(256) NOT NULL COMMENT '运动员名称',
`height` int(11) NOT NULL COMMENT '身高',
`weight` int(11) NOT NULL COMMENT '体重',
`game_performance` text COMMENT '最近一场比赛表现',
`creator` varchar(256) NOT NULL COMMENT '创建人',
`updator` varchar(256) NOT NULL COMMENT '修改人',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '修改时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
复制代码
运动员实体对象,gamePerformance字段作为string保存在数据库,这体现了数据层尽量纯净,不要整合过多业务,解析任务应该放在业务层:
public class PlayerEntity {
private Long id;
private String playerId;
private String playerName;
private Integer height;
private Integer weight;
private String creator;
private String updator;
private Date createTime;
private Date updateTime;
private String gamePerformance;
}
复制代码
运动员Mapper对象:
@Repository
public interface PlayerEntityMapper {
int insert(PlayerEntity record);
int updateById(PlayerEntity record);
PlayerEntity selectById(@Param("playerId") String playerId);
}
复制代码
2.3 domain
2.3.1 概念说明
领域层是DDD流行兴起之概念
可以通过三组对比理解领域层
- 领域对象 VS 数据对象
- 领域对象 VS 业务对象
- 领域层 VS 业务层
(1) 领域对象 VS 数据对象
数据对象字段尽量纯净,使用基本类型
public class PlayerEntity {
private Long id;
private String playerId;
private String playerName;
private Integer height;
private Integer weight;
private String creator;
private String updator;
private Date createTime;
private Date updateTime;
private String gamePerformance;
}
复制代码
以查询结果领域对象为例
领域对象需要体现业务含义
public class PlayerQueryResultDomain {
private String playerId;
private String playerName;
private Integer height;
private Integer weight;
private GamePerformanceVO gamePerformance;
}
public class GamePerformanceVO {
// 跑动距离
private Double runDistance;
// 传球成功率
private Double passSuccess;
// 进球数
private Integer score