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

System Architecture Overview

OTC 衍生品 · 19 JUL 2026 · 14 min read · 2,796 words
· · ·

ODTS 25 — 系统架构总览:45+ 个项目组成的生态

本文件需与 ODTS-49(协议注册中心)、ODTS-43(通信架构)、ODTS-48(监控)等结合阅读。 这里是全景图,其他文件是细节。

为什么有 45+ 个项目?

投资银行的场外衍生品系统不是一个单一应用。它是一个由 45 个以上项目组成的生态系统,通过 Protobuf TCP、HTTP REST、文件和数据库进行通信。每个项目有特定的职责。这不是过度设计——每个服务对应一个独立的业务领域,比如结算、确认、保证金、抵押品管理、报告、会计等——它们各自有不同的发布节奏和团队。

万米高空视角 — 修正版

                   协议层(tradedesign — 37 个子系统的 Proto 契约仓库)
                      1,709 个 .proto 文件定义全部通信格式

                   ┌──────────┼──────────┐
                   │          │          │
         ┌─────────▼──┐  ┌───▼────┐  ┌──▼────────────┐
         │ 前台/交易台  │  │ 中台    │  │ IMS/GWMS      │
         │ (5 个前端)   │  │(hedging │  │ (内部管理系统)  │
         │ new-edsweb  │  │ -as,   │  │               │
         │ odts-option │  │ eds-web │  │  accessapp    │
         │ eds-rm-web  │  │ -app,   │  │  (网关桥接)    │
         │ new-quota   │  │ eds-    │  │               │
         │ fuxi-fe     │  │ price   │  │  gwms/protos  │
         └──────┬──────┘  │ -server)│  └──────┬────────┘
                │         └────┬────┘         │
                │              │              │
         ┌──────▼──────────────▼──────────────▼───────┐
         │              数据层                          │
         │  Oracle RAC (EDS schema)                    │
         │  + 共享文件服务器 (CSV/XML)                   │
         │  + 消息队列 (ActiveMQ / Kafka)               │
         └──────┬──────────────┬───────────────────────┘
                │              │
          ┌──────▼──────┐ ┌────▼──────────────────────┐
          │ 后台服务群    │ │ 外部集成                   │
          │ settlement  │ │                           │
          │ confirma-   │ │ IPMP      (资金划拨平台)    │
          │ tion        │ │ Cert App  (数字证书/签章)   │
          │ margin      │ │ Doc Agent (文档生成)        │
          │ collateral  │ │ JUMS      (通知服务)        │
          │ accounting  │ │ HCP       (对象存储)        │
          │ regulatory  │ │ Dipper    (数据同步)        │
          │ report      │ │ SacAS     (监管报送)        │
          │             │ │                           │
          │             │ │ custodian-bank             │
          │             │ │ clearing-house             │
          │             │ │ DTCC/SFC                   │
          │             │ │ CATS (commodity gw)        │
          │             │ │ oss-image (文件服务)        │
          └─────────────┘ └────────────────────────────┘

两个世界

世界 1:hedging-as (JFinal + Protobuf + 直接 SQL)。这是 2016-2017 年构建的原始系统。优点:快(无 ORM 开销,Protobuf 序列化高效)、完整(定价、风险、保证金都在一处)、经过实战检验(2017 年起在生产环境运行)。缺点:JFinal 是小众框架(难招人)、到处是原始 SQL 字符串(无编译时检查)、紧耦合(风险计算直接调用 DAO)、无测试基础设施(大多数类没有单元测试)、单体架构(全部在一个 WAR 文件中)。

世界 2:eds-web-app (Spring Boot + MyBatis + REST)。这是正在构建的新系统,旨在取代 hedging-as。优点:Spring Boot(标准框架,容易招人)、REST API(服务可独立部署)、关注点分离更好、测试支持更好、有依赖注入。缺点:功能不完整(某些功能只在 hedging-as 中有)、过渡期需要维护两个系统、定价引擎的数值结果不同、迁移缓慢(5+ 年仍未完成)。

外部系统生态 / External System Landscape

ODTS 对接了 8 个以上的外部系统,按业务目的分为四类:

