接上一篇 一条拍卖系统优化(一) -- 建立拍品Lot模型,实践CQRS,分享一下组内小伙总结的后续优化
一、背景:现有拍卖系统设计实现
1.1 核心领域实体 ReadWriteLot 和 ReadonlyLot (CQRS)
ReadWriteLot 通过DB数据构造,主要解决拍品的写场景问题, 例如:开拍、出价、成交、流拍 等;
ReadonlyLot 在竞拍中的拍品使用redis中的数据构造进行读加速解决竞拍过程中高频读问题(无缓存,redis数据与DB数据强一致),非竞拍中的拍品数据已不再变化通过ReadWriteLot降级得到并进行缓存;
1.2 优化前的出价和流拍实现方案
因为业务场景下存在即为了应对线上或下线这不同业务场景下拍卖业务的差异流程,例如:线上出价受额度限制而线下代拍员不受该约束、线上出价后需要发送出局提醒而同步拍不需要等等。为了应对这种差异我们通过抽象的出价策略接口定义,想借此隔离了不同出价策略 OnlineBidStrategy(线上出价策略) 和 OfflineBidStrategy(线下出价策略)的实现。
1.2.1 线下出价策略:
线下的出价包含三个部分。 1、无效掉高于自己本次出价的历史出价,无效出价的结果 2. lot 调用出价领域行为,持久化出价结果 3.发送出价的相关通知、IM 消息、修改价格系统价格
1.2.2 线上出价策略 :
线上出价包括三个部分:1.线下出价的前置校验。2. lot 调用出价领域行为,持久化出价结果 3. 发送出价的相关通知、IM 消息、修改价格系统价格,线下出价自动订阅
1.2.3 领域实体Lot 的 出价方法 :
- 线上出价的校验 或 线下出价的校验;2. 出价变更相关出价信息
到这里我们可以思考这样几个问题:
这里的我们寄期望引入出价策略的抽象能够隐藏不同场景下的出价流程的差异,真的做到了吗?
我们寄期望与出价策略的抽象能够隔离实现细节,但存在在策略中的一些操作往往又与拍品领域对象的出价行为有一定的相关性,并且这种相关性的感知最终还是扩散到了拍品中,这还是背离了我们的设计初衷。
线上出价和线下出价的前置校验逻辑散落在 Lot和 两种不同的出价策略中,显然如果出价逻辑变更,我需要修改的是Lot 和 出价策略两个地方,要是能将这个差异性的逻辑统一写在一个地方就好了,比如当线下出价的校验逻辑改变了,我只去修改 Lot 内部关于线下出价的校验或者我只去修改 OfflineBidStrategy(线下出价策略),这样看起来似乎更加合理。
理想情况下我们期望这种差异化的行为可以通过领域事件或者对象的不同行为实现,对外提供一个标准化的出价服务,无需感知不同场景下的实现差异,但显然当前采用的出价策略的设计方案并没有能够隐藏住实现细节和逻辑内聚(Lot实体还是区分了不同场景做了一些差异化处理)。
2.是不是只要把线上出价的逻辑和线下出价的逻辑都放到lot 的 bid(出价行为)中然后一个简单的 if-else就能将线上出价和线下出价的逻辑隔离,同时又将逻辑内聚到lot中,如下图?
这个解决方案我觉得其实也不是完全不可行的,只不过目前来说我们对不同类型的出价场景(线上用户出价、线下用户出价)的差异性主要体现在:出价前置的校验、出价后后置的操作(比如发送消息、订阅提醒等)、用户出价后会对自身信息进行变更(比如变更自身的额度信息等)。如果按照上面的设计貌似很难满足需求,因为很明显在出价这个上下文中,有两个不同的实体拍品( Lot)、出价用户 (BidUser),之前的设计中并没有构建出BidUser 这个实体,主要是因为我们对不同出价用户并没有细致差异化的区分,但是随着业务发展的需要很多时候一个Lot 无法支撑这个业务逻辑(这也是引入出价策略的原因之一)。
3.出价领域服务主要解决的核心问题是出价,但是出价后的一些列行为比如发送通知、IM消息、订阅拍品、修改price系统的价格,这些本质上不属于出价行为,即使这些后置的操作执行失败也不应该影响到本次出价,那么应该在哪里触发这些出价的后置操作?
这里很多出价的后置操作本质上与出价核心行为无关,并且后置操作失败或者报错都不应该影响到本次出价的成功与否,这里我们最好使用不与事务绑定的领域事件来实现。具体实现见下文。
二、优化设计构建BidUser实体
构建BidUser 实体的主要作用,解决线下用户出价、线上用户出价的差异性,优化出价的实现,优化成交的实现,最终达到的结果是,将原来散落在应用服务中的逻辑内聚在领域实体中,使用领域服务操作领域对象,应用服务调用领域服务,发布领域事件
2.1 构建线下出价用户、线上出价用户实体
注意BidUser的领域行为接受的拍品都为只读副本,通过这种方式安全的在不同领域对象中进行对象的访问
2.3 领域事件的应用
一次出价的基本流程:
BidUserA 举牌(扣除出价额度)=> Lot(接受BidUserA出价,将BidUserA置为领先,出局BidUserB)===发布出局事件==> BidUserB 出局(归还出价额度)。以拍品为桥梁,隔离了竞价用户之间的关系,同时通过领域事件模型,也解耦了出价领先、出局两者的关系。
2.4 领域服务实现和应用服务的实现
出价的领域服务
出价的应用服务
通过提升出BidUser模型,我们将原有的分散在出价策略以及拍品实体中关于”出价用户“的差异化处理内聚到了BidUser模型中,拍品的模型最终达到了场景的无关性。
回到问题的原点我们再想一下,既然拍品能支持线上拍或者线上线下同步拍,其本身就应该是场景无关的也就说明拍品本身应该是场景无关的,真正与场景有关的应该是这个过程中参与的出价人是有差异的,从这一点上反观这个模型上面的结论能够得到互相的印证。
2.5 待优化
目前领域对象的持久层,并不是一个稳定的逻辑,我们要根据不同的领域行为,写不同的持久化的方法,这样其实就相当于,领域行为的逻辑扩散到了持久层
目前bidUser 主要用于出价阶段,未来可以考虑赋予bidUser更多的含义,比如让bidUser去完成支付保证金的逻辑等等