01 FinFlow 项目介绍

全栈学习体系大约 8 分钟

01 FinFlow 项目介绍

这是 FinFlow 系列的第一篇。先看懂这个项目「是什么」和「为什么」,再动手写代码。


1. 背景 —— 为什么做这个项目

1.1 一句话缘起

FinFlow 是一个面试作品级的金融微服务项目。它的目标不是上线赚钱,而是用一套完整的代码 + 文档,在面试中证明一件事:

我不仅能写 Java 代码,更能设计系统、做技术决策、并把决策讲清楚。

1.2 这个项目在回答什么问题

面向的 JD 是一份 Java 资深开发 / 架构工程师 岗位(30-35K,5 年+ 经验),要求覆盖:

  • 微服务架构设计与落地
  • 分库分表、分布式事务
  • Redis、Kafka、MySQL 性能调优
  • JVM 调优实战
  • 高并发处理经验

传统面试中,候选人只能「口头描述」这些能力。FinFlow 的做法是:每一项 JD 要求都有对应的代码、压测数据、ADR 决策文档

1.3 项目定位

打个比方:FinFlow 就像消防演练

它不是真银行,但整套流程和真银行一样 —— 用户注册 → 借款申请 → 风控审核 → 放款 → 还款 → 对账,一个环节都不少。学员学完这套流程,进到真正的金融项目就不会懵。

1.4 设计哲学

整个项目有一条铁律:不拍脑袋选技术,每个决策都要写 ADR(架构决策记录)

比如「为什么用 Kafka 而不是 RocketMQ」「为什么 MVP 不用 Dubbo」「分片键为什么选 user_id」—— 这些问题在项目中都有书面答案。


2. 核心功能

FinFlow 模拟了一套互联网消费信贷的完整业务流程。

2.1 一句话讲清业务

用户在 App 上看到一款借款产品 → 提交申请 → 系统自动风控审核 → 审核通过 → 钱打到用户账户 → 用户到期还款。

这中间涉及 5 个核心环节:

环节对应服务做什么
身份user-service注册、登录、实名认证
借款loan-service借款申请、审批状态、合同生命周期
风控risk-service准入规则检查、反欺诈、额度决策
放款/还款payment-service资金流水、还款处理、对账
搜索search-service借款产品目录搜索

2.2 端到端流程

用一句话走完「用户借一笔钱」的全链路:

用户登录 → 搜索借款产品 → 提交借款申请
→ 风控自动审核(查黑名单 + 算额度)
→ 审核通过 → 触发放款(调用支付渠道)
→ 放款成功 → 记资金流水
→ 到期自动还款(或主动还款)
→ T+1 日对账,确保流水和余额一致

2.3 哪些做了、哪些没做

为了在 4 周内完成 MVP,项目做了清晰的边界划分:

In Scope(做)

  • 用户注册 / 登录 / 资料管理
  • 借款产品目录搜索
  • 借款申请 → 风控决策 → 放款 → 还款的完整编排
  • 资金流水记录与对账
  • Kafka 异步消息、Redis 缓存、ES 搜索
  • 压测报告 + JVM 调优

Out of Scope(明确不做)

  • 真实支付清结算(用 mock 支付网关替代)
  • 真实征信数据(用本地种子数据替代)
  • 运营后台 UI(只保留 API)
  • 前端界面(用 HTTP Client 脚本 + 录屏演示)

3. 使用的技术

3.1 技术栈总表

层级选型选型理由
语言Java 21虚拟线程、Record 类、Pattern Matching —— 21 才像现代 Java
框架Spring Boot 3.2.5业界标准,生态最全,面试最认
网关Spring Cloud Gateway基于 WebFlux,非阻塞,比 Zuul 性能好
构建Gradle 8比 Maven 灵活,多模块项目管理更简洁
数据库MySQL 8.0金融系统标配 InnoDB,事务 + 行锁成熟可靠
缓存Redis 7.2Lua 脚本原子操作,支持分布式锁和限流
消息队列Kafka 3.6高吞吐、持久化、顺序保证,适合削峰和事件总线
搜索Elasticsearch 8.11分词搜索 + 复合筛选,产品目录查询场景首选
容器Docker Compose一键起全套中间件,15 分钟从 clone 到跑通
观测Actuator + Micrometer健康检查 + Prometheus 指标预留

