1. 金融服务的核心领域拆解与需求定位
1.1 金融服务到底覆盖了哪些业务场景
提到金融服务,很多人第一反应就是银行存取款、股票买卖,但实际上这个领域的边界远比想象中宽。从最基础的支付结算、存贷汇兑,到稍复杂一些的保险精算、资产托管,再到近年来越来越普及的智能投顾、消费信贷、供应链金融,都属于金融服务的范畴。我过去几年参与过几个金融类项目的架构设计和落地实施,最大的感受就是:这个领域对准确性、一致性、安全性的要求,远高于绝大多数互联网业务。
金融服务本质上是在处理“钱”和“信任”这两件事。钱要求账目分毫不差,信任要求系统稳定可靠、数据不可篡改。所以做金融类项目,技术选型和架构设计的第一优先级永远不是“快”,而是“稳”和“准”。这一点如果一开始没想清楚,后面返工的成本会非常高。
1.2 为什么金融服务项目需要独立的技术架构
普通电商系统丢一个订单可能只是用户体验问题,金融服务系统丢一笔交易就是资金事故。这个差异决定了金融服务不能简单套用通用的互联网架构方案。我见过一些团队拿电商那套高并发方案直接往金融场景上搬,结果在账务一致性上栽了跟头。
金融服务的核心诉求可以归纳为三个词:一致性、可追溯、可审计。一致性要求任何一笔资金变动都必须保证借贷平衡,不能出现“钱凭空消失”的情况;可追溯要求每一笔操作都有完整的链路记录,出了问题能定位到具体环节;可审计要求系统能对外提供符合规范的账务凭证和流水记录。这三点是金融服务区别于其他领域的技术分水岭。
1.3 适合哪些人参考这套思路
这篇文章主要面向三类读者:一是刚接触金融类项目开发的技术人员,需要快速建立对金融服务技术体系的整体认知;二是有一定经验但想深入了解金融场景特殊性的工程师,比如从电商、社交转过来的开发者;三是需要做技术选型和方案评审的架构师或技术负责人,需要一份可落地的参考框架。
不管你用的是Java、Go还是Python,金融服务的核心逻辑是相通的。下面我会从架构设计、核心模块实现、数据一致性保障、常见问题排查几个维度展开,尽量把每个关键决策背后的“为什么”讲清楚。
2. 金融服务系统的整体架构设计与选型考量
2.1 分层架构的划分逻辑
金融服务系统我通常建议采用四层架构:接入层、业务服务层、核心账务层、数据存储层。这个划分不是拍脑袋定的,而是根据职责边界和变更频率来切的。
接入层负责协议转换、鉴权、限流、路由,这一层变化最频繁,因为前端形态和接入方式经常调整。业务服务层承载具体的业务逻辑,比如转账、还款、计息,这一层的变化频率中等,跟业务需求走。核心账务层是整个系统的心脏,负责记账、对账、余额管理,这一层必须极其稳定,变更要走严格的评审流程。数据存储层负责持久化,包括关系型数据库、缓存、消息队列等。
这样分层的好处是:变更被隔离在各自的层内,核心账务层不会因为前端改了个交互方式就跟着动。我经历过一个项目,早期没有严格分层,结果改一个还款页面的展示逻辑,差点影响到记账逻辑,后来花了很大力气做重构才把边界理清楚。
2.2 核心账务层的设计原则
核心账务层我坚持几个原则。第一,只做记账,不做业务判断。业务规则比如“满100减20”这种应该在业务服务层处理完,到了账务层就是纯粹的借贷记账。第二,所有账务操作必须幂等。同一个请求重复提交多次,结果必须一致,这需要引入全局唯一的业务流水号来做去重。第三,账务记录只增不改。任何调整都通过反向记账来实现,而不是直接修改历史记录,这样才能保证审计链路完整。
举个例子,用户A向用户B转账100元。账务层的操作是:借A账户100元,贷B账户100元,同时生成一条转账流水。如果后续发现这笔转账有误需要冲正,不是去改原来的记录,而是再记一笔反向分录:借B账户100元,贷A账户100元。这样账本上永远能看到完整的资金轨迹。
2.3 技术栈选型与参数考量
技术栈方面,关系型数据库我首选支持强一致性的方案,比如MySQL配合InnoDB引擎,事务隔离级别用可重复读。这里有个细节:金融场景下不建议用读已提交,因为可能出现不可重复读的问题,影响对账逻辑。缓存层用Redis做热点账户的余额缓存,但必须注意缓存和数据库的一致性,我通常采用“先更新数据库,再删除缓存”的策略,并且设置合理的过期时间兜底。
消息队列在金融服务里主要用来做异步解耦和削峰填谷。比如批量代发工资的场景,几万笔转账请求同时进来,直接打数据库肯定扛不住,这时候用消息队列把请求排队,后端消费者按自己的能力去拉取处理。选型上Kafka和RocketMQ都可以,关键是要保证消息不丢、不重。我一般会开启消息持久化,消费者端做幂等处理,双保险。
| 组件 | 选型建议 | 关键参数 | 选择理由 |
|---|---|---|---|
| 关系型数据库 | MySQL 8.0 + InnoDB | 隔离级别:可重复读;事务超时:30s | 强一致性支持好,生态成熟 |
| 缓存 | Redis 7.x | 最大内存:根据热点数据量定;淘汰策略:volatile-lru | 热点账户余额查询性能高 |
| 消息队列 | RocketMQ 5.x | 刷盘方式:同步刷盘;重试次数:3次 | 金融级消息可靠性 |
| 分布式协调 | ZooKeeper / Nacos | 会话超时:30s | 分布式锁和配置管理 |
2.4 高可用与容灾设计
金融服务的可用性目标通常是99.99%以上,这意味着全年停机时间不能超过52分钟。要达到这个目标,单点故障是绝对不能有的。数据库做主从架构,主库挂了自动切换到从库;应用服务多实例部署,前面挂负载均衡;缓存做集群模式,节点故障自动摘除。
容灾方面,同城双活是基本要求,条件允许的话做异地多活。同城双活的核心是数据同步,数据库层面用半同步复制保证至少一个从库收到日志,应用层面做流量分发。异地多活更复杂,涉及到数据冲突解决,一般金融核心账务不建议做异地多活,而是采用异地灾备的方式,平时不承载流量,灾难时切换。
3. 核心模块的实操要点与关键细节
3.1 账户体系的设计与实现
账户体系是金融服务的基石。我通常把账户分为两类:客户账户和内部账户。客户账户就是用户实际持有的账户,内部账户是系统内部用于记账的过渡账户,比如“待清算资金”、“手续费收入”等。每一笔资金变动都要保证客户账户和内部账户的借贷平衡。
账户表的核心字段包括:账户ID、客户ID、账户类型、币种、余额、可用余额、冻结余额、状态、版本号。这里重点说两个字段。可用余额和冻结余额要分开,比如用户下单时冻结一部分资金,这时候可用余额减少但冻结余额增加,总额不变。版本号用于乐观锁,更新余额时带上版本号条件,防止并发更新导致数据覆盖。
-- 账户表核心结构示例 CREATE TABLE account ( account_id BIGINT PRIMARY KEY, customer_id BIGINT NOT NULL, account_type VARCHAR(20) NOT NULL, currency VARCHAR(3) NOT NULL DEFAULT 'CNY', balance DECIMAL(20,4) NOT NULL DEFAULT 0, available_balance DECIMAL(20,4) NOT NULL DEFAULT 0, frozen_balance DECIMAL(20,4) NOT NULL DEFAULT 0, status VARCHAR(10) NOT NULL DEFAULT 'ACTIVE', version INT NOT NULL DEFAULT 0, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_customer (customer_id), UNIQUE KEY uk_account (account_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;金额字段用DECIMAL而不是FLOAT或DOUBLE,这是金融系统的铁律。浮点数有精度问题,0.1+0.2不等于0.3这种事在金融场景里是致命的。DECIMAL(20,4)表示总共20位,其中4位小数,对于大多数人民币场景够用了,如果是外汇或者加密货币,需要根据实际情况调整精度。
3.2 交易流程的完整实现
一笔完整的交易流程通常包括:请求接收、参数校验、风控检查、账务处理、结果通知五个阶段。每个阶段都有需要注意的细节。
请求接收阶段要做的事情包括:解析请求参数、验证签名、检查重复请求。重复请求检查靠的是业务流水号,每个请求必须带一个全局唯一的流水号,服务端用Redis记录已处理的流水号,设置合理的过期时间。参数校验阶段要检查金额是否合法、账户是否存在、状态是否正常。风控检查阶段根据业务规则判断这笔交易是否允许,比如单笔限额、日累计限额、黑名单等。
账务处理是整个流程的核心。我通常把它放在一个数据库事务里,包含以下步骤:锁定转出账户和转入账户(用SELECT FOR UPDATE或者乐观锁)、检查余额是否充足、更新转出账户余额、更新转入账户余额、插入流水记录。这里有个关键点:锁账户的顺序要一致,比如都按账户ID从小到大加锁,否则可能出现死锁。
// 账务处理核心逻辑伪代码 @Transactional(rollbackFor = Exception.class) public TransferResult transfer(TransferRequest request) { // 1. 幂等检查 if (idempotentService.isProcessed(request.getBizNo())) { return TransferResult.duplicate(); } // 2. 按账户ID排序,避免死锁 Long fromId = request.getFromAccountId(); Long toId = request.getToAccountId(); Long firstId = Math.min(fromId, toId); Long secondId = Math.max(fromId, toId); // 3. 锁定账户 Account first = accountMapper.selectForUpdate(firstId); Account second = accountMapper.selectForUpdate(secondId); // 4. 余额检查 Account from = fromId.equals(firstId) ? first : second; if (from.getAvailableBalance().compareTo(request.getAmount()) < 0) { throw new InsufficientBalanceException(); } // 5. 更新余额 accountMapper.decreaseBalance(fromId, request.getAmount()); accountMapper.increaseBalance(toId, request.getAmount()); // 6. 记录流水 flowMapper.insert(buildFlow(request)); // 7. 标记幂等 idempotentService.markProcessed(request.getBizNo()); return TransferResult.success(); }结果通知阶段根据业务需要选择同步返回还是异步通知。同步返回就是接口直接返回处理结果,异步通知是通过消息队列或者回调URL告知业务方。金融场景下我建议两者都做,同步返回给用户即时反馈,异步通知保证业务方最终能收到结果。
3.3 对账系统的搭建方法
对账是金融服务里最容易被忽视但最重要的环节。系统跑得再稳,没有对账机制,出了问题都不知道。对账的核心逻辑是:拿我方账务记录和对方账务记录做比对,找出差异并处理。
对账分为两种:内部对账和外部对账。内部对账是系统内部各模块之间的核对,比如账务流水和账户余额的核对。外部对账是和第三方渠道的核对,比如和银行、支付机构的对账。内部对账通常T+0做,外部对账一般是T+1。
对账系统的实现步骤:第一步,获取双方的对账文件,通常是按日期切分的流水文件。第二步,解析文件,统一格式。第三步,按业务流水号做匹配,找出我方有对方无、对方有我方无、双方都有但金额不一致的记录。第四步,生成差异报告,人工或者自动处理。第五步,记录对账结果,供后续查询和审计。
# 对账核心逻辑示例 def reconcile(our_records, their_records): our_map = {r.biz_no: r for r in our_records} their_map = {r.biz_no: r for r in their_records} all_keys = set(our_map.keys()) | set(their_map.keys()) differences = [] for key in all_keys: our = our_map.get(key) their = their_map.get(key) if our is None: differences.append(Difference(key, 'MISSING_OURS', their)) elif their is None: differences.append(Difference(key, 'MISSING_THEIRS', our)) elif our.amount != their.amount: differences.append(Difference(key, 'AMOUNT_MISMATCH', our, their)) return differences对账的时间窗口选择也有讲究。太短了可能漏掉跨日的交易,太长了差异处理不及时。我一般建议外部对账用T+1,内部对账用T+0小时级。对账文件要保留至少一年,监管检查的时候要用。
3.4 风控模块的接入方式
风控模块在金融服务里承担着“守门人”的角色。它不直接处理资金,但决定了哪些交易可以放行。风控的接入方式通常有两种:同步调用和异步分析。同步调用是在交易主流程里实时调用风控接口,根据返回结果决定是否继续。异步分析是把交易数据发给风控系统,风控系统后台分析,发现异常再事后处理。
同步风控的响应时间要求很高,通常不能超过100毫秒,否则会影响用户体验。所以同步风控一般只做简单的规则判断,比如黑名单、限额、频率检查。复杂的模型分析放在异步。风控规则要支持动态配置,不能硬编码在代码里,否则每次调整规则都要发版。
我踩过的一个坑是:风控规则配置错误导致大量正常交易被拦截。后来我们加了规则灰度机制,新规则先在小流量上验证,确认没问题再全量。另外风控的拒绝要有明确的错误码和提示,方便用户理解和客服处理。
4. 数据一致性保障与并发处理实战
4.1 分布式事务在金融场景的取舍
金融服务里跨库跨服务的操作很常见,比如转账涉及转出方和转入方可能在不同的数据库实例。这时候就面临分布式事务的问题。强一致性的方案有两阶段提交(2PC)、TCC(Try-Confirm-Cancel),最终一致性的方案有本地消息表、事务消息。
我的经验是:核心账务用强一致性,非核心用最终一致性。核心账务比如转账,必须保证要么都成功要么都失败,不能出现转出成功转入失败的情况。这时候用TCC比较合适,Try阶段冻结双方资金,Confirm阶段实际扣减,Cancel阶段解冻。非核心的比如积分发放、消息通知,可以用本地消息表+定时补偿的方式,允许短暂的不一致。
TCC的难点在于Cancel阶段的幂等和空回滚处理。空回滚是指Try阶段没执行成功,但Cancel被调用了。这时候Cancel要能识别出来并直接返回成功。幂等是指Cancel可能被重复调用,每次结果要一致。这些都需要在业务代码里仔细处理。
4.2 高并发下的账户余额更新策略
热点账户是高并发场景下的典型难题。比如电商大促时,平台手续费账户可能每秒有几千笔入账,直接更新数据库肯定扛不住。我通常采用缓冲记账的策略:先把流水写到消息队列或者内存队列,后端消费者批量合并后更新数据库。
具体做法是:每笔交易产生一条记账流水,流水先入队。消费者每隔一定时间或者积累到一定数量后,从队列拉取一批流水,按账户汇总金额,然后一次性更新账户余额。这样数据库的更新次数从每笔一次降低到每批一次,性能提升非常明显。
但缓冲记账有个问题:账户余额不是实时准确的。用户查询余额时可能看到的是旧值。解决方案是:查询时把缓冲中的金额也算进去。比如账户表余额是1000,缓冲队列里还有50的入账没处理,查询时返回1050。这需要在查询逻辑里做特殊处理。
// 缓冲记账的余额查询逻辑 public BigDecimal getRealTimeBalance(Long accountId) { // 数据库中的余额 BigDecimal dbBalance = accountMapper.getBalance(accountId); // 缓冲队列中待处理的金额 BigDecimal pendingAmount = bufferQueue.getPendingAmount(accountId); return dbBalance.add(pendingAmount); }4.3 幂等设计的多种实现方式
幂等是金融服务的基本要求。同一个请求不管调用多少次,结果必须一致。实现幂等的方式有好几种,我根据场景选择。
数据库唯一索引是最简单的方式。比如流水表的业务流水号加唯一索引,重复插入会报错,捕获异常后返回已有结果。这种方式适合插入类的操作。Redis记录适合需要快速判断的场景,请求进来先查Redis有没有处理过,没有的话处理完写Redis。要注意设置合理的过期时间,太短了可能漏判,太长了占内存。状态机适合有明确状态流转的业务,比如订单从“待支付”到“已支付”,只有“待支付”状态才能执行支付操作,重复请求会因为状态不对被拒绝。
我一般会组合使用:Redis做第一道快速过滤,数据库唯一索引做最终兜底。这样既保证了性能,又保证了可靠性。
4.4 数据一致性校验的定时任务
不管系统设计得多好,数据不一致的风险始终存在。所以需要定时任务做一致性校验,及时发现和修复问题。我通常做三层校验:流水与余额校验、流水与流水校验、总分校验。
流水与余额校验是检查账户的每一笔流水累加起来是否等于当前余额。流水与流水校验是检查关联流水是否匹配,比如转账的转出流水和转入流水金额是否一致。总分校验是检查所有子账户余额之和是否等于总账户余额。这些校验任务通常在凌晨低峰期执行,发现差异生成告警,人工介入处理。
校验任务本身也要注意性能。全量校验数据量大,可以分片执行,每次只校验一部分账户。校验结果要持久化,方便追溯。对于确认是系统问题导致的差异,要有自动修复的机制,但修复操作必须记录审计日志。
5. 常见问题排查与避坑经验实录
5.1 账务不平的排查思路
账务不平是金融服务最严重的问题之一。排查的时候我通常按这个顺序来:先看流水记录是否完整,有没有漏记或者重复记;再看余额更新是否正确,有没有更新失败但流水已记的情况;然后看并发处理有没有问题,是不是并发更新导致的数据覆盖;最后看对账文件有没有解析错误。
有个实际案例:某次对账发现一笔转账在转出方有记录,转入方没有。排查后发现是转入方的账务处理抛了异常,但异常被上层捕获后没有回滚转出方的操作。原因是转出和转入在不同的数据库,本地事务只能保证各自库内的原子性。后来我们引入了TCC,Try阶段先冻结双方资金,Confirm阶段才实际扣减,解决了这个问题。
5.2 性能瓶颈的定位与优化
金融服务的性能瓶颈通常出现在数据库层面。定位方法:先看慢查询日志,找出执行时间长的SQL;再看数据库的CPU、IO、连接数指标,判断是计算密集还是IO密集;然后看应用层的线程池和连接池配置,是不是池子太小导致排队。
优化手段按成本从低到高排列:加索引、优化SQL写法、调整连接池参数、加缓存、读写分离、分库分表。我一般优先考虑加索引和优化SQL,这两招能解决大部分问题。缓存要谨慎用,金融场景下缓存一致性不好保证。分库分表是最后的手段,复杂度高,不到万不得已不用。
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 接口响应慢 | 慢SQL | 慢查询日志 | 加索引、优化SQL |
| 数据库CPU高 | 全表扫描 | EXPLAIN分析 | 加索引、限制返回行数 |
| 连接超时 | 连接池满 | 查看连接池监控 | 调大连接池、优化SQL释放连接 |
| 死锁 | 加锁顺序不一致 | 死锁日志 | 统一加锁顺序、减小事务范围 |
| 余额不对 | 并发更新 | 核对流水和余额 | 乐观锁、悲观锁、缓冲记账 |
5.3 幂等失效的典型场景
幂等失效通常有几个原因。流水号重复:不同请求用了相同的流水号,导致第二个请求被误判为重复。这需要保证流水号生成规则足够唯一,通常用“业务前缀+日期+序列号”的方式。Redis过期:幂等标记在Redis里过期了,重复请求进来查不到标记,又处理了一次。这需要设置合理的过期时间,并且数据库唯一索引兜底。并发窗口:两个相同请求几乎同时到达,都查了Redis发现没有标记,都执行了处理。这需要加分布式锁,保证同一时刻只有一个请求在处理。
我一般会在幂等检查上加锁,锁的粒度是业务流水号。获取到锁的请求才继续处理,处理完释放锁。这样即使并发请求同时到达,也只有一个能进入处理逻辑。
5.4 对账差异的处理经验
对账差异分几种类型,处理方式不同。我方有对方无:可能是我方多记了,需要核实后冲正。对方有我方无:可能是我方漏记了,需要补记。金额不一致:需要逐笔核对,找出差异原因。状态不一致:比如我方显示成功对方显示失败,需要根据实际情况调整。
处理差异的时候一定要留痕。每一笔差异的处理都要有记录,谁处理的、什么时候处理的、处理结果是什么。这不仅是审计要求,也是后续分析问题原因的数据基础。我见过一个团队处理差异很随意,直接改数据库,结果后来监管检查的时候拿不出处理记录,非常被动。
5.5 上线前的检查清单
金融服务上线前的检查比普通系统严格得多。我整理了一份检查清单,每次上线前逐项确认。
- 账务逻辑是否经过充分的单元测试和集成测试,覆盖率是否达标
- 幂等机制是否验证过,重复请求是否返回一致结果
- 对账任务是否配置好,对账文件路径和格式是否正确
- 风控规则是否配置正确,灰度机制是否生效
- 数据库索引是否齐全,慢查询是否在可接受范围
- 监控告警是否配置,关键指标是否有阈值告警
- 回滚方案是否准备好,出问题能否快速回退
- 数据备份是否完成,恢复流程是否验证过
这份清单看起来简单,但每一条背后都是血泪教训。我经历过一次上线后发现对账任务没配置,第二天对账的时候发现数据对不上,排查了一整天才找到原因。从那以后,这份清单我每次上线都逐项打勾,再也不敢跳步。
6. 监控体系与安全防护的落地方法
6.1 关键监控指标的设置
金融服务的监控不能只看CPU、内存这些基础指标,更要关注业务指标。我通常设置三类监控:系统层监控、应用层监控、业务层监控。
系统层监控包括CPU使用率、内存使用率、磁盘IO、网络流量,这些用Prometheus+Grafana就能搞定。应用层监控包括接口响应时间、QPS、错误率、线程池状态、数据库连接池状态,这些需要在应用里埋点。业务层监控包括交易量、交易金额、成功率、对账差异数,这些是最能反映系统健康度的指标。
告警阈值设置有讲究。太敏感了天天误报,大家就麻木了;太迟钝了真出问题发现不了。我一般根据历史数据设置动态阈值,比如取过去7天同时段数据的P99值作为参考,超过就告警。关键指标比如账务不平、对账差异,设置零容忍,一有就告警。
6.2 安全防护的多层措施
金融服务的安全防护要覆盖多个层面。传输层用TLS加密,防止数据被窃听。应用层做身份认证和权限控制,每个接口都要验证调用方身份,敏感操作要二次确认。数据层做加密存储,敏感字段比如身份证号、银行卡号要加密。审计层记录所有关键操作,谁在什么时候做了什么,都要有日志。
防重放攻击也是重点。每个请求带时间戳和随机数,服务端检查时间戳是否在有效窗口内,随机数是否已经用过。这样即使请求被截获,也无法重复使用。接口还要做限流,防止恶意刷接口。限流可以用令牌桶或者漏桶算法,根据业务特点选择。
6.3 日志规范与审计要求
金融服务的日志要求比普通系统高得多。首先,日志不能丢,关键操作必须有日志记录。其次,日志不能改,要写入只追加的存储,防止被篡改。再次,日志要能追溯,通过一个请求ID能把整个调用链路的日志串起来。
日志内容要包含:时间、请求ID、用户ID、操作类型、操作对象、操作结果、耗时。敏感信息比如密码、完整卡号不能记日志,要脱敏。日志保留时间根据监管要求定,一般至少半年,重要的要保留更久。
审计日志和业务日志要分开存储。审计日志专门记录关键操作,比如账户冻结、大额转账、参数修改,这些日志要长期保存,随时可以调取。我通常用独立的审计表来存,字段包括操作人、操作时间、操作类型、操作前值、操作后值、操作原因。
6.4 应急响应流程
再好的系统也可能出问题,关键是要有应急响应流程。我一般把应急响应分为四个阶段:发现、定位、止损、复盘。
发现阶段靠监控告警和人工巡检。定位阶段要快速判断问题影响范围和严重程度,是单个交易问题还是批量问题,是资金问题还是体验问题。止损阶段要果断,该切流量切流量,该回滚回滚,先恢复服务再查原因。复盘阶段要找到根本原因,制定改进措施,避免同样的问题再次发生。
应急响应最重要的是决策要快。我见过一些团队出问题后层层汇报,等领导拍板,结果错过了最佳止损时机。我的建议是:一线值班人员要有一定的决策权,比如发现资金类问题可以先切流量再汇报。同时要建立明确的升级机制,什么问题什么级别,该找谁找谁,不能乱。
7. 个人实操体会与后续扩展方向
做金融服务这些年,最大的体会就是:敬畏心。这个领域跟其他互联网业务最大的不同在于,你的每一个技术决策都可能影响到真实的资金。一个bug在社交产品里可能只是用户发不了动态,在金融产品里可能就是用户的钱少了。所以做金融一定要有敬畏心,对每一行代码负责,对每一笔交易负责。
另一个体会是:简单可靠优于复杂精巧。我见过很多团队追求技术先进性,用各种新框架新中间件,结果系统复杂度上去了,稳定性反而下来了。金融服务的核心诉求是稳定可靠,技术选型应该优先考虑成熟度、社区活跃度、团队熟悉度,而不是一味追求新潮。能用简单方案解决的,不要引入复杂方案。
后续如果要把这套框架扩展,我觉得有几个方向可以考虑。一是多币种支持,不同币种的精度、汇率换算、记账规则都不一样,需要在账户体系和账务逻辑上做扩展。二是实时风控,随着交易量增长,事后风控不够用了,需要把更多风控规则前置到交易主流程里。三是自动化对账,目前对账差异还需要人工处理,未来可以通过规则引擎实现大部分差异的自动修复。
最后分享一个小技巧:金融服务的测试数据准备很麻烦,我通常写一个数据工厂工具,能快速生成各种场景的测试数据,比如不同余额的账户、不同状态的交易、各种异常场景。这个工具在开发和测试阶段能省很多时间,强烈建议每个金融项目都配一个。