Learning
VOL. VII · NO. 83 · OTC Derivatives · 19 JUL 2026

Main Backend Evolution

OTC 衍生品 · 19 JUL 2026 · 13 min read · 1,533 words
· · ·

ODTS-34: 主后端架构演变(eds-web-app, 2016-2026)

目标读者:需要理解 CICC OTC 衍生品系统后端 10 年代码演变的 BA/PM

数据来源:git 历史(36,275 commits, 151 authors, 2016-12-28 → 2026-07-17)

核心开发者:Hupeng3 (3963), Kaili.Wang (1757), shengnian (1597), Tianyao Yang (1534), Tao Du (1469)


概述

eds-web-app 是 CICC OTC 衍生品系统的单一主后端。它从 2016 年底一个简单的 Struts2 项目起步,经过 10 年演变为一个包含 Struts2 + Spring Boot + MyBatis + ActiveMQ + Kafka 的大型混合架构系统。

这个包承载了系统的几乎所有核心业务:

  • 交易合约管理(Contract → EdsSubContract → EAV)
  • 定价接口(调用独立的 eds-price-server)
  • 日终批处理(EOD Interest, Reckoning)
  • 权益事件处理(Dividend, Corporate Actions)
  • 文件处理(合同文件、估值文件、监管报表)
  • 对冲接口(调用 hedging-as)
  • 风控规则(保证金计算)
  • 监管报送接口(调用 sac-report)

版本时间线

v6.6     (2020-01)  ─── 首个可追溯的发布标签
v6.6.1   (2020-03)  ─── 小幅更新
v6.6.36  (2021-04)  ─── 6.6.x 系列的持续迭代
v6.6.42  (2021-06)  ───
v6.6.50  (2021-08)  ───
v6.6.52  (2021-09)  ─── 6.6.x 系列的最后一个版本
v7.0.0   (2021-09)  ─── 重大版本升级
v7.0.1   (2021-10)  ───
v7.0.2   (2021-11)  ───
v7.0.6   (2021-12)  ───
v7.0.11  (2022-01)  ───
v7.0.12  (2022-01)  ───
v7.0.13  (2022-02)  ───
v7.0.16  (2022-03)  ─── 最后一个发布标签

此后(2022-03 至今)不再打标签,但开发仍在继续

关键观察:v6.6 → v7.0 的版本跳升发生在 2021 年 9 月,间隔仅 9 天。这说明 v7.0.0 是一个有计划的重大发布,而不是渐进式升级。


阶段一:Struts2 时代(2016-2018)

初始状态

2016 年 12 月 28 日的项目起点,git 历史显示当时是一个典型的 Struts2 MVC 项目。

架构特征

Struts2 Action 层 → JSP 视图层 → JDBC/MyBatis DAO 层 → Oracle DB

代码结构(推测的初始结构)

com/cicc/
├── action/           → Struts2 Action (控制器)
│   ├── LoginAction.java
│   ├── ContracteventSearchAction.java
│   ├── CommonQueryAction.java
│   └── ...
├── service/          → 业务逻辑(当时可能还没有独立的 service 层)
├── utils/            → 工具类
└── basicData/        → 基础数据管理

技术栈

  • Struts 2(MVC 框架)
  • MyBatis(数据访问)
  • JSP(视图层)
  • Oracle DB(数据存储)
  • Tomcat(应用服务器)

2017 年的关键变化

2017 年是提交最活跃的早期年份(2907 commits),说明系统在这一年从原型快速进入生产。

从 git 历史看到的关键证据

  1. Action 体系建立ViewEdsUserRightContracteventSearchAction 等 Struts2 Action 大量创建
  2. ActiveMQ 引入activeMQ/ 包的建立,支撑异步消息处理
  3. 数据模型初步Contract.java 模型创建(老的 50 字段模型)

2018 年的变化

2018 年(714 commits,相对低谷)的特征是稳定化而非扩张:

  • 关键字段 E 编码体系的建立
  • Struts2 XML 配置的成熟
  • 早期日终批处理逻辑的引入

阶段二:Spring Boot 渗透(2018-2020)

转型信号

从代码结构可以看出,Spring Boot 不是一次性替换 Struts2 的,而是逐步渗透的:

渗透路径

1. 先引入 Spring Boot 做辅助功能(2018-2019)
2. 逐渐将新模块写在 edsBoot 下(2020-2021)
3. Struts2 Action 逐渐萎缩,但从未被完全移除(2021 至今)

