前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >业务逻辑开发套路的三板斧

业务逻辑开发套路的三板斧

作者头像
Bug开发工程师
发布2020-02-20 12:47:01
4370
发布2020-02-20 12:47:01
举报
文章被收录于专栏:码农沉思录码农沉思录

点击上方“码农沉思录”,选择“设为星标”

优质文章,及时送达

背景

作为一个研发,我们工作中都会处理面临下面这些困惑:

  1. 又加需求,一个方法本来就处理了 300 行,现在又加 50 行。
  2. 状态逻辑太多了,产品第 2 期又加了一个逻辑,代码结构要调整,很头疼。
  3. 每个人都在吐槽,业务研发在工作中处理最多的就是 if else,好不容易写个 switch 都能给同事吹一周。以上三个场景应该是日常需求迭代优化中面临最多的场景了,作为一个自称编码水平较高的人,总结了以下三个真实的场景,给出一些可选的方案。

第一板斧:抽象事件,驱动业务

核心

梳理产品逻辑中的主流程节点,整理节点所需要的依赖数据已经节点触发后对应的业务逻辑。类比消息队列,也是不同的业务方订阅自己的事件源,进行不同的处理。不同点在于一个是分布式,一个是本文描述单机业务处理场景。

实际例子

举一个用户注册之后的场景,需要:

  1. 发短信
  2. 发优惠券 如果用户注册成功之后,直接发了mq消息,那么用户系统和券系统分别订阅这个消息进行处理。不过这里讨论的是在一个项目模块中处理完所有相关的逻辑

代码将在 UserRegistered() 中一步一步去处理逻辑,之后需求又加入了 初始化A数据初始化B数据 两个需求,实现也会落到这个方法之后,最后整个代码会越来越臃肿。

接下来用事件订阅模型去化解这个点,非常实用,一点都不华丽,代码也很好读懂。

最后对应到程序代码可能是这样的:

代码语言:javascript
复制
# 事件注册
Event::register(UserRegistered::class, [
    SendSms::class,
    SendCoupon::class,
    InitSystemA::class,
    InitSystemB::class,
]);

# 业务调用
handleRegistered($user) {
    $event = new UserRegistered($user);
    $event->fire();
}

后面的迭代维护中,只要主流程不发生变化,那么相应的逻辑只需要去增加订阅者去实现。

第二板斧:有限状态机,定义流程

在业务逻辑数据处理这一层,很多的业务场景都与数据扭转状态有关,并且最后会有相应的数据实体相映射。比如我们常见的:

  1. 各种商品订单(天猫,淘宝,外卖)
  2. 工作流(审批,工单处理)

这类需求的特点是,读写场景QPS不高,对数据的准确一致性要求非常高。我们底层一般直接存储到数据库,之上加一层简单的数据缓存就能处理。

面临的主要问题是,状态太多难以维护,应该还会出现状态的调整比如特殊场景下的状态A到状态Z的扭转。

不过业内早已给出了比较通用的解决方案,有限状态机。下面我们列举一个简单的订单状态扭转逻辑:

代码语言:javascript
复制
fsm := fsm.NewFSM(
    "created",
    fsm.Events{
        {Name: "pay", Src: []string{"created"}, Dst: "paid"},  //支付
        {Name: "cancel", Src: []string{"created"}, Dst: "closed"}, //关闭
    },
    fsm.Callbacks{
        "after_pay": func(e *fsm.Event) { /* 支付成功调用 */ },
    },
)

如果设计到状态相关的调整,在状态机定义的地方去修改就可以解决问题。和事件订阅非常相识,也是集中维护,同一管理。

第三板斧:n元组配置,组合输出

软件工程没有银弹 --布鲁克斯

软件开发中我们遇到的一个一个需求都是不可预测的,我们确实很难找到一种终极的解决方案。不过本文中的讨论局限在业务逻辑开发的开发套路。那么,n元组这个简单的概念可能算得上一颗银弹。不管业务逻辑有多复杂,在理论上我们都能抽象出n个字段来表达我们的数据模型。

拿一个订单举例子,我们有如上订单特征。不可避免的,每一个业务场景,每一个逻辑,产品逻辑都可能有自己的配置和相应的处理流程,且这些逻辑都是业务迭代优化的重灾区,比如:

  1. 江浙沪地区包邮
  2. 某一批固定的城市需要打8.8折
  3. 雨天调价格
  4. 法定节假日打烊不服务
  5. vip身份的用户展示文案特殊处理

每一个开发同学都曾被这些逻辑折磨的异常痛苦,这里给出一个抽象的方案,最终每一个订单特征都会落到具体的业务处理类,所有的类都实现该业务场景的 interface,主流程只需要构造 n元组然后获取到相应的 interface 之后进行调用。

n元组的概念其实早已经渗透到了开发中的每一个角落,我们需要做的事情就是,在业务开发的时候真正的去思考这一层数据模型,然后加以运用,最后的代码一定不那么 if else。

总结

以上三板斧在业务中使用可以很轻,也可以很重。这就意味着我们能自己写一个简单够用的(对于你完全了解成长有限的业务场景),或者找一个star多且在维护的开源方案(对于有潜力,未来大有可为的业务)来代替。同时,这些编码套路在各种场景下都能非常灵活的组合,比如:

  1. 订单状态更新后触发事件(状态机+事件订阅)
  2. 不同业务线,状态机配置,初始化放松不同(n元组+状态机)

不得不提到的一点,在漫长的业务迭代中,产品文档会越来越缺失,最终只有通过代码才能了解线上的真正逻辑(有时间代码过于复杂,可能就没有人知道线上的具体情况了),集中配置,统一维护的意义之一就在于此。相信做过复杂历史系统的交接或重构的同学对这一点都深有体会。

祝大家在2020年工作顺路,家庭幸福,合家团圆

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-02-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农沉思录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 第一板斧:抽象事件,驱动业务
    • 核心
      • 实际例子
      • 第二板斧:有限状态机,定义流程
      • 第三板斧:n元组配置,组合输出
      • 总结
      相关产品与服务
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档