3.2 两个典型选型决策

为什么用 Gradle 而不是 Maven?

Maven 的 XML 在多模块项目中会迅速膨胀。Gradle 用 Groovy DSL,6 个子模块的构建脚本总共不到 30 行。且 Gradle 的增量编译和构建缓存比 Maven 快 2-3 倍 —— 对于需要频繁构建的微服务项目,这个差距很明显。

为什么 MVP 阶段不用 Dubbo?

Dubbo 是好东西,但 MVP 阶段的学习成本和调试成本太高。项目用 Spring Web(REST)做服务间调用,但有一个关键设计:每个服务的接口和 DTO 都放在独立的 *-api 模块里,物理上和 Spring 框架解耦。这意味着将来切 Dubbo Triple 时,只需要换通信层,业务代码一行不动。这个决策也写在了 ADR 里。


4. 系统架构图

4.1 C4 上下文图

从外部视角看 FinFlow 的全貌:

                    ┌────────────────┐
                    │    终端用户     │
                    │  (APP / H5)    │
                    └────────┬───────┘
                             │ HTTPS (REST / JSON)
                             ▼
                    ┌────────────────┐
                    │  Gateway       │
                    │  (8080)        │ ── 统一路由 / 鉴权 / 限流
                    └────────┬───────┘
                             │
           ┌───────┬─────────┼─────────┬─────────┬─────────┐
           ▼       ▼         ▼         ▼         ▼         ▼
      ┌────────┐┌──────┐┌─────────┐┌───────┐┌──────────┐
      │ user   ││ loan ││ risk    ││payment││  search   │
      │(8081)  ││(8082)││(8083)   ││(8084) ││ (8085)    │
      └────────┘└──────┘└─────────┘└───────┘└──────────┘
           │        │        │          │         │
           └────┬───┴────┬───┴─────┬────┴────┬────┘
                ▼        ▼         ▼         ▼
            ┌───────┐ ┌───────┐ ┌──────┐ ┌──────────┐
            │ MySQL │ │ Redis │ │Kafka │ │Elasticsearch│
            └───────┘ └───────┘ └──────┘ └──────────┘
                                  │
                                  ▼
                         ┌───────────────┐
                         │  外部系统(Mock)│
                         │  · 支付渠道    │
                         │  · 征信数据    │
                         └───────────────┘

4.2 六个服务一句话职责

服务端口一句话职责数据所有权
gateway-service8080统一入口,负责鉴权、路由、限流无(无状态)
user-service8081用户注册登录、实名认证user
loan-service8082借款申请的编排者,管理借款生命周期loan_order
risk-service8083风控规则引擎,决定「借不借、借多少」risk_decision
payment-service8084管钱 —— 放款、还款、资金流水、对账payment_orderfund_flow
search-service8085借款产品搜索ES 索引 product_catalog

关键原则:一张表只由一个服务写。其他服务要读数据,只能通过 API 或 Kafka 事件。


5. 技术架构图

5.1 分层架构

┌─────────────────────────────────────────────────┐
│                    表示层                        │
│          HTTP Client / Swagger UI               │
└─────────────────────┬───────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────┐
│              网关层 (gateway-service)            │
│      鉴权 · 限流 · 路由 · TraceId 生成           │
└─────────────────────┬───────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────┐
│               业务服务层 (5 个微服务)             │
│  user · loan · risk · payment · search          │
│  ┌─────────────────────────────────────────┐    │
│  │  同步通信:REST (Spring Web)             │    │
│  │  异步通信:Kafka (事件驱动)              │    │
│  └─────────────────────────────────────────┘    │
└─────────────────────┬───────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────┐
│               中间件层 (4 个中间件)               │
│  ┌──────────┐ ┌──────────┐ ┌──────┐ ┌──────┐   │
│  │  MySQL   │ │  Redis   │ │Kafka │ │  ES  │   │
│  │ 事务/索引│ │缓存/锁   │ │削峰  │ │搜索  │   │
│  └──────────┘ └──────────┘ └──────┘ └──────┘   │
└─────────────────────────────────────────────────┘

