一、正本清源:微服务的本质与架构演进的底层逻辑
很多团队对微服务的认知陷入了根本性误区:把微服务当成了架构升级的银弹,盲目拆分后不仅没解决单体的痛点,反而造出了更难维护的「分布式单体」——服务间强耦合、共享数据库、部署必须全量上线、故障级联传播,研发和运维成本指数级上升。
微服务的核心定义,源自Martin Fowler与James Lewis的行业标准论述:微服务是一种架构模式,它将应用拆分为一组小型、自治的服务,每个服务围绕特定业务能力构建,可独立部署、独立迭代,服务间通过轻量化的HTTP/GRPC协议通信,采用去中心化的数据治理与技术选型。
我们必须先明确一个核心前提:单体架构并非原罪,微服务也不是终点,架构演进的唯一驱动力是业务发展。
- 初创期/业务规模小时,单体架构是最优解:开发成本低、部署简单、运维成本几乎为零,无需分布式架构的复杂度 overhead;
- 当业务进入高速增长期,团队规模扩大、迭代频率提升、并发量上涨,单体架构的瓶颈会集中爆发:
- 研发效率瓶颈:多人协作代码冲突频发,一个小功能修改需要全量回归,迭代周期从天级拉长到周级;
- 部署效率瓶颈:哪怕修改一行代码,都需要全量打包、重启整个应用,发布风险高、回滚成本大;
- 扩展性瓶颈:无法针对高并发模块做单独扩容,只能整个应用横向扩容,资源浪费严重;
- 容错性瓶颈:一个非核心模块的OOM、死循环,会拖垮整个应用,故障隔离能力为零;
- 技术债务瓶颈:老旧代码与新业务耦合,技术栈无法升级,重构成本极高。
架构演进的核心目标,从来不是「用微服务」,而是解决业务发展过程中遇到的实际痛点,在研发效率、资源成本、系统稳定性之间找到最优平衡。
二、单体到微服务的渐进式演进路径
微服务改造绝对不能一步到位、全量拆分,行业90%以上的微服务改造翻车事故,都源于「休克式疗法」。正确的演进路径是渐进式、低风险、可回滚的,每一步都有明确的验证标准,完整流程如下:
2.1 第一步:单体内部模块化先行
微服务拆分的前置条件,是单体内部已经有清晰的模块边界。如果单体代码是面条式耦合,直接拆分只会把耦合从单体内部转移到服务间,造出分布式垃圾。
这一步的核心动作:
- 按照业务域对单体代码进行分层分模块,比如电商场景拆分为用户模块、商品模块、订单模块、支付模块、通知模块等;
- 模块间只能通过接口调用,禁止直接依赖内部实现类,禁止跨模块直接操作数据库表;
- 抽离公共组件,比如工具类、通用配置、统一异常处理等,形成独立的二方包,避免后续拆分后代码重复。
验证标准:修改某一个业务模块的代码,不会影响其他模块;模块间的依赖是单向的,无循环依赖。
2.2 第二步:数据库垂直拆分
微服务的核心是数据自治,共享数据库的微服务,本质上还是单体。很多团队先拆代码、后拆库,最终导致服务间通过数据库强耦合,后续再拆库的成本会指数级上升。
数据库垂直拆分的核心原则:按照业务域拆分数据库,一个业务域对应一个独立的数据库,禁止跨库连表查询。比如电商场景,拆分出用户库、商品库、订单库、支付库,每个库只允许对应的业务模块操作。
这一步的核心动作:
- 梳理表与业务域的归属关系,拆分出独立的库表,禁止一张表被多个业务模块操作;
- 数据同步:通过双写+数据校验的方式,完成存量数据迁移,确保数据一致性;
- 代码改造:单体内部模块只能操作自己归属的数据库,跨模块的数据获取,必须通过模块接口调用,禁止跨库查询。
验证标准:单体应用内无跨库SQL;修改某一个库的表结构,不会影响其他业务模块。
2.3 第三步:非核心模块优先拆分
先拆非核心、低风险的模块,比如通知服务、日志服务、统计服务、文件服务等,这些模块不影响核心交易链路,哪怕出问题也不会导致业务停摆,能帮助团队快速积累微服务落地的经验,完善基础设施。
这一步的核心动作:
- 将非核心模块从单体中独立出来,形成独立的微服务,提供标准化的HTTP/GRPC接口;
- 单体应用通过接口调用新的微服务,替换原有的内部模块实现;
- 灰度验证:通过流量切分,逐步将请求切换到新服务,验证无误后下线单体内部的对应代码。
验证标准:新服务可独立部署、独立扩容;服务下线不会影响核心业务链路;单体应用可正常回滚到原有实现。
2.4 第四步:核心模块渐进式拆分
核心模块(比如订单、交易、支付)的拆分,是微服务改造的重中之重,必须遵循「小步快跑、灰度验证、随时可回滚」的原则,绝对不能一次性全量拆分。
以电商核心链路为例,拆分顺序建议:用户服务 → 商品服务 → 订单服务 → 支付服务,每拆分完一个服务,都要经过充分的验证,再进行下一个服务的拆分。
这一步的核心动作:
- 针对核心模块,先梳理清楚上下游依赖、接口定义、数据模型,形成完整的拆分方案;
- 独立开发对应的微服务,实现完整的业务逻辑,与单体应用并行迭代;
- 灰度发布:先切1%的流量到新服务,对比单体与新服务的执行结果,验证数据一致性、接口性能、业务正确性,逐步提升流量占比,直到100%切流完成;
- 下线单体内部对应的模块代码,完成该服务的拆分。
2.5 第五步:服务治理体系完善与单体下线
当所有核心模块都完成拆分后,单体应用就变成了一个「空壳」,此时需要完善微服务的治理体系,确保整个架构的可控性,最终完成单体的下线。
服务治理体系的核心组件,必须与拆分过程同步建设,否则拆出来的服务就是一盘散沙:
- 注册中心:实现服务的注册与发现,解决服务地址动态管理的问题;
- API网关:统一入口,实现路由转发、鉴权、限流、熔断、日志监控等能力;
- 配置中心:统一管理服务配置,实现配置的动态刷新、环境隔离;
- 链路追踪:实现分布式链路的全链路监控,快速定位问题;
- 监控告警:覆盖服务的CPU、内存、接口响应时间、错误率、业务指标等全维度监控;
- 限流熔断降级:实现故障隔离,避免服务级联崩溃。
完整的微服务架构如下:
三、微服务拆分的7大黄金原则(可落地、可验证)
拆分原则是微服务落地的核心,所有的拆分动作都必须围绕这些原则展开,否则就会陷入盲目拆分的误区。
3.1 高内聚低耦合原则(根本原则)
这是软件设计的根本原则,也是微服务拆分的第一准则。
- 高内聚:一个微服务内的所有代码、数据、业务逻辑,都必须围绕同一个业务域,只负责该业务域内的相关能力,把相关的行为和数据聚合在一起,不做与该业务域无关的事情。比如用户服务,只负责用户的注册、登录、信息管理、权限校验,不负责商品的库存管理、订单的创建;
- 低耦合:服务之间的依赖要最小化,只通过标准化的接口通信,不依赖对方的内部实现,不共享数据库,不直接操作对方的内存数据。服务间的依赖必须是单向的,禁止循环依赖。
反例:按技术层拆分服务(把Controller、Service、DAO分别拆成独立的服务),这种拆分方式完全违背了高内聚原则,一个业务逻辑的修改需要同时改动三个服务,耦合度极高,完全失去了微服务的意义。
3.2 数据自治原则(核心红线)
这是微服务与分布式单体的核心区别,绝对不能突破的红线。数据自治的核心定义:每个微服务独享自己的数据库,只有该服务可以对对应的数据库进行读写操作,其他服务想要获取该服务的数据,只能通过该服务提供的接口调用,绝对禁止跨服务直接访问数据库、禁止跨库连表查询、禁止多个服务共享同一个数据库。
数据自治的底层逻辑:数据是业务的核心,数据库的耦合是最强的耦合,一旦共享数据库,服务的独立迭代、独立部署、故障隔离都会变成空谈——修改表结构需要所有相关服务同步修改,一个服务的慢SQL会拖垮整个数据库,完全失去了微服务的优势。
特殊场景处理:如果需要跨服务的数据分析,只能通过数据同步的方式,将业务库的数据同步到数仓,在数仓中进行统计分析,绝对不能直接操作业务库。
3.3 业务域对齐原则(拆分边界标准)
微服务的拆分边界,必须与业务域的边界对齐,也就是DDD(领域驱动设计)中的「限界上下文」。 限界上下文,通俗来讲就是「一个业务领域的边界,在这个边界内,所有的业务术语、业务规则、数据模型都有统一的定义,不会产生歧义」。比如电商场景中,「商品」这个术语,在商品域里的定义是完整的商品信息、库存、规格、价格,而在订单域里,只需要商品ID、商品名称、单价、快照信息,两个域里的「商品」是完全不同的模型,必须拆分到不同的限界上下文中。
拆分的标准:一个限界上下文,对应一个或多个微服务,但一个微服务绝对不能跨越多个限界上下文。
很多团队拆分微服务时,完全不考虑业务域边界,凭感觉拆分,最终导致服务边界模糊,一个业务逻辑需要调用十几个服务,接口爆炸,维护成本极高。
3.4 粒度适配原则(避免过度拆分)
微服务不是拆得越细越好,过度拆分只会导致「纳米服务」,运维成本、通信成本、分布式事务成本会指数级上升,反而降低研发效率。
拆分粒度的判断标准,有3个可落地的行业通用准则:
- 2 Pizza团队原则:一个微服务的维护团队,能被2个披萨喂饱(6-10人),一个团队负责1-3个微服务。如果一个微服务只需要1-2人维护,甚至半个开发就能搞定,那这个服务的粒度就太细了,完全可以合并;
- 迭代频率对齐原则:迭代频率相近的业务能力,可以放到同一个服务里。比如商品的基础信息管理、库存管理、价格管理,迭代频率都是比较低的,可以放到同一个商品服务里;而订单的创建、支付回调、物流同步,迭代频率很高,可以放到订单服务里。如果把迭代频率差异极大的模块放到同一个服务里,会导致频繁的发布,影响服务的稳定性;
- 变更影响范围原则:如果两个业务模块,每次变更都会同时修改,那它们就应该放到同一个服务里;如果两个模块的变更完全互不影响,就应该拆分。
3.5 单向依赖与依赖倒置原则(避免循环依赖)
服务之间的依赖必须是单向的,绝对禁止循环依赖。比如订单服务依赖商品服务、商品服务依赖用户服务,这是正常的单向依赖;但如果商品服务又依赖订单服务,就形成了循环依赖,此时两个服务必须同时发布、同时部署,完全失去了独立迭代的能力,耦合度极高。
解决循环依赖的核心方案,是依赖倒置原则:通过抽象接口解耦,把强依赖变成弱依赖,通过事件驱动的方式,替换同步调用。
比如商品服务需要获取订单的销量数据,不需要直接依赖订单服务,而是通过订单服务发布的「订单创建事件」,商品服务订阅该事件,更新自己的销量数据,把同步的强依赖,变成异步的弱依赖,彻底解决循环依赖的问题。
3.6 故障隔离原则(架构稳定性底线)
拆分微服务的核心目标之一,就是实现故障隔离,避免一个模块的故障拖垮整个系统。所以在拆分的时候,必须考虑故障的传播路径,确保核心业务链路不受非核心模块故障的影响。
故障隔离的核心要求:
- 核心服务与非核心服务拆分,非核心服务的故障,不能影响核心服务的运行。比如通知服务挂了,不能影响订单的创建、支付的完成;
- 服务之间通过限流、熔断、降级机制,实现故障隔离,避免级联崩溃。比如商品服务挂了,订单服务可以触发降级,返回商品的缓存快照信息,保证下单流程正常进行;
- 资源隔离:核心服务与非核心服务使用独立的资源池,比如数据库连接池、线程池、Redis实例,避免非核心服务的资源耗尽,影响核心服务。
3.7 演进式拆分原则(避免一次定终身)
业务是不断变化的,微服务的拆分也不是一次定终身的,必须允许服务的合并、再拆分、重构。
比如初期把商品和库存放到了同一个服务里,随着业务发展,库存的并发量越来越高,迭代频率越来越快,就可以把库存模块从商品服务里拆分出来,形成独立的库存服务;反之,如果两个服务的迭代频率越来越低,每次变更都需要同步修改,就可以把它们合并成一个服务,降低运维成本。
架构的演进,永远要跟着业务走,而不是反过来让业务适应架构。
四、实战落地:电商场景微服务拆分完整实现
我们以最经典的电商场景为例,基于最新稳定版技术栈,实现从单体到微服务的拆分,所有代码均符合规范,可直接编译运行。
4.1 技术栈选型与版本说明
| 组件 | 版本 | 说明 |
| JDK | 17 | LTS稳定版 |
| Spring Boot | 3.2.4 | 最新稳定版 |
| Spring Cloud | 2023.0.1 | 与Spring Boot 3.2.x兼容 |
| Spring Cloud Alibaba | 2023.0.1.0 | Nacos注册配置中心 |
| MyBatis Plus | 3.5.6 | 持久层框架 |
| SpringDoc OpenAPI | 2.5.0 | Swagger3规范实现,兼容Spring Boot3 |
| Lombok | 1.18.32 | 代码简化工具 |
| FastJSON2 | 2.0.52 | JSON序列化工具 |
| Guava | 33.1.0-jre | 集合工具类 |
| MySQL | 8.0 | 数据库 |
4.2 公共父工程POM定义
所有微服务统一继承该父工程,统一管理版本,避免版本冲突。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.4</version>
<relativePath/>
</parent>
<groupId>com.jam.demo</groupId>
<artifactId>micro-service-demo-parent</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<name>micro-service-demo-parent</name>
<modules>
<module>user-service</module>
<module>goods-service</module>
<module>order-service</module>
</modules>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2023.0.1</spring-cloud.version>
<spring-cloud-alibaba.version>2023.0.1.0</spring-cloud-alibaba.version>
<mybatis-plus.version>3.5.6</mybatis-plus.version>
<springdoc.version>2.5.0</springdoc.version>
<lombok.version>1.18.32</lombok.version>
<fastjson2.version>2.0.52</fastjson2.version>
<guava.version>33.1.0-jre</guava.version>
<mysql.version>8.0.36</mysql.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>${fastjson2.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
4.3 用户服务实现(用户域微服务)
用户服务负责用户的注册、查询、权限校验,完全遵循数据自治原则,独享用户数据库,对外提供标准化接口。
4.3.1 用户服务POM文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.jam.demo</groupId>
<artifactId>micro-service-demo-parent</artifactId>
<version>1.0.0</version>
</parent>
<groupId>com.jam.demo</groupId>
<artifactId>user-service</artifactId>
<version>1.0.0</version>
<name>user-service</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
4.3.2 数据库脚本(MySQL8.0)
CREATE DATABASE IF NOT EXISTS user_db DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_0900_ai_ci;
USE user_db;
DROP TABLE IF EXISTS t_user;
CREATE TABLE t_user (
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '用户ID',
username VARCHAR(64) NOT NULL COMMENT '用户名',
password VARCHAR(128) NOT NULL COMMENT '密码',
phone VARCHAR(11) NOT NULL COMMENT '手机号',
real_name VARCHAR(32) DEFAULT NULL COMMENT '真实姓名',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0-禁用,1-正常',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (id),
UNIQUE KEY uk_username (username),
UNIQUE KEY uk_phone (phone)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表';
4.3.3 实体类定义
package com.jam.demo.user.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 用户实体类
* @author ken
* @since 2026-03-19
*/
@Data
@TableName("t_user")
@Schema(description = "用户实体")
public class User {
@TableId(type = IdType.AUTO)
@Schema(description = "用户ID", example = "1")
private Long id;
@Schema(description = "用户名", example = "zhangsan")
private String username;
@Schema(description = "密码", example = "123456")
private String password;
@Schema(description = "手机号", example = "13800138000")
private String phone;
@Schema(description = "真实姓名", example = "张三")
private String realName;
@Schema(description = "状态:0-禁用,1-正常", example = "1")
private Integer status;
@Schema(description = "创建时间")
private LocalDateTime createTime;
@Schema(description = "更新时间")
private LocalDateTime updateTime;
}
4.3.4 Mapper接口定义
package com.jam.demo.user.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jam.demo.user.entity.User;
import org.apache.ibatis.annotations.Mapper;
/**
* 用户Mapper接口
* @author ken
* @since 2026-03-19
*/
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
4.3.5 Service层接口与实现
package com.jam.demo.user.service;
import com.jam.demo.user.entity.User;
/**
* 用户服务接口
* @author ken
* @since 2026-03-19
*/
public interface UserService {
/**
* 根据用户ID查询用户信息
* @param id 用户ID
* @return 用户实体
*/
User getUserById(Long id);
/**
* 创建用户
* @param user 用户实体
* @return 创建成功的用户ID
*/
Long createUser(User user);
}
package com.jam.demo.user.service.impl;
import com.jam.demo.user.entity.User;
import com.jam.demo.user.mapper.UserMapper;
import com.jam.demo.user.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* 用户服务实现类
* @author ken
* @since 2026-03-19
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
private final UserMapper userMapper;
private final TransactionTemplate transactionTemplate;
@Override
public User getUserById(Long id) {
if (ObjectUtils.isEmpty(id)) {
log.warn("查询用户失败,用户ID为空");
return null;
}
return userMapper.selectById(id);
}
@Override
public Long createUser(User user) {
if (ObjectUtils.isEmpty(user)) {
log.warn("创建用户失败,用户实体为空");
return null;
}
if (!StringUtils.hasText(user.getUsername())) {
log.warn("创建用户失败,用户名为空");
return null;
}
if (!StringUtils.hasText(user.getPhone())) {
log.warn("创建用户失败,手机号为空");
return null;
}
return transactionTemplate.execute(new TransactionCallback<Long>() {
@Override
public Long doInTransaction(TransactionStatus status) {
try {
int insert = userMapper.insert(user);
if (insert > 0) {
log.info("创建用户成功,用户ID:{}", user.getId());
return user.getId();
}
log.warn("创建用户失败,数据库插入失败");
return null;
} catch (Exception e) {
status.setRollbackOnly();
log.error("创建用户异常,用户名:{}", user.getUsername(), e);
return null;
}
}
});
}
}
4.3.6 Controller层接口定义
package com.jam.demo.user.controller;
import com.jam.demo.user.entity.User;
import com.jam.demo.user.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
/**
* 用户接口控制器
* @author ken
* @since 2026-03-19
*/
@RestController
@RequestMapping("/user")
@RequiredArgsConstructor
@Tag(name = "用户接口", description = "用户信息管理相关接口")
public class UserController {
private final UserService userService;
@GetMapping("/{id}")
@Operation(summary = "根据ID查询用户信息", description = "通过用户ID查询用户详细信息")
public User getUserById(
@Parameter(description = "用户ID", required = true, example = "1")
@PathVariable Long id) {
return userService.getUserById(id);
}
@PostMapping("/create")
@Operation(summary = "创建用户", description = "新增用户信息")
public Long createUser(@RequestBody User user) {
return userService.createUser(user);
}
}
4.3.7 启动类与配置文件
package com.jam.demo.user;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Info;
/**
* 用户服务启动类
* @author ken
* @since 2026-03-19
*/
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.jam.demo.user.mapper")
@OpenAPIDefinition(info = @Info(title = "用户服务API", version = "1.0.0", description = "用户服务接口文档"))
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
server:
port: 8081
spring:
application:
name: user-service
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/user_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: root
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
springdoc:
api-docs:
enabled: true
swagger-ui:
enabled: true
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
4.4 商品服务实现(商品域微服务)
商品服务负责商品的信息管理、库存管理,独享商品数据库,对外提供标准化接口,完整代码结构与用户服务一致,核心代码如下:
4.4.1 数据库脚本
CREATE DATABASE IF NOT EXISTS goods_db DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_0900_ai_ci;
USE goods_db;
DROP TABLE IF EXISTS t_goods;
CREATE TABLE t_goods (
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '商品ID',
goods_name VARCHAR(128) NOT NULL COMMENT '商品名称',
price DECIMAL(10,2) NOT NULL COMMENT '商品价格',
stock INT NOT NULL DEFAULT 0 COMMENT '商品库存',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0-下架,1-上架',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (id),
KEY idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商品表';
4.4.2 核心Service实现(库存扣减逻辑)
package com.jam.demo.goods.service.impl;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.jam.demo.goods.entity.Goods;
import com.jam.demo.goods.mapper.GoodsMapper;
import com.jam.demo.goods.service.GoodsService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.ObjectUtils;
/**
* 商品服务实现类
* @author ken
* @since 2026-03-19
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class GoodsServiceImpl implements GoodsService {
private final GoodsMapper goodsMapper;
private final TransactionTemplate transactionTemplate;
@Override
public Goods getGoodsById(Long id) {
if (ObjectUtils.isEmpty(id)) {
log.warn("查询商品失败,商品ID为空");
return null;
}
return goodsMapper.selectById(id);
}
@Override
public Boolean deductStock(Long goodsId, Integer num) {
if (ObjectUtils.isEmpty(goodsId) || ObjectUtils.isEmpty(num) || num <= 0) {
log.warn("扣减库存失败,参数非法,goodsId:{}, num:{}", goodsId, num);
return false;
}
return transactionTemplate.execute(new TransactionCallback<Boolean>() {
@Override
public Boolean doInTransaction(TransactionStatus status) {
try {
LambdaUpdateWrapper<Goods> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(Goods::getId, goodsId)
.ge(Goods::getStock, num)
.setSql("stock = stock - " + num);
int update = goodsMapper.update(null, updateWrapper);
if (update > 0) {
log.info("扣减库存成功,goodsId:{}, num:{}", goodsId, num);
return true;
}
log.warn("扣减库存失败,库存不足,goodsId:{}, num:{}", goodsId, num);
return false;
} catch (Exception e) {
status.setRollbackOnly();
log.error("扣减库存异常,goodsId:{}, num:{}", goodsId, num, e);
return false;
}
}
});
}
}
4.5 订单服务实现(订单域微服务)
订单服务是核心链路服务,依赖用户服务和商品服务,通过OpenFeign实现服务间调用,遵循单向依赖原则,独享订单数据库。
4.5.1 Feign客户端定义(服务间调用)
package com.jam.demo.order.feign;
import com.jam.demo.order.entity.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* 用户服务Feign客户端
* @author ken
* @since 2026-03-19
*/
@FeignClient(name = "user-service", fallback = UserFeignFallback.class)
public interface UserFeignClient {
@GetMapping("/user/{id}")
User getUserById(@PathVariable("id") Long id);
}
package com.jam.demo.order.feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* 商品服务Feign客户端
* @author ken
* @since 2026-03-19
*/
@FeignClient(name = "goods-service", fallback = GoodsFeignFallback.class)
public interface GoodsFeignClient {
@PostMapping("/goods/deductStock")
Boolean deductStock(@RequestParam("goodsId") Long goodsId, @RequestParam("num") Integer num);
}
4.5.2 订单创建核心逻辑
package com.jam.demo.order.service.impl;
import com.jam.demo.order.entity.Order;
import com.jam.demo.order.entity.User;
import com.jam.demo.order.feign.GoodsFeignClient;
import com.jam.demo.order.feign.UserFeignClient;
import com.jam.demo.order.mapper.OrderMapper;
import com.jam.demo.order.service.OrderService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.ObjectUtils;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 订单服务实现类
* @author ken
* @since 2026-03-19
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
private final OrderMapper orderMapper;
private final UserFeignClient userFeignClient;
private final GoodsFeignClient goodsFeignClient;
private final TransactionTemplate transactionTemplate;
@Override
public Long createOrder(Long userId, Long goodsId, Integer num, BigDecimal price) {
if (ObjectUtils.isEmpty(userId) || ObjectUtils.isEmpty(goodsId) || ObjectUtils.isEmpty(num) || num <= 0 || ObjectUtils.isEmpty(price)) {
log.warn("创建订单失败,参数非法,userId:{}, goodsId:{}, num:{}, price:{}", userId, goodsId, num, price);
return null;
}
User user = userFeignClient.getUserById(userId);
if (ObjectUtils.isEmpty(user) || user.getStatus() != 1) {
log.warn("创建订单失败,用户不存在或已禁用,userId:{}", userId);
return null;
}
Boolean deductResult = goodsFeignClient.deductStock(goodsId, num);
if (!deductResult) {
log.warn("创建订单失败,库存扣减失败,goodsId:{}, num:{}", goodsId, num);
return null;
}
Order order = new Order();
order.setUserId(userId);
order.setGoodsId(goodsId);
order.setNum(num);
order.setTotalPrice(price.multiply(BigDecimal.valueOf(num)));
order.setOrderStatus(1);
order.setCreateTime(LocalDateTime.now());
order.setUpdateTime(LocalDateTime.now());
return transactionTemplate.execute(new TransactionCallback<Long>() {
@Override
public Long doInTransaction(TransactionStatus status) {
try {
int insert = orderMapper.insert(order);
if (insert > 0) {
log.info("创建订单成功,订单ID:{}", order.getId());
return order.getId();
}
log.warn("创建订单失败,数据库插入失败");
return null;
} catch (Exception e) {
status.setRollbackOnly();
log.error("创建订单异常,userId:{}, goodsId:{}", userId, goodsId, e);
return null;
}
}
});
}
}
五、微服务拆分的常见坑与避坑指南
5.1 为了微服务而微服务,过度拆分
很多团队不管业务规模大小,上来就拆十几个甚至几十个服务,完全不考虑团队规模和运维能力,最终导致研发效率不升反降。避坑方案:先明确拆分的目标是解决什么痛点,没有痛点就不要拆分;初创团队优先用单体,业务增长到瓶颈后,再逐步拆分,先拆2-3个核心服务,再根据业务发展逐步扩展。
5.2 共享数据库,拆成分布式单体
这是最常见的错误,很多团队拆分了服务,但是所有服务都连接同一个数据库,服务间通过数据库表耦合,本质上还是单体。避坑方案:严格遵守数据自治原则,一个服务只能操作自己的数据库,跨服务数据获取只能通过接口调用;绝对禁止跨库连表、禁止多个服务操作同一张表。
5.3 服务间循环依赖,调用关系成蜘蛛网
服务拆分后,没有梳理清楚依赖关系,导致服务间循环依赖,A调用B,B调用C,C又调用A,最终所有服务必须同时发布、同时部署,完全失去了独立迭代的能力。避坑方案:严格遵守单向依赖原则,通过架构治理禁止循环依赖;通过事件驱动的异步调用,替换同步的强依赖,解耦服务间的调用关系。
5.4 忽略服务治理,拆了之后没人管
很多团队只关注拆分,不关注服务治理,没有注册中心、网关、监控、链路追踪、限流熔断,最终导致服务出了问题根本无法定位,一个服务挂了整个系统都崩溃。避坑方案:服务治理体系必须与拆分同步建设,先搭建核心的注册中心、网关、监控告警,再进行服务拆分;每拆分一个服务,都必须纳入治理体系,完善监控、限流、熔断降级策略。
5.5 一步到位全量拆分,上线翻车
很多团队搞休克式改造,一次性把整个单体拆成十几个服务,上线后问题频发,根本无法回滚,最终导致业务停摆。避坑方案:严格遵循渐进式演进路径,先模块化、再拆库、再拆非核心服务、最后拆核心服务;每一步都有灰度验证和回滚方案,确保风险可控。
六、总结
微服务架构从来不是银弹,它是一把双刃剑,在解决单体架构痛点的同时,也带来了分布式架构的复杂度。架构演进的唯一驱动力,永远是业务发展,而不是技术炫技。
微服务拆分的核心,从来不是技术实现,而是对业务域的深刻理解,只有把业务边界梳理清楚,才能拆分出边界清晰、高内聚低耦合的微服务。记住:好的架构是演进出来的,不是设计出来的;适合业务的架构,才是最好的架构。