在“巨型单体”与“微服务”之间,寻找最佳平衡点
最近做项目遇到了一些问题,于是手搓了一个框架。
在当今的 Java 开发领域,我们似乎总是面临两个极端的选择:要么是维护成本极高、牵一发而动全身的传统“巨型单体”;要么是虽然灵活但运维极其复杂、调用链路漫长的“微服务架构”。
有没有一种架构,既能保持单体应用的开发便捷性,又能拥有微服务的模块化边界?
今天我们要介绍的开源项目 LiHe ,就是一个基于 Spring Boot 3 + Spring Modulith + React 18 的全栈开发框架。它采用“模块化单体(Modular Monolith)”的设计理念,试图为现代企业级开发提供一个“刚刚好”的解决方案。
项目地址
https://gitee.com/blackhold/LiHe
🏛️ 架构哲学:回归理性的“模块化单体”
LiHe 的核心理念在于“平衡”。它试图解决传统单体“代码耦合严重”的痛点,同时避免微服务“运维复杂”的深坑。
什么是 Spring Modulith?
LiHe 引入了 Spring 生态中备受瞩目的 Spring Modulith 项目。这不仅仅是一个依赖库,更是一种架构约束。
- 逻辑分离,物理统一 :在 LiHe 中,用户模块、部门模块、系统监控模块在代码结构上是严格隔离的。Spring Modulith 会在编译期和测试期检查模块间的依赖关系,禁止循环依赖和非法调用。
- 事件驱动架构 :模块之间通过 Spring Event 进行异步交互。例如,当用户创建一个新账号(User 模块),系统需要记录日志(Log 模块)。在 LiHe 中,这不是直接的方法调用,而是抛出一个事件。这种设计使得各个模块高度解耦,未来如果业务量级突破单机瓶颈,你可以轻松地将某个模块剥离出来独立部署。
🎨 前端架构:React 18 与 Arco Design 的美学
前端部分,LiHe 同样拒绝平庸。
1. 字节跳动 Arco Design
LiHe 选用了字节跳动开源的 Arco Design 作为 UI 组件库。相比于大家看腻了的 Ant Design,Arco 的设计语言更加年轻、扁平,配色更加清爽。它提供了极其丰富的组件和开箱即用的“工作台”模板,让中后台系统也能拥有 C 端产品的精致感。


