我把 2000 行下单代码,重构成了一套交易前端架构

在很多金融类产品里,下单模块几乎永远是最复杂的前端代码之一

一个完整的交易流程通常包括:

  • 普通单 / 条件单
  • 创建订单 / 改单
  • 价格校验
  • 资产校验
  • 风险提示
  • 协议签署
  • 二次确认
  • 输入交易密码
  • 提交订单
  • 结果处理

随着需求不断叠加,很多项目最后都会演变成这样:

ts 复制代码
async function onOrder() {

  if (!priceValid()) return toast("价格不合法")

  if (!assetEnough()) return toast("资产不足")

  if (needRiskConfirm()) {
    await openRiskDialog()
  }

  if (!signedAgreement()) {
    await openAgreementDialog()
  }

  if (!await openConfirmDialog()) return

  const password = await openPassword()

  const res = await submitOrderApi()

  handleResult(res)

}

组件文件很容易膨胀到:

ts 复制代码
Order.vue
≈ 2000 行

随着业务增长,会出现三个典型问题:

1️⃣ 校验逻辑越来越复杂

2️⃣ 创建单 / 改单到处是 if (isEdit)

3️⃣ 提交流程越来越长

后来我对这块代码做了一次系统性的重构,最终形成了一套 稳定可扩展的交易前端架构

ts 复制代码
OrderSession
+ OrderFlowStateMachine
+ RuleEngine
+ SubmitPipeline
+ Ports / Adapters

下面把这套架构完整拆解。


一、交易系统为什么容易失控

交易模块有一个典型特点:

流程很长 + 规则很多。

完整流程通常是:

ts 复制代码
填写订单
 ↓
前置校验
 ↓
风险确认
 ↓
协议签署
 ↓
最终确认
 ↓
输入密码
 ↓
提交订单
 ↓
结果处理

如果所有逻辑都写在组件里,代码结构会变成:

ts 复制代码
UI + 业务规则 + 流程控制 + API调用

全部混在一起。

解决办法只有一个:

拆层。


二、交易前端架构分层

重构后我们把交易模块拆成五层:

ts 复制代码
Presentation
↓
Application
↓
Domain
↓
Ports
↓
Adapters

整体结构如下:

ts 复制代码
UI (Vue / React)
      ↓
useOrderFlow
      ↓
OrderFlowMachine
      ↓
SubmitPipeline
      ↓
RuleEngine
      ↓
OrderSession
      ↓
Ports
      ↓
Adapters(API / Store)

每一层职责都非常清晰。


三、OrderSession:统一创建单和改单

交易系统中有一个非常经典的问题:

ts 复制代码
创建订单
改单

很多项目的代码会变成:

ts 复制代码
if (isEdit) {
  // 修改逻辑
} else {
  // 创建逻辑
}

时间久了,整个项目会充满:

ts 复制代码
if (isEdit)

解决方案是引入一个领域模型:

OrderSession

ts 复制代码
interface OrderSession {

  type: "CREATE" | "EDIT"

  entrustId?: string

  originOrder?: OrderDTO

}

它表示:

用户的一次下单会话。

例如:

ts 复制代码
CREATE Session → 创建订单
EDIT Session → 修改订单

所有逻辑只依赖 session.type,而不是 isEdit


四、RuleEngine:把校验逻辑规则化

原来的校验代码通常是:

ts 复制代码
if (!priceValid()) return toast()

if (!assetEnough()) return toast()

if (priceDiffTooLarge()) openRiskDialog()

随着需求增加,preVerify 会变成一大坨 if

解决方案是 规则引擎化

定义统一结果结构:

ts 复制代码
interface ValidationResult {

  code: string

  severity: "block" | "warn"

  message: string
}

规则写成纯函数:

ts 复制代码
function priceRule(ctx): ValidationResult | null

function assetRule(ctx): ValidationResult | null

统一执行:

ts 复制代码
function runRules(ctx, rules) {

  return rules
    .map(rule => rule(ctx))
    .filter(Boolean)

}

新增规则只需要:

ts 复制代码
新增一个函数

而不需要修改原有代码。


五、SubmitPipeline:拆解提交流程

原来的提交流程通常是:

ts 复制代码
verify
↓
risk confirm
↓
confirm
↓
password
↓
submit

我们把它拆成 Pipeline

