CHARLIE SAYS

查理如是说
DATE 2026-08-24
THEME
SERIES / JHIPSTER / P-220 · JHipster 项目开发

JHipster 开发 05:微服务 vs 单体——怎么选

“我们要不要上微服务?“几乎每个 JHipster 项目启动时都会被问一遍。我们的答案五年前是单体,现在还是单体——但这个问题我们认真评估过三次,包括真刀真枪拆出过一组微服务。这篇把两种拓扑在 JHipster 里的形态、成本与选择逻辑讲透。

JHipster 的两种应用类型

生成时第一个问题就是 applicationType,JHipster 提供两条完整路径:

  • Monolith(单体):一个应用包含全部——REST API、前端、安全、缓存、管理界面。单进程部署,是绝大多数项目的正确起点。
  • 微服务族:拆成三种角色协作——Gateway(Spring Cloud Gateway,路由、鉴权入口,可带前端)、Microservice(纯后端服务)、JHipster Registry(注册中心,内置 Eureka 或 Consul,另带 Spring Cloud Config 配置下发与管理界面)。

微服务模式下的拓扑长这样:

flowchart TB
  B["浏览器 / 客户端"] --> GW["Gateway<br/>:8080<br/>路由 / 鉴权 / 限流"]
  subgraph registry["JHipster Registry"]
    EU["Eureka / Consul<br/>服务注册发现"]
    CFG["Spring Cloud Config<br/>统一配置"]
  end
  GW -->|"查路由"| EU
  subgraph services["业务微服务"]
    S1["order-service<br/>:8081"]
    S2["billing-service<br/>:8082"]
    S3["inventory-service<br/>:8083"]
  end
  GW --> S1
  GW --> S2
  GW --> S3
  S1 & S2 & S3 -->|"注册/心跳"| EU
  S1 & S2 & S3 -->|"拉配置"| CFG
  S1 --> DB1[("order DB")]
  S2 --> DB2[("billing DB")]
  S3 --> DB3[("inventory DB")]

关键事实:微服务不是”多个单体”。Gateway 的路由规则、Registry 的服务发现、跨服务的认证传递(JWT 在网关校验后透传,OAuth2 则各服务自行与 IdP 交互)、每个服务独立数据库——这些都要作为体系来运维。

两种拓扑正面对比

维度Monolith微服务(Gateway + Registry + N 服务)
启动复杂度./mvnw 一条命令Registry → 各服务 → Gateway 依次起(docker-compose 可一键)
本地开发内存一个 JVM + 一个前端轻松 4-6 个 JVM 起步
部署产物一个 jar(前端已打入)N+2 个镜像,需编排
事务一致性本地事务跨服务只能 Saga/最终一致(第 12 篇 Kafka 是主要工具)
数据查询JOIN 随便写各库独立,跨服务聚合靠 API 组合或 CQRS
认证一次配置JWT 网关统一校验尚简单,OAuth2 每服务对接
团队协作共享一个代码库按服务划分所有权,需 API 契约纪律
故障排查一个进程一份日志全链路 tracing 基本成为必需(第 20 篇)
升级一个应用每个服务单独排期,版本可以不同但代价是矩阵复杂

一句话概括:微服务买的是组织扩展性(多团队并行、独立部署)与故障隔离,付的是分布式系统的全部税

怎么选:决策表

我们的实操判断标准:

问题若”否”若”是”
团队会超过 3-4 个独立特性小队(约 30+ 人)吗?单体倾向微服务
存在明确异质需求(某模块要独立扩缩容/独立技术栈/独立发布节奏)吗?单体倾向拆出该模块
有专职运维/平台能力(K8s、监控、链路追踪)吗?单体可评估微服务
业务能接受最终一致性吗?单体可评估微服务
系统边界与限界上下文画得清楚吗?单体(先画清楚再说)可评估微服务
老板/客户只是觉得”微服务先进”吗?单体依然是单体

经验值:80% 的企业内部系统用 JHipster 单体 + 良好的模块化就足够。我们没有见过因为没上微服务而死的项目,但见过多个因为过早上微服务而被运维复杂度拖死的。

演进路线:单体起步,模块化,再拆分

