具体例子求解,异步后数据弱导致的决策失误问题

14-06-17 sinaID87521
                   

最近看了 cqrs和lamx.

采用 富领域模型 ,event source. 异步操作.

这个有个bad case, 我不知道采用什么方案解决.

钱有100,两口子之前有约定要剩下90. 老公看到有100,花10元,花完以后因为事件异步,数据不一致,此时老婆刷新页面也看到100,再花10元. 最终所有异步事件都处理完之后最终是80元. 不符合用户的预期.

这个问题如何解决?

                   

4
banq
2014-06-18 09:35

你这个案例是数据一致性问题。数据一致性分强一致性和弱一致性。你现在要求的是强一致性,而异步属于弱一致性,当然解决不了强一致性。

DDD聚合根实体使用行为守护状态,能够保证状态变化符合强一致性。所以,这里建立一个聚合根Money,里面封装的是余额100元。通过Money的add和remove方法保证余额的高一致性。

正因为有了DDD聚合根将特殊的高一致性对付了,缺省的其他普遍的才可以使用异步这种弱一致性方法,就如同足球比赛,将C罗这个特殊人才看住了,其他就好办了,擒贼先擒王。

函数编程也是这个道理。将状态可变看住了,其他就可以都要用不变性。两者都是相通的。

sinaID87521
2014-06-18 23:41

2014-06-18 09:35 "@banq"的内容
通过Money的add和remove方法保证余额的高一致性 ...

能结合上面的例子具体说说如何保证强一致性么?

上面例子中两个都是remove.

万分感谢.

还有一个问题,先在这里问了.之后整理成专门的帖子.

cqrs 架构中 多个事务生成的领域event 谁先执行谁后执行?

两个实体Teacher ,Student

事务1 产生事件, TeacherEvent1,StudentEvent1,事务2 产生事件 TeacherEvent2, StudentEvent2.

如何记录这些事件,是否需要 把N个领域事件和事务Id绑定,以便之后按事务顺序执行这些事件.确保整体事件的成功或者失败时,整体事件事务回滚.确保数据的正确性?

[该贴被sinaID87521于2014-06-18 23:49修改过]

banq
2014-06-19 06:46

2014-06-18 23:41 "@sinaID87521"的内容
能结合上面的例子具体说说如何保证强一致性么 ...

可以参考我的什么是流式思维中案例,类似其中TV的ChannelState 。这里大概用代码重新写一下,

class Money{
  private AtomicInteger balance = ..;
  
  public void add(int v){
         balance.addAndGet(v);
  }

  public void remove(int v){
         balance.decrementAndGet(v);
  }

}
<p>

这里使用Java的并发控制AtomicInteger,实际上强一致性都要通过锁实现,比如使用数据库表锁,事务锁等,以保证每个时刻只有一个线程修改状态,优雅是像LMA使用Disruptor使用单线程访问。Jdon框架使用Disruptor支持上述修改。代码如下:

class Money{
  private int balance = ..;
  
 @[author][author]OnCommand[/author][/author]("addCommand")
  public void add(int v){
         balance=balance +v;
  }

 @[author][author]OnCommand[/author][/author]("removeCommand")
  public void remove(int v){
                  balance=balance -v;
  }

}
<p>

通过Jdon框架提供的元注释来保证这两个方法是单线程访问,这样,如果方法体内部复杂步骤,无需synchronized 或JTA的事务机制。

[该贴被banq于2014-06-19 06:47修改过]

sinaID87521
2014-06-19 11:35

2014-06-19 06:46 "@banq"的内容
单线程访问 ...

上面代码演示的是内存并发编程; 引申到事件流式执行. 只是解决并发导致的原子问题, 但没有解决上述的业务问题.

上述方案最终钱还是80元,没有避免异步事件框架下妻子扣钱成功的问题.

昨天咨询了下我们的高T.这个场景在国外银行很常见,国外有夫妻卡.,他认为是这样实现的:

先说说不用异步事件框架实现是如何保持一致性的.

丈夫显示100元, 进行消费,向后端 传递聚合跟的objectVersion 1, 正常扣钱10. 传递聚合跟的objectVersion值+1变为2.

妻子由于也是显示100元,进行消费,所以递聚合跟的objectVersion也是1, 在你的调用方法前会做业务校验,由于版本号objectVersion不匹配,妻子会得到错误.页面重新刷新,显示90元. 妻子就不会继续消费了.

再说说异步事件架构下:

丈夫显示100元,进行消费,. 变成事件1存储. 正常返回给前端.但不是真的出钱. 而是告知用户"后台正在处理,请稍等".

妻子由于也是显示100元,进行消费,同样变成事件2存储.正常返回给前端.同样不出钱,告知用户"后台正在处理,请稍等".

这时候 列里有两个事务产生的事件. 顺序看上面两个事务commit的次序. 加时丈夫事件1先被执行. 检查聚合跟的objectVersion,成功.通过一个新接口告知给前端钱已扣除.妻子的事件2后执行,检查聚合跟的objectVersion,失败.通过一个新接口告知给前端钱无法扣除,无法消费.

同步架构采用异步架构,整个业务流程都变了. 需要新增加一个接口.还有就是.异步事件执行可能因为网络等原因产生偶然Exception.需要有重试的机制.

总结: 同步流程采用异步后. 对于开发者和产品经理来说都更复杂了.

对于开发者来说: 需要1.保存事件 2. 重试机制 3. 新增加一个接口(即异步框架里的回调接口) 4.告知产品经理流程已变成异步化.

对产品经理来说: 需要把原来的一个同步流程思考为多个流程.

那么到底是谁决定谁呢?

开发和产品经理是互相决定,影响的. 一方面当产品经理处于用户体验的角度,可能会主动把一个同步流程,拆成多个异步流程.增加步骤和接口. 这时候开发者坑并不愿意,因为增加了工作量. 另一方面开发者处于性能,并发量的考虑,可能会把PM思考的一个同步调用改成异步. 这时候产品经理需要知道要有新的页面提醒用户"后台处理中", 流程已变.

[该贴被sinaID87521于2014-06-19 11:37修改过]

3Go 1 2 3 下一页