ts 复制代码
ValidateStep
↓
RiskConfirmStep
↓
AgreementStep
↓
FinalConfirmStep
↓
PasswordStep
↓
SubmitStep
↓
ResultStep

代码实现:

ts 复制代码
async function runPipeline(ctx, steps) {

  for (const step of steps) {

    await step(ctx)

  }

}

好处是:

流程步骤可以自由插拔。

例如新增:

ts 复制代码
冷静期确认

只需要增加一个 step。


六、OrderFlowStateMachine:状态机驱动流程

交易流程本质上是一个 状态机

定义状态:

ts 复制代码
FORM
VALIDATING
RISK_CONFIRM
PASSWORD
SUBMITTING
SUCCESS
FAILED

状态转移:

ts 复制代码
FORM
 ↓
VALIDATING
 ↓
RISK_CONFIRM
 ↓
PASSWORD
 ↓
SUBMITTING
 ↓
SUCCESS

简单实现:

ts 复制代码
class OrderFlowMachine {

  state = "FORM"

  transition(next) {
    this.state = next
  }

}

UI 只根据状态渲染:

ts 复制代码
FORM → 表单
RISK_CONFIRM → 风险弹窗
PASSWORD → 密码输入

逻辑会变得非常清晰。


七、Ports / Adapters:隔离外部系统

交易模块通常依赖很多系统:

ts 复制代码
行情
资产
交易接口
协议系统

如果直接依赖 Store 或 API,会导致代码强耦合。

解决方案是 Ports + Adapters

定义接口:

ts 复制代码
interface OrderPort {

  submitOrder(command)

}

Adapter 实现:

ts 复制代码
export const orderAdapter = {

  submitOrder(cmd) {

    return api.trade.submit(cmd)

  }

}

Domain 只依赖:

ts 复制代码
Port

而不是具体 API。


八、完整目录结构

重构后的目录结构大致如下:

ts 复制代码
order/

domain/

  OrderSession.ts
  OrderContext.ts

  validation/
    priceRule.ts
    assetRule.ts
    modifyRule.ts

application/

  OrderFlowMachine.ts
  submitPipeline.ts

ports/

  OrderPort.ts
  MarketPort.ts

adapters/

  orderAdapter.ts
  marketAdapter.ts

composables/

  useOrderFlow.ts

结构非常清晰。


九、改造后的效果

重构前:

ts 复制代码
Order.vue
≈ 2000 行

重构后:

ts 复制代码
UI组件
≈ 300 行

其余逻辑全部放在:

ts 复制代码
Domain
Application

新增规则或流程时:

ts 复制代码
新增 Rule
新增 Step

而不是修改原有代码。


十、总结

复杂交易系统的稳定架构通常由五个核心模块组成:

ts 复制代码
OrderSession
+
StateMachine
+
RuleEngine
+
SubmitPipeline
+
Ports / Adapters

简单理解就是:

ts 复制代码
Session 管数据
StateMachine 管流程
RuleEngine 管规则
Pipeline 管步骤
Ports 管依赖

当这些模块拆清楚之后,下单系统就会变成:

稳定、清晰、可扩展。

这也是很多大型金融系统前端常见的架构模式。

相关推荐
im_AMBER4 小时前
今日开发反思:编辑器大纲跳转与数据持久化实践
前端·架构
Qinana4 小时前
从数据包旅程到首屏渲染:深入理解 TCP/IP 如何决定你的 Web 性能
前端·tcp/ip·浏览器
橙子的AI笔记4 小时前
旧版 LangChain 已死:新版竟以LangGraph为底座封装
前端·langchain
Wect4 小时前
LeetCode 17. 电话号码的字母组合:回溯算法入门实战
前端·算法·typescript
SuperEugene4 小时前
Vue3 中后台实战:VXE-Table 从基础表格到复杂业务表格全攻略 | Vue生态精选篇
前端·javascript·vue.js
SuperEugene4 小时前
Vue3 中后台实战:Element + VXE Table 搜索表格分页完整方案 | Vue生态精选篇
前端·javascript·vue.js
欧哥讼4 小时前
当我问AI如何熟练掌握表单验证时
前端
德鲁叔叔4 小时前
vite前端项目运行时切换代理
前端
亿元程序员5 小时前
老板说最近这款游戏很火让我抄,可是我连玩都玩不明白...
前端