类别系统协议数据方向关键业务
资金结算IPMP 资金划拨平台XML over HTTP→ 转账指令, ← 结果轮询银行间资金划拨、CNAPS 转账
文档与法律Cert App 数字证书HTTP REST↔ 签名请求/响应合同签署、数字证书管理
Doc Agent 文档生成HTTP REST请求→ 生成←ISDA/NAFMII/CSA 文档生成
通知与存储JUMS 通知服务HTTP REST→ 模板化通知邮件/SMS 通知(Margin Call 等)
HCP 对象存储HTTP REST→ 文件上传合同 PDF、报表、附件的持久化
监管与数据SacAS 监管报送Protobuf over HTTP→ 交易报送向 SAC/ISDA/NAFMII 报送数据
Dipper / PulsarProtobuf→ 数据同步从生产同步到定稿数据库
SFTP 文件交换SFTP↔ CSV/XML 文件与结算行、托管行交换文件

这些系统通过不同的集成模式与 ODTS 对接:

  • 同步请求-响应:Cert App, Doc Agent, JUMS, HCP — HTTP REST,超时重试
  • 异步提交-轮询:IPMP — 提交转账后轮询获取最终状态
  • 文件推送:Dipper/Pulsar — Protobuf → CSV 转换后写入目标数据库
  • 文件交换:SFTP — 定时上传/下载 CSV 文件

详见 ODTS-52 的可视化流程图。

通信方式

hedging-as 与 eds-web-app 之间: 两者读写同一个 Oracle 数据库(EDS schema)。eds-web-app 簿记交易 → 写入数据库 → hedging-as 从数据库读取来定价。没有实时通知——轮询 (polling) 驱动。

前端与后端之间: new-edsweb → hedging-as 使用 HTTP REST (JSON);new-edsweb → eds-web-app 使用 HTTP REST (JSON);odts-option-web → hedging-as 使用 HTTP REST (JSON);edsWeb (JSP) → hedging-as 使用同 JVM 内的直接 Java 调用。

服务之间: 大多数服务使用基于文件的集成——eds-web-app 将 CSV/XML 文件写入共享目录,settlement-service 轮询读取这些文件。没有 API 调用,没有消息队列。部分服务使用数据库级集成——服务 A 写入表 X,服务 B 从表 X 读取。同样没有实时通知。

这是一个显著的架构负债 (architectural debt)。 基于文件的集成意味着:没有重试逻辑(文件损坏怎么办?)、没有实时处理(文件每 N 分钟轮询一次)、没有监控(文件到了吗?正在处理吗?)、手工恢复(运维必须重新触发失败的处理)。

通信协议层

系统中有一个常常被忽视但至关重要的组件——协议注册中心(tradedesign)。详见 ODTS-49

这是一个”看不见”的系统:没有进程、没有部署、没有告警。但它定义了 37 个子系统之间的全部通信契约。

tradedesign 包含:

  • 37 个 PbMessageHead.* 文件(每个子系统一个)
  • 381 行的全局 MSG 枚举(消息类型定义)
  • 1,709 个 .proto 文件分布在 50+ 子目录中
  • 15,014 次提交(2015-2022),在 2015 年启动,比主后端 eds-web-app(2016-12)早约 15 个月

所有 Java 项目在编译时依赖 tradedesign 的 JAR 包。当 eds-web-app 需要发送一笔交易给 hedging-as 时,请求和响应的格式由 tradedesign 定义。这个”接口先行”的模式在 2015 年前后是领先的。

2022 年之后,新项目(odyssey report-processing、new-edsweb REST API)不再使用 Protobuf,转而使用 REST + JSON。Tradedesign 的 Git 历史停在了 2022 年初。

45+ 个项目实际分类

类别数量项目
协议注册中心1tradedesign(15,014 commits, 1,709 proto)
后端应用4eds-web-app, hedging-as, eds-price-server, sac-report
前端应用7new-edsweb, odts-option-web, edsWeb, eds-rm-web, new-quota, fuxi-fe-demo, odts-linear-web (odyssey)
微服务 (odyssey)6report-processing-service, internal-gateway, cash-manager, quotation-service, ciccchecker, ofareg
网关/桥接2accessapp-new (IMS), gateway (protos)
共享库2eds-utility (4,444 commits), eds-utilitysrc
基础设施5unionLogin (SSO), eva-health, eva-master, oss-image, ofareg-path
外部系统8+IPMP (资金划拨), Cert App (数字证书/签章), Doc Agent (文档生成), JUMS (通知服务), HCP (对象存储), Dipper (数据同步), SacAS (监管报送), SFTP (文件交换)
其他系统2CATS (大宗商品), bootstrapvalidator (校验库)
构建/工具3protobuf-gradle-plugin, diagrams.net, refactor
外部依赖3ice, jzmq, lox (ZeroMQ 相关)

