12306核心模型设计思路和架构设计
12306核心模型设计思路和架构设计
技术人员往往更注重技术层面的解决方案,比如一上来就分析如何集群、负载均衡、排队、分库分表、用锁,用缓存等技术问题,而忽略了最根本的业务层面的思考,如分析业务、领域建模。其实复杂的业务系统,则越要设计一个健壮的领域模型。如果一个系统的架构我们设计错了,还有补救的余地,因为架构最终沉淀的只是代码,调整架构即可;而如果领域模型设计错了,那要补救的代价是非常大的,因为领域模型沉淀的是数据结构及其对应的大量数据,对任何一个大型系统,要改核心领域模型都是成本非常高的。
12306这个系统,核心要解决的问题是网上售票。涉及到2个角色使用该系统:用户、铁道部。用户的核心诉求是查询余票、购票;铁道部的核心诉求是售票。购票和售票其实是一个场景,对用户来说是购票,对铁道部来说是售票。因此,我们要设计一个在线的网站系统,解决用户的查询余票、购票,以及铁道部的售票这3个核心诉求。看起来,这3个场景都是围绕火车票展开的。最主要的需求就是查询余票和购票。
其实可以将12306类比成一个商品是车票的电商系统,那购票就类似于购买商品,然后每张票都有库存,商品也有库存的概念。但是如果我们仔细想想,会发现12306要复杂很多,因为我们无法预先确定好所有的票,如果非要确定,那只能通过穷举法了,实际上,这个车次可以卖的票是非常多的。为了方便后面的讨论,我们先明确一下票是什么?通过分析,我们可以知道一张票的本质是某个车次的某一段区间(一条线段),这个区间包含了若干个站点。然后我们还发现,只要区间不重叠,那座位就不会发生竞争,可以被回收利用,也就是说,可以同时预先出售。另外,经过更深入的分析,我们还发现区间有4种关系;不重叠部分重叠、完全重叠、覆盖,不重叠的情况我们已经讨论过了,而覆盖也是重叠的一种。所以我们发现如果重叠,比如有两个区间发生重叠,那重叠部分的区间(可能夸一个或多个站点)是在争抢座位的。因为假设一列火车有100个座位,那每个原子区间(两个相邻站点的连线),最多允许重叠99次。一个车次能够出售一张车票的核心业务规则是这张车票所包含的每个原子区间的重叠次数加1都不能超过车次的总座位数,实际上重叠次数+1也可以理解为线段的厚度。
GRASP九大模式中的信息专家模式,将职责分配给拥有执行该职责所需信息的类。我们这个场景,车次具有一次出票的所有信息,所以我们应该把出票的职责交给车次。DDD中聚合设计有一个原则,就是:聚合内强一致性,聚合之间最终一致性。票不是核心聚合根,票只是一个计算的结果,一个凭证而已,票本身没有什么逻辑;12306真正的核心模型应该是车次,车次具有出票的职责,并以强一致性的方式维护一次出票(或退票)时所有原子区间的可用票数。2306这样的业务场景,非常适合使用CQRS架构;因为首先它是一个查多写少、但是写的业务逻辑非常复杂的系统。所以,非常适合做架构层面的读写分离,即采用CQRS架构