2. React 18 的并发能力
基于 React 18 构建,利用其并发渲染特性(Concurrent Features),在处理大量数据表格渲染或复杂交互时,界面响应更加流畅,极大地提升了用户体验。
📦 开箱即用的企业级功能
LiHe 不是一个空的脚手架,它内置了企业级开发所需的“基础设施”,让你能专注于业务逻辑:
- 组织架构管理 :精细到部门、岗位、用户的树形管理。
- 动态权限控制 :支持菜单级、按钮级的权限控制,甚至支持数据权限(例如:只能看本部门的数据)。
- 全方位监控 :
- 服务监控 :实时查看 CPU、内存、磁盘使用率。
- 缓存监控 :Redis 的命中率、内存占用一目了然。
- 在线用户 :谁在线,谁在操作,尽在掌握。
- 异步日志系统 :基于事件驱动的操作日志和登录日志,记录详尽且不阻塞主线程。
🛠️ 后端技术栈:硬核与前沿的碰撞
LiHe 的后端选型非常激进且富有远见,它直接跳过了过渡版本,拥抱了 Java 生态的最新标准。
1. JDK 21+:不仅仅是语法糖
LiHe 强制要求 JDK 21。这意味着你不仅能使用 var 、 record 等语法糖来精简代码,更能享受到 虚拟线程 (Virtual Threads) 带来的性能红利。在高并发 I/O 密集型场景下(如大量数据库查询、Web 请求),虚拟线程能以极低的资源消耗实现惊人的吞吐量,这让 LiHe 在单体模式下也能拥有强悍的性能表现。
2. Spring Data JPA + QueryDSL:类型安全的优雅
在国内 MyBatis 统治的背景下,LiHe 选择了 JPA + QueryDSL 的组合。
- JPA :负责基础的 CRUD,开发效率极高,一行 SQL 都不用写。
- QueryDSL :解决了 JPA 处理复杂动态查询的痛点。它让你用 Java 代码来构建 SQL,最大的好处是 类型安全 。字段名写错了?编译器直接报错,而不是等到运行时抛出 SQL 异常。这对于长期维护和重构来说,价值千金。
3. Spring Security + JWT:无状态的护城河
采用了标准的 RBAC(基于角色的访问控制)模型,配合 JWT 实现无状态认证。支持多终端认证,无论是 Web 端、小程序还是移动端,都能一套鉴权逻辑搞定。
/**
* 分页查询 (QueryDSL 版)
*/
public Page<SysOperLogVo> pageSysOperLogByParam(SysOperLogDto queryDto, Pageable pageable) {
// 1. 获取 Q 类单例 (类似 SQL 中的表别名)
QSysOperLogEntity q = QSysOperLogEntity.sysOperLogEntity;
// 2. 构建查询条件
BooleanBuilder builder = new BooleanBuilder();
// 自动生成 module 查询条件
if (queryDto.getModule() != null) {
if (StringUtils.hasText(queryDto.getModule())) {
builder.and(q.module.containsIgnoreCase(queryDto.getModule()));
}
}
// 自动生成 businessType 查询条件
if (queryDto.getBusinessType() != null) {
if (StringUtils.hasText(queryDto.getBusinessType())) {
builder.and(q.businessType.containsIgnoreCase(queryDto.getBusinessType()));
}
}
// 自动生成 operator 查询条件
if (queryDto.getOperator() != null) {
if (StringUtils.hasText(queryDto.getOperator())) {
builder.and(q.operator.containsIgnoreCase(queryDto.getOperator()));
}
}
// 自动生成 operatorId 查询条件
if (queryDto.getOperatorId() != null) {
if (StringUtils.hasText(queryDto.getOperatorId())) {
builder.and(q.operatorId.containsIgnoreCase(queryDto.getOperatorId()));
}
}
// 自动生成 operIp 查询条件
if (queryDto.getOperIp() != null) {
if (StringUtils.hasText(queryDto.getOperIp())) {
builder.and(q.operIp.containsIgnoreCase(queryDto.getOperIp()));
}
}
// 自动生成 costTime 查询条件
if (queryDto.getCostTime() != null) {
builder.and(q.costTime.eq(queryDto.getCostTime()));
}
// 自动生成 operTime 查询条件
if (queryDto.getOperTime() != null) {
builder.and(q.operTime.eq(queryDto.getOperTime()));
}
// 自动生成 operParam 查询条件
if (queryDto.getOperParam() != null) {
if (StringUtils.hasText(queryDto.getOperParam())) {
builder.and(q.operParam.containsIgnoreCase(queryDto.getOperParam()));
}
}
// 自动生成 jsonResult 查询条件
if (queryDto.getJsonResult() != null) {
if (StringUtils.hasText(queryDto.getJsonResult())) {
builder.and(q.jsonResult.containsIgnoreCase(queryDto.getJsonResult()));
}
}
// 自动生成 status 查询条件
if (queryDto.getStatus() != null) {
builder.and(q.status.eq(queryDto.getStatus()));
}
// 自动生成 errorMsg 查询条件
if (queryDto.getErrorMsg() != null) {
if (StringUtils.hasText(queryDto.getErrorMsg())) {
builder.and(q.errorMsg.containsIgnoreCase(queryDto.getErrorMsg()));
}
}
// 3. 执行查询 (Repository 原生支持 QueryDSL)
return sysOperLogRepository.findAll(builder, pageable).map(this::toSysOperLogVo);
}
Entity 简单配置
@Data
@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = "sys_menu")
public class SysMenuEntity extends BaseEntity {
@Comment("菜单名称")
private String menuName;
@Comment("父菜单ID")
private String parentId; // 根节点通常为 "0"
@Comment("显示顺序")
private Integer orderNum;
@Comment("路由地址")
private String path;
@Comment("组件路径 (vue组件路径)")
private String component;
@Comment("是否为外链 (1是 0否)")
private Integer isFrame;
@Comment("菜单类型 (M目录 C菜单 F按钮)")
private String menuType;
@Comment("菜单状态 (1显示 0隐藏)")
private String visible;
@Comment("权限标识 (e.g. system:user:list)")
private String perms;
@Comment("菜单图标")
private String icon;
@ToString.Exclude
@EqualsAndHashCode.Exclude
@ManyToMany(mappedBy = "menus")
private Set<SysRoleEntity> roles = new HashSet<>();
}
📦数据库
数据库使用的是 PostgreSQL 作为一款强大的开源关系型数据库,具有以下显著优点:
开源免费 - BSD协议,无商业限制
功能全面
• 最标准SQL支持
• JSON文档存储 + 关系型双模式
• 高级查询(CTE、窗口函数等)
性能强大
• MVCC高并发
• 多种索引优化
• 并行查询
可靠稳定
• 完整ACID事务
• 数据完整性保障
• 流复制高可用
高度可扩展
• 自定义函数/类型
• 丰富扩展生态(如PostGIS)
• 多语言支持
适用: 需要高可靠性、复杂查询、混合数据模型的企业应用
📦 开箱即用的企业级功能
LiHe 不是一个空的脚手架,它内置了企业级开发所需的“基础设施”,让你能专注于业务逻辑:
- 组织架构管理 :精细到部门、岗位、用户的树形管理。
- 动态权限控制 :支持菜单级、按钮级的权限控制,甚至支持数据权限(例如:只能看本部门的数据)。
- 全方位监控 :
- 服务监控 :实时查看 CPU、内存、磁盘使用率。
- 缓存监控 :Redis 的命中率、内存占用一目了然。
- 在线用户 :谁在线,谁在操作,尽在掌握。
- 异步日志系统 :基于事件驱动的操作日志和登录日志,记录详尽且不阻塞主线程。

