会员体系设计

    又到了年终回顾总结的时候,突然发现自己个人博客上没有关于业务知识的分享,全是技术篇。这给了我很大的启示,因为我自己长期都在做业务,大大小小、不同领域的业务做过很多了,感觉可以把自己对业务设计上的感悟分享一下(又有了灌水的好方法)。

1 应用架构

对于我们做业务的后端同学来讲,虽然一些领域设计、架构设计很高大上,但是在实际的业务开发中所应用到的非常小,所带来的作用也是一个长期作用,在互联网行业的快速发展下优势不明显。而最重要的其实就是一个应用的结构划分还比较实在一点,更具体的说应用划分其实也是为了减轻一部分的管理成本。

在整个会员业务的迭代中,踩了很多很多的坑,最终慢慢形成了以下的用户会员域-应用划分结构,希望可以给大家带来一点帮助。

image-20220207161401278

我个人认为,应用划分应该追求的是业务单向流转,所以我的基本设计理念就是DAG(有向无环图)。

同时为了能够使应用的划分更为清晰,职责更为明确,将所有的业务应用抽象为了三类应用,

  • 业务应用

    业务应用位于各个业务线的最上层应用,可以理解为整个业务的对外入口。

  • 能力应用

    部分参考了中台的设计理念,将部分复杂、难度高、聚合度高的业务打包成能力应用,给上层业务提供能力支持。更简单的来说,就是为了能够复用部分设计起来比较复杂的功能。

    比如说对于订单的提交处理流程,想要追求高吞吐量、高性能、零丢单还是需要投入很大的成本的,如果单独封装成一个应用,由专门人员去负责优化,这时候可以对业务部门不造成额外的负担和成本。

  • 基础应用(业务基础应用)

    单业务领域内的最底层应用,在单个业务领域内业务无关(我也不知道怎么能解释出来这个关系。。)

    我之前把这个是当成了业务无关的基础应用,其实是不太准确的,比如说用户应用,其实他跟整个用户会员域是有关联的,但是放大到用户会员域去看,该应用应该抽象成不关心上层业务,而只去关心用户会员域相关的能力。

该划分设计下的优点:

  • 三类应用中间对于业务处理单向流转,可以降低耦合度、优化成本和发布成本。
  • 可以降低业务的开发成本,增加并发度。比方说整个开通会员业务,可能需要订单和会员的同时支持,如果划分应用之后,可以两拨人协助进行,并且将彼此的影响降到最低。
  • 可以有效解决开发人员的协助问题,由于各个开发人员的技术水平参差不齐,开发风格迥异,如果十来个开发同时开发一个应用,彼此之间难免会产生冲突,尤其是代码合并冲突问题相当严重。而应用拆分后可以显著降低这些影响。

不足之处:

  • 当业务发展位于初期时,业务比较简单,可能就二三十个类就足以支持,放在一个应用中开发比较顺畅,而现在就需要至少拆分成三个应用,变相的增加了开发成本。
  • 该划分层次下可能导致代码划分很不均匀,比方说基础应用层的应用中,几乎没有什么代码,很薄的一层,绝大多数处理过程都在业务应用中。

其实这也是绝大多数系统设计拆分后的通病,虽然拆分可以带来优势,但是也带来了维护成本。就目前来看,我个人认为,其优势是可以弥补不足的。

特别加注:用户和会员绝对是两个完全不相干的东西,我一开始设计把这两个放一起了,导致迭代过程中不断的纠结,走了很多弯路。

2 会员业务简述

我之前所负责的会员应用主要包括三大部分,会员开通、会员成长体系和会员权益。

对于我们业务的开发人员来说,一定对业务充分理解,并能站在产品的角度上去和产品沟通交流。

实现一件事情的方法多种多样,我们业务开发要能从中选择一条技术实现起来简单的路径,或者说提供另一种需求给产品参考,来尽可能实现用户需要,毕竟实现简单意味着bug少并且修复快捷

  • 会员订单

    如果只是单纯的购买会员,其实逻辑很简单,只要保障一下开通会员业务的可靠性就好了,如下图所示。

    image-20220207161452630

    我对订单系统进行了重构,当订单系统受到第三方支付回调后,记录流水后就返回成功(使用事务保障原子性),接着就可以向各业务系统进行交易成功的消息群发了。

    由消息的ACK重试 + 定时任务轮询流水进行消息重发,来保障整个订单的可靠性和一致性。

    但是在业务上,会员的开通可能会变的及其复杂,比如说多种会员可以并存(一定要说服产品把这个该死的需求拿掉)、跟第三方会员的打通(比如说淘宝88会员)。

    强烈建议:如果会员业务对于公司来说并不是最核心的场景,就保持单一会员就好了,多会员并存真的很恶心。

  • 成长体系

    一般成长体系不外乎就是会员的等级提升和成长值系统的计算,甚至我们之前还弄了个任务中心系统专门用于成长值任务的完成。

    成长值的发放和计算规则十分的蛋疼,我当时都做吐了。。

    首先,开通的不同会员(单月、包年、连续包月)的成长值计算是不同的,而且这些会员还可以累加开- - 也就是说需要定位到任一一个时间点,该用户生效的是哪个级别哪个来源的会员,才能计算出该时间点要加的成长值是多少。更加可怕的是会员是有逆向流程的。。。。。。此处我打了六个句话来默哀我当时开发的心理感受。我至今都没有想出来简单的技术方案能同时实现这些需求,以下是我之前的方案设计。

    我使用一张表来存储用户的会员持续时间(特别提醒,年、月建议使用标准365天、30天来减少业务复杂度

    用户 会员类型 开始时间 结束时间 退款标识
    用户A 年费会员 2021-01-05 2022-01-04 0
    用户A 连续包月 2022-01-05 2022-02-03 0
    用户A 月费会员 2022-02-04 2022-03-04 0

    单用户连续开通期间内,时间段保持连续(使用锁来保证),发生了退款时要抹除退款的会员时间,并修改以后的所有时间段来保持时间连续。由于退款发生频次较低,该操作的复杂度可以容忍。

  • 会员权益

    权益部分更多是在在和其他部门、外部系统的各种沟通协商。

    技术上最大的难度就是一个分布式事务问题,怎么来保障会员系统和外部系统之间的数据一致,比方说会员中礼物显示发放了,实际上却没有发放成功。

    鉴于当时业务发展刚刚起步,解决方案就是靠用户反馈,靠数据订正来解决。真正的解决手段可以参考支付宝的支付回调方式(随着开发经验的不断增加,越发的体会到这种回调模式真的是如此完美),该回调方式可以称得上和外部系统交互的优秀模板。

整个会员业务的设计中,最难的就是会员其实是要与很多其他系统进行打通交互的。比方说会员的来源可能多种多样(来自第三方会员、其他业务的附赠会员)、会员的成长值来源多种多样(活动赠送、任务完成)、会员权益的范围广阔(特别是会员权益这块,会让会员和各个系统之间产生关联)。

所以如何预留扩展点,保持会员自身业务的纯粹非常考量开发人员的水准。