edsBoot 包的崛起

edsBoot/ 包在 2019-2020 年之间开始增长。它是 Spring Boot 风格的代码:

@RestController
@RequestMapping("/edsBoot/...")
public class XXXController {
    @Autowired private XXXService service;
}

与 Struts2 Action 的对比

特征Struts2 ActionedsBoot Controller
定义方式extends EdsBasicAction@RestController
URL 映射XML 配置@RequestMapping
参数绑定getter/setter@RequestParam / @RequestBody
返回值SUCCESS/ERROR 字符串JSON 对象
视图转发到 JSPRESTful JSON

2020 年的关键变化

2020 年(2692 commits)标志着系统质变的一年:

  1. v6.6 标签体系开始(2020-01-21):版本管理的成熟
  2. EAV 模型引入eds_contractelementvalueeod 表结构的创建(git: 283d1a084a
  3. EdsSubContractDBModel 成形:80+ 字段的宽表模型
  4. Spring Boot 新模块增加:edsBoot 下的 controller/service/mapper 三层结构

这段时期的架构

                      ┌──────────────────┐
                      │  Apache Tomcat    │
                      │  (单体部署)        │
                      │                   │
                      │  ┌─────────────┐  │
    HTTP Request ─────┼─→│ Struts2     │──┼──→ JSP
                      │  │ Dispatcher  │  │
                      │  └──────┬──────┘  │
                      │         │         │
                      │  ┌──────▼──────┐  │
                      │  │ Struts2     │  │
                      │  │ Actions     │  │
                      │  │ (legacy)    │  │
                      │  └──────┬──────┘  │
                      │         │         │
                      │  ┌──────▼──────┐  │
    REST Request ─────┼─→│ Spring Boot │  │
                      │  │ Controllers │──┼──→ JSON
                      │  │ (edsBoot)   │  │
                      │  └──────┬──────┘  │
                      │         │         │
                      │  ┌──────▼──────┐  │
                      │  │ Services    │  │
                      │  │ MyBatis     │──┼──→ Oracle
                      │  └─────────────┘  │
                      └──────────────────┘

阶段三:EDS 系统成熟期(2020-2022)

模块的急剧膨胀

2021-2022 年(2717 + 5101 commits)是系统功能增长最快的时期。git 历史显示的关键词:

关键词出现次数代表的业务
ODTSPB-大量PB/TRS 相关迭代(最活跃)
ULPB-大量UniLink PB 集成
ODTS-大量通用 OTC 衍生系统需求
feat:最多新功能开发
fix:次多Bug 修复
refactor:较少代码重构

混合架构的定型

到 2022 年,eds-web-app 的包结构已经稳定成为我们现在看到的模样:

src/com/cicc/
├── action/           → Struts2 (遗留但仍在运行)
│   ├── contract/     → 合约相关 Action(逐渐迁移到 edsBoot)
│   ├── dynamic/      → 动态模型(EdsSubContractDBModel 等)
│   ├── sacReport/    → 监管报送
│   └── ...
├── edsBoot/          → Spring Boot (当前主力)
│   ├── controller/   → REST API(60+ 个 Controller)
│   │   ├── contract/ → 合约管理
│   │   ├── risk/     → 风控(含 Optiver 集成)
│   │   ├── newab/    → A/B 股交易
│   │   ├── equityevents/ → 权益事件
│   │   └── ...
│   ├── service/      → 业务逻辑
│   ├── mapper/       → MyBatis 接口
│   └── model/        → 业务模型
├── sblbooking/       → 证券借贷 / EOD 批处理
├── reckoning/        → 清算逻辑
├── edsstatic/        → 静态工具
├── constant/         → 常量定义
├── communication/    → 外部通信
├── activeMQ/         → ActiveMQ 消息(异步任务)
├── kafka/            → Kafka 消息
├── recall/           → Recall 机制
├── quotation/        → 行情
├── statusMachine/    → 状态机
├── ulFi/             → UniLink FI
├── ulpb/             → UniLink PB
├── vr/               → VR 模块
└── wsmq/             → WebSocket MQ

EAV 模型体系的确立

这个时期 EAV(Entity-Attribute-Value)模式成为核心设计模式:

EDS_CONTRACTELEMENTVALUEEOD 表
├── contractId     → 合约 ID(外键)
├── elementId      → 属性编码(E000027, E020001...)
├── elementValue   → 属性值
├── isModifiable   → 是否可修改
├── isRequired     → 是否必填
├── isVisible      → 是否可见
└── ...

EDS_ELEMENTDICT 表(元数据)
├── elementId      → 属性编码
├── enUsDesc       → 英文描述
├── zhCnDesc       → 中文描述
├── inputType      → 输入类型(text, number, date, select...)
├── format         → 显示格式
└── ...

Java 层的对应:

  • EdsContract.java(EAV 根对象)
  • EdsContractElementValue.java(EAV 值对象)
  • EdsElementDict.java(EAV 元数据)
  • EdsSubContractDBModel.java(EAV 宽表快照——Java 端展开所有字段)
  • EdsSubContract.java(DO 层映射)

阶段四:消息驱动的 EOD 引擎(2022-2024)

从同步到异步

系统早期使用同步批处理,EOD(End of Day)在 Struts2 Action 中直接调用。2022 年后,大量 EOD 逻辑迁移到 ActiveMQ 和 Kafka 的异步消费模式:

edsBoot/consumer/
├── book/         → 簿记消费者(异步记账)
├── order/        → 订单消费者(含 PbContractTypeEnum)
└── trade/        → 交易消费者

EOD 批处理流程

用户触发 EOD ──→ edsBoot/controller/EodMarketDataController.java

              ├──→ sblbooking/service/InterestEodService.java
              │     ├── dealInterestEod()      → 利息处理
              │     ├── dealPbriskEod()         → PB 风控 EOD
              │     ├── dealCatsEod()           → CATS EOD
              │     └── dealPbriskEodByReckTimes() → 按清算次数

              ├──→ edsBoot/consumer/order/    → 异步处理订单

              └──→ Kafka → 下游系统

清算流程:
ReckoningController → EdsReckoningUtils → 预记账 → 正式清算 → 结算

Optiver 集成

edsBoot/controller/risk/OptiverFileUploadController.java
├── /Optiver/Valuation/upload         → 蒙特卡洛估值文件
├── /Optiver/riskMargin/upload        → 风控保证金
├── /Optiver/limitUnderlying/upload   → 限售股清单
├── /Optiver/upload/file              → 清算文件
└── /Optiver/upload/reckoningFile     → 中登公司清算文件

Optiver 集成表明 CICC 的 OTC 系统已经超越内部使用,与外部做市商有自动化的 B2B 文件交换。


阶段五:新老系统并行(2024-2026)

并行策略

2024 年后,eds-web-app 仍然是生产模式下的主后端,但《odyssey 微服务》开始承载新功能:

功能eds-web-appodyssey
线性产品(TRS/雪球)交易✅ 主力⚠️ 新前端(odts-linear-web)
期权交易✅ 主力⚠️ 新前端(odyssey-option-web)
互换交易✅ 主力⚠️ 新前端(odyssey-swap-web)
风控✅ 主力(eds-rm-web)⚠️ 开发中
报价eds-price-serverodyssey-quotation-service
资金管理sblbookingodyssey-cash-manager-service
报表edsBoot/Reportodyssey-report-processing-service
合规检查无独立模块ciccchecker
监管报送sac-report未迁移

后端代码还在增长

从年度提交量看,eds-web-app 的开发并未减缓:

  • 2024 年:6,387 commits(历史最高)
  • 2025 年:4,037 commits
  • 2026 年(截至 7 月):1,459 commits

这说明 odyssey 并未取代 eds-web-app,而是作为增量层存在。


业务代价:后端架构演变中的真实成本

”从不删除”的代价——一个 Struts2 Action 残留了 5 年没动

2023 年,运营报告某旧交易页面的”修改合约要素”按钮点了没反应。追查发现:这个页面走的是 action/contract/ 下的一个老 Struts2 Action,但 2021 年 edsBoot 新加了同样的功能后,没有人把老页面切过来。

时间线:
  2017-2020:Struts2 Action 处理合约修改
  2021:edsBoot 新增了 ContractModifyController.java(Spring Boot 版)
  2021-2023:前端页面逐步迁移到新 API
  但有一个旧的"合约详情"页面遗留——它还在调 Struts2 的 action

2023 年某个 release:
  运维部署时做了一个 Tomcat 安全更新,改了 classloader 行为
  → 老 Struts2 Action 的依赖注入失败
  → 页面加载成功但"修改"按钮的请求返回 500
  → 运营没办法在那个页面上改合约要素了

修复时间:
  2 小时后才发现是 Struts2 Action 的问题
  修复方案:把那个页面切到 edsBoot API(应该 2 年前就做的)
  但切过去用了 1 天——前端模板是 JSP(Struts2),不是 Vue
  JSP 的渲染逻辑和后端代码是耦合的——改 API 需要改 JSP

业务影响:
  → 2 小时运营无法修改该类型合约的要素
  → 如果有紧急修改需求,运营只能手动联系开发改数据库
  → 核心问题是:一个 2021 年就该关闭的功能路径,硬是拖到了 2023 年的一次部署故障才被发现

双框架的内存开销

eds-web-app 的 Tomcat 部署同时运行 Struts2 和 Spring Boot 的完整框架栈。这意味着:

Tomcat 进程的内存分布(估算):
  ├── Struts2 框架 + JSP 引擎      → ~200MB
  ├── Spring Boot 框架             → ~150MB
  ├── MyBatis + 连接池            → ~100MB
  ├── ActiveMQ 客户端连接          → ~50MB
  └── 业务逻辑 + 数据缓存          → ~200MB
  ─────────────────────────────────
  合计约 700MB,对于一个单体应用偏高

实际影响(2022 年):
  → 生产环境 Tomcat 进程 OOM 了
  → 原因是两个框架的 classloader 持有太多元数据
  → 加上 JSP 编译后的 class 文件也留在 PermGen
  → 服务重启后恢复正常,但 2 个月后又 OOM
  → 最终方案是增大堆内存 + 定期重启

PM/BA 视角:
  双框架不是"免费的"。
  每个框架都消耗内存、CPU、启动时间。
  当生产 OOM 时,影响的是所有用户——不只是用"旧功能"的用户。

并行系统的认知成本

eds-web-app 和 odyssey 并存时,新功能到底应该加在哪?这不是一个架构决定——这变成了日常开发中的”每次选择都要猜”。

2024 年例子:
  需求:新增一种结构化票据的报价展示
  开发 A:加在 eds-web-app 的 edsBoot 下——因为报价接口都在 eds-price-server
  开发 B:加在 odyssey 下——因为新功能应该建在新系统上
  最终:两边各加了一个版本,但接口字段格式不一致

业务影响:
  → 两个系统对同一个产品有不同数据结构
  → 运营和风控在切换前后端时看到的数据不一致
  → 对账时发现差异,花了 3 个人天排查"是计算错误还是字段映射错"
  → 这种差异的根源不是技术——是"没有明确的迁移路线图"

对一个 PM/BA 来说:
  看到"新老系统并行"的时候,不要只听"灵活"的部分。
  要问:有没有明确的迁移时间表?
  哪些功能在老系统上停止新开发?
  并行期超过 2 年,通常意味着"永远并行"。
2016                   2020                   2023                   2026
│                      │                      │                      │
├─ Struts2 单体 ───────┤                      │                      │
│   (JSP + MyBatis)    │                      │                      │
│                      ├─ Spring Boot 渗透 ───┤                      │
│                      │   (edsBoot REST API) │                      │
│                      │                      ├─ EAV 模型成熟 ───────┤
│                      │                      │   (动态合约字段体系)   │
│                      │                      │                      │
│                      │                      ├─ 消息驱动 EOD ───────┤
│                      │                      │   (ActiveMQ/Kafka)   │
│                      │                      │                      │
│                      │                      └─ odyssey 并行 ───────┤
│                      │                         (微服务尝试)         │
│                      │                                            │
└──────────────────────┴──────────────────────┴──────────────────────┘
                      eds-web-app 仍在生产主力中

关键数字

维度20162026
提交数736,275
分支数12,055
核心贡献者未知10+
包数量~5~30+
controller0(Struts2 Action)60+ REST Controllers
数据模型Contract (~50 fields)EdsSubContract (80+ fields, EAV)
消息系统ActiveMQ + Kafka
外部系统集成Optiver, UniLink, DAS, 中登

一个值得注意的模式

CICC 的后端有一个”从不删除,只加新层”的模式:

  • Struts2 Action 从未被删除,但 2021 年后新功能全写在 edsBoot
  • 旧的 Contract.java 从未被删除,但新功能用 EdsSubContractDBModel
  • v6.6 代码从未被删除,但 v7.0 加了一堆新特性
  • eds-web-app 从未被替换,但 odyssey 在尝试

这是一个务实的架构策略——确保生产稳定,同时允许渐进式现代化。