用户和系统的边界区分
我们在做风控层的时候有个需求,就是实现一条一条的风控规则,对每个规则,都有自己需要维护的状态,比如关注盘口的需要读books5,关注资金的需要读fund,关注nav的需要读nav_update。之前为了复用逻辑,直接让风控规则的基类借用了Executor,让风控规则去override+subscribe Executor的各类事件回调。这是当时设计风控层时很自然的做法,但是今天梳理时发现会有些恶心的问题:
- 调用复杂度上升:一个事件过来后需要知道这是哪类事件,哪些规则订阅了这个事件,再逐个去调用订阅了这个事件的规则对应的回调函数,对我们海量的事件来说,每处理一个事件都需要反复查字典反复去调各种不同的函数,是不小的性能开销
- 可维护性下降:Executor接口定义可能常改,改一次都要调整一大堆风控规则
- 思维上比较“重”:每个风控规则其实都是一个小执行策略Executor,几十条规则就是同时跑几十个Executor,不对劲
思考了两个小时后发现其实是内部系统和用户接口的边界没有梳理清楚,正确的做法是直接在风控层用更底层的on_event函数,而不是一堆为了方便用户顾名思义的不同的回调函数。区分几个概念:
- 系统:提供一系列功能的复杂代码逻辑
- 用户:对系统不了解的使用者,在我们的语境下可以是策略研究员
- 工程师:对系统内部实现非常了解的开发者,就是我们
- 用户接口:系统的用户和系统内部逻辑进行交互的入口
Executor的API set设计是面向用户的一种易用用户接口,使得用户不感知执行引擎、ctx中的事件循环和分发逻辑,保证了可读性、易用性、防呆设计。但是风控层本身是系统的一部分,这部分逻辑不面向用户而是面向工程师,之前图方便直接借用了Executor的API set,但是实际上风控层根本用不着这么上层的API:我们知道每个可以override的callback背后都是一个个的event,那么不用绕一圈去屏蔽event概念,直接定义个on_event函数就完事了,里面让规则自己去match event。
面向用户的接口要减少概念;面向系统的接口要暴露事实。把系统部件建立在用户接口之上,迟早会被用户接口的演进拖累。
小小的工程哲学:抽象给用户,底层给系统。顺手记一下。