突如其来的需求
公司最近要上线一个新产品,准备开个小型的发布会做一波推广。离发布会还有一周时,产品经理突然跑过来说本次发布的是产品的内测版本,用户需要有邀请码才可以使用。
这是一家刚开始创业不久的公司,技术积累并不多,目前并没有邀请码系统。而本次发布的产品涉及到三项相互比较独立的服务(都通过统一登陆系统校验用户),所以如何在一周时间完成这个功能便成了一个问题。
条件反射示设计
一周时间上一个邀请码系统肯定来不及了,而且就算上了一个邀请码系统,产品的三项服务都要进行邀请码服务接入改造,想想就知道这个方案的风险很大。那么最快的方案就是改造统一登陆服务,把邀请码放在统一登陆里面,那么就可以达到有邀请码的用户才能登陆使用。
问题是统一登陆系统服务整个公司的所有产品,不能因为新发布一个产品,导致其它的产品也不能用。所以统一登陆里面除了加入一个邀请码的业务逻辑以外,还要加入一段判断登陆来源系统确定校验规则的逻辑。
如鲠在喉的难受
很多人都号称自己有代码洁癖,然而最后都屈服于时间不够的现实。程序员大多有一个特点,在写下脏代码的那一刻是很难受的,但因为时间紧迫,只能暂时先解决掉这个问题,而少有人会等到有空后去重构曾经写下的脏代码。
设计也一样,如果迫于时间的压力,做了一些丑陋的设计,一旦系统上线之后,还会再去修改设计的概率几乎为零。而且重构代码还会有测试用例来保证正确性,重构设计的风险和代价都要大的多。
对于上面邀请码的设计,有几个地方丑陋的很明显。
- 邀请码业务跟统一登陆业务没有强相关性,不该放在一个系统里。
- 服务校验规则的修改应该由服务自己决定,不该放在统一登陆里。
做为一个有过几年开发经验的人,我深刻的知道,一旦设计确定是这样的,统一登陆系统很快就会变的臃肿不堪。因为一旦有了一个丑陋的设计,那么其它丑陋设计也会进入的理直气壮。系统的熵增抵抗性全无。
深思熟虑后行动
其实可以把此次需求分解成三段业务逻辑。
- 邀请码生成
- 用户邀请码绑定
- 用户是否有邀请码的校验
邀请码生成
关于邀请码生成,可以手工生成,也可以写个小程序生成,然后导入系统,也可以系统自动生成,这是一个很独立的功能,没有耦合关系。所以对设计决策影响不大。
用户和邀请码绑定
这里需要考虑的是绑定功能在哪里实现,绑定关系放在哪里?上面说过邀请码的业务和统一登陆的业务是无关的,所以绑定功能不应该放在统一登陆里,那也没有更合适的其它系统可以放,那么就新建一个邀请码系统来放这段逻辑。那么绑定关系看起来也理所当然放在这个邀请码系统了。
校验用户是否绑定邀请码
似乎理所当然的这段逻辑应该放在邀请码系统里。各个使用到邀请码的系统通过调用服务来校验用户。但这样的设计其实是有问题的,统一登陆系统现在是合理了,邀请码系统也是合理的,客户端系统看上起也是合理的,使用哪些服务就调用哪些服务的接口。
然而整个系统的架构却是不合理的。客户端如果需要知道它调用的每个服务所属的系统,随着系统增多,整个系统架构会固化,难扩展。
较为合理的设计
现在客户端系统的用户校验是使用统一登陆系统的服务,那么最好所有关于用户校验的服务都通过统一系统,用户是否绑定邀请码只是校验服务接口中的一个参数。
前面说过邀请码跟统一登陆无关,而现在又说要在用户验证里校验邀请码,这不是前后矛盾吗?其实不然,统一登陆确实跟邀请码没有强关联关系,但用户校验应该提供用户属性校验的功能,而用户是否绑定邀请码,只有用户的某个属性而已,所以统一登陆应该支持这个功能。
具体的实现
- 用户通过不同的注册方式,可以携带不同的注册信息。
- 统一登陆在用户注册成功之后广播有新用户注册的消息出来,并携带上额外的注册信息。
- 验证码系统订阅新用户注册的消息,收到到校验注册信息中是否有邀请码,如果有则尝试绑定用户和邀请码。如果绑定成功,广播一个用户属性更新消息,新增用户的邀请码属性。
- 统一登陆系统订阅用户属性变更消息,收到后更新用户的属性信息。
- 统一登陆系统提供的校验服务里可以携弹参数校验用户的属性信息。
后期如果邀请码不是通过注册是填写,而是注册之后再去绑定,只要在邀请码系统提供一个绑定功能,然后绑定成功后广播消息即可,统一登陆系统不用做修改。
后期邀请码功能要下线,统一系统也不需要做修改,客户端系统的校验服务不校验用户属性即可。
总结
从接到需求到开始实现,设计一共做了两次修改。虽然设计看上去是更合理了,但是怎么来证明呢?或者所谓的更加合理只是你的一家之言,只是因为你是这个设计的决策者和实现者,这种设计更符合你的审美而已。
审美当然很重要,但是我们还需要理性来分析为什么说更加合理。
对比第一次设计,我们可以发现它违反了开闭原则
,用户属性每一次变更都需要修改统一登陆系统,这次是邀请码,下次可能又是优惠码或者别的什么。而我们最后的设计,用户属性的变更可以通过新增一个相应的系统来完成,统一登陆系统不用任何修改。
对比第二次设计,我们可以发现它违反了迪米特法则
,客户端系统需要知道它每一个校验逻辑的服务提供者是谁。而我们最后的设计,客户端始终调用同一个校验服务,通过不同的参数来表达不同的校验逻辑。
点题
从题目可知,我不仅仅是想写这一次设计的思考过程,更想探讨的是当我们面对一个临时性需求时,我们应该如何应对?
临时性需求从来不会少,而对于创业型公司更是常态。实际中我看到太多这样的例子来,来了一个临时需求,然后在各个涉及到的系统里增加一段临时代码。
其实你要写的业务逻辑是一点也没少,你节省下了新建一个系统的时间和调整原先系统结构的时间。但你又付出了什么呢?
- 新建一个系统的时间,涉及到公司的一些技术基础设施,现在有很多开源的技术可以用来构架这些基础设施。也有很多创业公司在提供这方面的服务,直接购买就可以用了。
- 调整原先系统结构的时间,
流水不腐
,如果你的系统在遇到不支持的需求时会合理的调整,那么系统的结构会向好的方向发展,调整也会越来越顺畅,对业务的支持也会越来越好。反之则会越来越固化。
我们都知道技术债务
是怎么回事,不要欺骗自己说之后再去修改。当遇到一个临时性需求时,提取出里面非临时性的逻辑,调整你的系统,使它合理的实现这段业务逻辑。把临时性的部分放到临时性系统里去。