共计:40+ 个内部项目 + 8+ 个外部集成系统

部署架构

生产环境: 2 个 hedging-as 实例(负载均衡)、2 个 eds-web-app 实例(负载均衡)、1 个 Oracle RAC(2 节点)、1 个文件服务器(共享目录用于基于文件的集成)。

UAT 环境: 1 个 hedging-as 实例、1 个 eds-web-app 实例、1 个 Oracle 实例(独立,每周从生产恢复)。

开发环境: 每个开发者有自己的本地环境(Oracle XE、本地文件系统等)。

共享数据库的惨痛教训

2019 年,eds-web-app 的一个开发者改了一个通用字段类型:EDS_CONTRACT.remarkVARCHAR2(500) 改为 VARCHAR2(1000)。这个字段在 eds-web-app 的界面中被用于显示交易备注。改完后正常运行。

但一周后 hedging-as 的 EOD 批处理开始随机失败。根因出人意料:

EDS_CONTRACT 表是 hedging-as EOD 批处理的核心输入表
→ remark 字段长度变更触发了 hedging-as 中一个旧 SQL 的隐式转换
→ 该 SQL 用 CHAR 函数截断 remark 到 500 字节
→ 新插入的 remark 超过 500 字节导致 SQL 异常
→ EOD 批处理中断
→ 当天的保证金催缴延迟了 4 小时

后果: 一个简单的字段长度变更导致整个 EOD 流程中断。修复需要回滚字段长度 + 同步两个应用的 SQL 语句。根本问题不是”改字段的人没通知”,而是”两个应用共享同一个表,但一个不知道另一个的 SQL 逻辑”。 如果有独立的数据库和服务边界,这个风险不存在。

45+ 项目的依赖链如何断裂

另一个常见问题:tradedesign 的 proto 文件变更后,所有依赖项目需要重新编译。但开发者不是每次都能同步更新所有项目。

2021 年某事件:
  团队 A 修改了 tradedesign 中某个消息类型的字段定义
  → 重新编译并发布了 tradedesign JAR
  → 团队 B(维护 eds-web-app)没有立即更新依赖
  → eds-web-app 继续使用旧版本 tradedesign JAR
  
  结果:
    hedging-as(已更新)发送的新格式消息
    → eds-web-app(未更新)反序列化失败
    → 交易同步中断 2 天(直到被发现和修复)

架构教训: 45+ 项目通过共享 proto 契约耦合在一起。tradedesign 的设计意图是”接口先行”,但在没有版本兼容性检查(proto 的 forward/backward compatibility 没有被强制执行)的情况下,接口变更变成了一把双刃剑——它提高了效率,但也创造了隐式依赖链。

部署流程演变

2022 年之前:开发者提交代码到 SVN → Jenkins 构建 → 手动 SCP 复制 WAR 到生产服务器 → 停 Tomcat、替换 WAR、启动 Tomcat → 手动运行迁移脚本 (SQL*Plus) → 手动冒烟测试。问题:手动步骤导致人为错误、无回滚机制、部署期间有停机、迁移脚本有时顺序错乱。

2022 年之后:开发者提交到 Git → GitHub Actions 构建和测试 → 制品推送到 Nexus → Ansible playbook 部署到生产 → 失败时自动回滚 → 自动冒烟测试。仍在进展中的:金丝雀部署 (canary deployment) 未实现、数据库迁移自动化仍使用旧的自定义脚本、功能开关 (feature flags) 未实现。

架构最大的问题

问题 1:所有服务共享同一个数据库。 每个服务读写同一个 Oracle schema。这意味着服务 A 的 bug 可能损坏服务 B 的数据,数据库 schema 变更需要协调所有服务,没有服务隔离(一个糟糕的查询可以让所有人的数据库宕机),无法独立扩展服务(单一数据库瓶颈)。