5.2 同步 vs 异步的分工

场景方式为什么
风控决策同步 REST必须拿到结果才能继续,不能异步
查询用户信息同步 REST强读一致,调用链短
放款结果通知异步 Kafka支付渠道可能有秒级延迟,不阻塞前端
产品变更通知异步 Kafka搜索索引更新不要求实时

5.3 一笔借款的完整调用链

[用户] ──申请借款──▶ [gateway] ──JWT 鉴权──▶ [loan-service]
                                                  │
                      ┌───────────────────────────┤
                      │ 同步调用                    │ 同步调用
                      ▼                           ▼
               [user-service]            [risk-service]
               查用户身份 + 状态           跑风控规则 → 返回额度
                      │                           │
                      └───────────┬───────────────┘
                                  │ 风控通过
                                  ▼
                          [loan-service]
                          更新借款单为 APPROVED
                          发事件 loan.approved → Kafka
                                  │
                                  ▼ (异步消费)
                          [payment-service]
                          调用 mock 支付渠道放款
                          记 fund_flow 流水
                          发事件 payment.disbursed → Kafka
                                  │
                                  ▼ (异步消费)
                          [loan-service]
                          更新借款单为 DISBURSED
                                  │
                                  ▼
                            [用户收到钱]

6. 项目亮点

亮点 1:业务导向的微服务拆分

服务命名直接反映业务,不是技术词。6 个服务按「数据所有权」拆分 —— 每张表只有一个写入者,天然避免分布式写冲突。比如 payment-service 管资金流水,loan-service 要查流水只能调 API 或消费 Kafka 事件,绝不直接读对方的数据库。

亮点 2:每个技术选择都有 ADR

不是「我觉得 Kafka 好」而是「对比了 Kafka vs RocketMQ vs RabbitMQ,在吞吐量、运维成本、生态成熟度三个维度评估后选 Kafka,详见 ADR」。项目计划产出 5 篇以上 ADR 文档,面试时可以直接打开给面试官看。

亮点 3:JVM 内存优化实战

6 个 Spring Boot 服务 + 4 个中间件,全栈 Docker Compose 跑起来 RSS 只有 1522 MB。通过 JVM MaxRAMPercentage=50、SerialGC、虚拟线程等手段,单服务堆内存控制在 93-115 MB 之间。这不是理论 —— 每一版都跑了实际的内存基线测试,数据在 docs/05-memory-baseline.md

亮点 4:为高并发留了后手

MVP 不搞分库分表,但 fund_flow 表已预留分片键 user_id,ShardingSphere 接入成本低。抢购接口设计了 Redis Lua 原子扣减 + Kafka 削峰 + Sentinel 限流的三段式方案,5000 并发抢 1000 份不超卖。这些都在文档里有完整的演进路径。

亮点 5:面试讲稿完备

docs/00-jd.md 是一份逐条拆解 JD 要求的证据映射表,每项能力都配有 STAR 话术(场景 → 动作 → 难点 → 结果),面试时直接调用。另有 7 条高频追问的 30 秒速查卡,覆盖「为什么 Seata AT 不是 TCC」「缓存一致性怎么保证」「10 倍流量怎么扩」等经典问题。

亮点 6:15 分钟可跑通全链路

git clone 到跑通「注册 → 登录 → 借款申请 → 风控 → 放款 → 查询流水」全流程,只需 15 分钟:

cp .env.example .env
./scripts/up.sh              # 一键起 MySQL + Redis + Kafka + ES
./gradlew build -x test      # 构建 6 个服务
# 逐一启动服务,或 IDE 里直接跑
curl http://localhost:8081/actuator/health  # 验证所有服务

接下来读什么

这一篇是「看懂全貌」。下一篇开始动手 —— 03 Spring Boot 多模块项目搭建 会带你从零创建 6 个 Gradle 子模块,跑通第一个健康检查接口。

本模块三篇文章的学习顺序

  1. 01-项目介绍(本文)—— 理解项目全貌
  2. 03-项目搭建 —— 从零搭建 Gradle 多模块骨架
  3. 04-数据库设计 —— 金融系统核心表结构
  4. 05-项目分层架构 —— 代码组织规范与 DDD 分层
本文阅读量