“先单体后微服务”不是安慰剂,JHipster 对这条路有实际支持,关键是从第一天就为拆分留好缝

flowchart LR
  A["阶段一:单体<br/>JDL 按限界上下文分包<br/>domain/order、domain/billing"] --> B["阶段二:模块化单体<br/>包间只走 Service 接口<br/>禁止跨包 JOIN 与表外键滥用"]
  B --> C["阶段三:拆分<br/>JHipster 微服务重新生成<br/>按包迁业务代码"]
  1. 单体阶段:JDL 建模时让实体归属清晰(package 选项可指定子包),Liquibase changelog 也按业务域分文件。代码Review盯住一条纪律:跨域调用只允许 Service 接口,不许 Repository 直连别人的表
  2. 模块化阶段:当单体变大,先用模块边界约束依赖(JHipster 支持 microfrontends 选项为前端做类似拆分)。这一步不需要任何新基础设施,纯纪律成本。
  3. 拆分阶段:真到了要拆的那天,用 JHipster 生成 Gateway + 目标微服务(老 Registry 配置可以直接复用),把对应包的业务代码、JDL 实体、changelog 迁过去。因为边界早就干净,拆分是体力活而不是手术。

我们拆 billing 出去那次,因为单体期纪律守得好,实际迁移两周完成;同期另一组直接在”面条单体”上拆分,三个月还没理清数据归属。差别不在工具,在边界。

用 JHipster 生成微服务:实际操作

真要上微服务,JHipster 的生成方式不是”一个大应用”,而是每个角色独立建模。JDL 里写多个 application 块(或分目录分别生成):

application {
  config {
    applicationType gateway
    baseName shopGateway
    serverPort 8080
    authenticationType jwt
    registry jhipster-registry
  }
}

application {
  config {
    applicationType microservice
    baseName orderService
    serverPort 8081
    authenticationType jwt
    registry jhipster-registry
  }
}

application {
  config {
    applicationType microservice
    baseName billingService
    serverPort 8082
    authenticationType jwt
    registry jhipster-registry
  }
}

entity Order { orderNo String required unique }
entity Invoice { invoiceNo String required unique }

paginate * with pagination
dto * with mapstruct
service * with serviceClass

实体用 package 归属到服务:package com.mycompany.shop.orderspackage com.mycompany.shop.billing(或在分目录方案里各自维护 JDL),生成器据此把实体路由到对应微服务项目。本地跑起来的顺序:

# 1. 注册中心(生成在任一服务的 src/main/docker 下)
docker compose -f docker/jhipster-registry.yml up -d

# 2. 各服务(每个目录一个终端)
cd order-service && ./mvnw
cd billing-service && ./mvnw

# 3. 网关最后起,浏览器仍只访问 8080
cd gateway && ./mvnw && npm start

网关按 Eureka 注册表把 /services/orders/api/** 路由到 order-service——对前端而言多服务是透明的。团队协作上,每个服务独立 git 仓库或 monorepo 子目录,各走各的发布节奏,这才是微服务的组织收益。

微服务路径的三个实用提醒

如果评估后确实要上微服务:

  1. Registry 用 docker-compose 先跑起来docker compose -f src/main/docker/jhipster-registry.yml up,本地开发体验立刻顺畅。
  2. JWT 优先于 OAuth2 起步:网关统一签发/校验,服务无状态透传,调试成本低得多;接企业 IdP 是后面的事。
  3. 异步解耦用 Kafka:JHipster 生成 Spring Cloud Stream 集成,跨服务事件流比 REST 链式调用健康(第 12 篇)。

小结

  • JHipster 两种拓扑:单体一条命令起步;微服务是 Gateway + Registry(Eureka/Consul + Config)+ N 个服务的体系化工程。
  • 微服务买组织扩展性与故障隔离,付部署、事务、排查的全套分布式税;80% 的企业系统单体足够。
  • 决策看团队规模、异质扩缩容需求、平台能力、一致性容忍与边界清晰度,而不是流行度。
  • 正确路径是”单体 → 模块化(纪律)→ 拆分(体力活)“,JDL 分包与 Liquibase 分文件从第一天留缝。

系列导航

← 集合源码 005:PriorityQueue源码解析 目录 网络协议 005:TCP 协议详解 →
← 返回文章列表