问题 2:两个引擎,两个真相。 旧的和新的定价引擎给出不同的结果。前台的 P&L = 1,234,567,风险的 P&L = 1,233,890,差异 677 元(0.05%)。这听起来很小,但每天累积,且每次询价 (RFQ) 时交易员和风控都不知道该信哪个数字。

问题 3:基于文件的集成。 结算、确认和报告仍然使用基于文件的集成。事务系统写 CSV 文件,结算服务每 5 分钟轮询一次。没有错误处理(CSV 格式错误怎么办?)、没有重试机制(结算服务宕机怎么办?)、没有监控(文件按时到达了吗?)。

问题 4:没有服务网格 (service mesh)。 服务通过硬编码的 IP 地址和端口发现彼此。配置在属性文件中,而不是配置服务器。负载均衡在生产中由硬件负载均衡器完成,但在 UAT 中没有。

理想架构应该是什么样子

                  API Gateway
       (认证、限流、路由)

     ┌────────┼────────┐
  ┌──▼──┐ ┌──▼───┐ ┌──▼───┐
  │交易 │ │定价   │ │保证金 │
  │服务  │◄┤服务  ►│◄┤服务  │
  └──┬──┘ └──┬───┘ └──┬───┘
     │       │        │
  ┌──▼───────▼────────▼────┐
  │    事件总线 (Kafka)     │
  └──┬───────┬────────┬────┘
     │       │        │
  ┌──▼──┐ ┌──▼───┐ ┌──▼───┐
  │结算  │ │市场   │ │报告   │
  │服务  │ │数据服务│ │服务  │
  └─────┘ └──────┘ └──────┘

       ┌──────▼──────┐
       │ PostgreSQL  │
       │ (服务 A)    │
       └─────────────┘
       ┌──────▼──────┐
       │ PostgreSQL  │
       │ (服务 B)    │
       └─────────────┘

理想架构的收益:每个服务拥有自己的数据库(无共享 schema)、服务通过事件总线(而非文件轮询)通信、单一定价服务(一个真相源)、实时通知(保证金催缴、确认)、独立扩展(只扩展需要的服务)、正确的错误处理(死信队列、重试策略)。

为什么没做? 从当前架构迁移到这个理想状态的成本和风险是巨大的。约 35 个项目需要重构,而业务方不愿意资助一个没有可见功能改进的多年迁移项目。

关键文件

组件路径备注
主后端~/odts1/eds-web-app/Spring Boot, 36,275 commits
定价/风控引擎~/odts1/hedging-as/JFinal + Protobuf
协议注册中心~/odts1/tradedesign/15,014 commits, 1,709 proto 文件
独立定价服务~/odts1/eds-price-server/9,325 commits
监管报送~/odts1/sac-report/1,308 commits, SAC/ISDA/NAFMII
共享库~/odts1/eds-utility/4,444 commits, 82 位作者
管理前端~/odts1/new-edsweb/Vue 2 + Element UI
期权交易 UI~/odts1/odts-option-web/Vue 2 + KoiUI
风控前端~/odts1/eds-rm-web/147 commits
额度管理~/odts1/new-quota/459 commits
报表微服务~/odyssey/report-processing-service/1,211 commits
新一代前端~/odyssey/odts-linear-web/2,707 commits
数据库迁移~/odts1/eds-web-app/db/EDS/Flyway 脚本
大宗商品~/odts1/CATS/Struts2 + Bloomberg
SSO 认证~/odts1/unionLogin/统一登录
IMS 网关~/odts1/accessapp-new/Netty + Protobuf 桥接
业务监控~/odts1/eva-health/Python, Prometheus
文件服务~/odts1/oss-image/Node.js
架构图~/odts1/diagrams.net/draw.io 源文件

ODTS 架构是一个围绕共享 Oracle 数据库构建的 45+ 项目生态系统(含 8+ 外部集成系统),由 tradedesign 的 1,709 个 Proto 文件定义全部通信契约,有两个给出略微不同答案的定价引擎。它靠基于文件的集成和自定义 shell 脚本维持在一起,而向现代微服务架构的多年迁移进展缓慢——只有 report-processing-service 真正存活了下来。