⚙️ 配置与启动:极简主义
LiHe 的落地门槛极低,只需几步即可从零启动。
server:
port: 8080
spring:
application:
name: lihe-gateway
config:
import: "optional:nacos:lihe-gateway.yml"
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
gateway:
discovery:
locator:
enabled: true # 使用配置来选择是否开启微服务
lower-case-service-id: true
routes:
- id: lihe-server
uri: lb://lihe-server
predicates:
- Path=/api/**
filters:
- StripPrefix=1 # 去掉 /api 前缀,转发给后端
Redis和Kafka 随意切换 还加入了 本地文件上传与Minio 的配置切换
lihe:
microservice-enabled: false # 默认为单体模式
service-name: lihe-server
captcha:
enabled: true # 验证码开关
type: math # math(算术) 或 line(线段干扰)
expiration: 120 # 过期时间(秒)
mq:
type: redis # 可选值: redis, kafka
file:
type: local # 默认本地存储
local:
# 默认存储在用户主目录下的 lihe/upload
# path: ${user.home}/lihe/upload
# 访问域名前缀
domain: http://localhost:8099/api/profile
# MinIO 配置示例 (需要时取消注释并修改 type: minio)
# type: minio
# minio:
# endpoint: http://localhost:9000
# access-key: minioadmin
# secret-key: minioadmin
# bucket-name: lihe
📝 总结与选型建议
LiHe 是一个非常务实的框架。它没有过度设计的复杂性,却拥有应对未来的扩展性。
它适合谁?
- 初创团队/中小企业 :业务处于快速验证期,需要极高的开发效率,且运维资源有限。
- 外包/私活开发者 :需要一个功能完备、界面美观的底座,能快速交付高质量的交付物。
- 技术探索者 :想学习 Spring Boot 3、JDK 21 新特性以及 React 18 最佳实践的开发者。
它不适合谁?
- 超大规模互联网应用 :如果你的系统日活千万,且团队有几百人维护,那么微服务架构可能更适合你。
- 极度依赖 MyBatis 生态 :如果你对 JPA 有强烈的抵触情绪,或者历史遗留了大量复杂的 MyBatis XML,迁移成本可能较高。
总的来说,LiHe 在“巨型单体”和“微服务”之间找到了一块 黄金平衡点 。如果你正在寻找一个既能快速起步,又不会在未来成为技术负债的框架,LiHe 值得你投入时间去尝试。
更多推荐
所有评论(0)