qiuriyuchen
2011-12-29 11:12
2011年12月29日 10:33 "@tangxuehua"的内容
你这样就无法确保数据库是一致的了。比如两个人同时修改一个订单,都往订单中增加一个OrderItem,虽然他们每个人在内存中的订单都是符合不变性的,比如没有超出订单总额。但是如果只是保存变动的部分到数据库,即每个人都把新增的那个OrderIt ...

对于不同用户来说,一致性是通过锁来控制的呀。如果想两个人修改订单不冲突就得给这个订单加锁啊,这个和全部保存还是部分保存好像没有关系,只和锁的粒度有关吧?

以前我们公司在多用户问题上都是入库前判断,后面的不满足规则就不入了,提示发生错误。没有用锁,因为多用户同时编辑的机率比较小。

tangxuehua
2011-12-29 11:28
2011年12月29日 11:12 "@qiuriyuchen"的内容
对于不同用户来说,一致性是通过锁来控制的呀。如果想两个人修改订单不冲突就得给这个订单加锁啊,这个和全部保存还是部分保存好像没有关系,只和锁的粒度有关吧?

以前我们公司在多用户问题上都是入库前判断,后面的不满足规则就不入了,提示发生错误。没有 ...

内存中根本不是同一个内存对象啊。两次HttpRequest请求,会导致同一个订单在内存中有两份。用所解决不了问题。

总之,我觉得你是在逃避聚合的定义,纯粹为了方便而只保存聚合的部分。与其这样,还不如不设计聚合,让所有实体都能单独保存即可。在想想Aggregate is a unit of data changes吧。

qiuriyuchen
2011-12-29 17:43
谢谢你的回复。

锁怎么会解决不了呢,这和request请求没有关系,和数据库有关系啊,第一个用户修改时用select for update nowait来锁定订单,然后第二个用户进来想修改这个订单时,用select for update nowait就会不能修改,订单只所以能被两个人同时修改,和锁当然有关系了。

我不是逃避聚合,是在考虑它帮编程中解决了什么问题,我也是不相信权威,知道了它的好处我才会信,现在也正在学习,学习的过程就是要疑问并证实的过程,我的意思是两个人不能同时修改一个订单是要靠锁来解决的,和一起保存没关系,就算是我用部分保存,也可以先锁定这个订单。

我的观点是聚合是用来保持业务不变性,但不应该是一定每次更新部分都要从根全部再保存一遍。我同意边界内的对象要通过根导航来找到。

还有一个怪我没说清楚,我现在在做一个小项目来验证领域建模能提供什么好处,用的是javafx做的桌面程序,所以在内存中是一份数据,不好意思上次回没表达清楚。

[该贴被qiuriyuchen于2011-12-29 17:44修改过]

tangxuehua
2011-12-29 18:11
2011年12月29日 17:43 "@qiuriyuchen"的内容
锁怎么会解决不了呢,这和request请求没有关系,和数据库有关系啊,第一个用户修改时用select for update nowait来锁定订单,然后第二个用户进来想修改这个订单时,用select for update nowait就会不 ...

你说的锁是锁数据库吧?我以为是锁内存对象。如果是锁数据库,那是可以确保数据库的数据不会违反不变性的;

但是你是如何只保存聚合的部分的呢?在DDD中,都是通过仓储Repository来保存聚合的,比如orderRepository.SaveOrder(order);那么,对于OrderRepository来说,又如何知道当前只要保存order的一部分呢?我觉得对于OrderRepository来说,当它接收到一个Order后,要么全部将Order最新状态直接覆盖到数据库,要么先检查Order内部有哪些改动点,然后再保存;

难不成你会提供orderRepository.SaveNewOrderItem(newOrderItem)这样的方法?那这个orderRepository还是一个repository吗?

qiuriyuchen
2011-12-29 18:55
终于知道我在说什么了,哈哈,这正是我想和你讨论的呀,我的意思就是没有必要全部一起保存,部分保存就可以了。先说在单机程序中吧,如果客户端持有的是副本,我觉得可以像这样order.EditOrderItem(orderItem),因为orderItem是副本,所以应该先执行逻辑判断,再更新order聚合中的orderItem,然后再调用orderRepository.EditOrderItem(orderItem)这样的方法保存到数据库。 保存方法放在哪以及实体可否调用repository可以再讨论,先把这个讨论清了啊。这样内存和数据库也是一致的,也没破坏不变性啊。所有我才有还不如直接持有一份,这样就可以省了更新聚合内实体这一步,因为更新的就是聚合内的实体。我做的是单机版程序,所以聚合的整个生命周期比较长,整个交互过程只加载了一遍整个聚合。部分更改后,我就是更新变更部分的内存对象,再同步这部分到数据库(部分更新);

引用你的话:难不成你会提供orderRepository.SaveNewOrderItem(newOrderItem)这样的方法?那这个orderRepository还是一个repository吗?

没有人说Repository不可以修改里面的实体啊,只说了不能直接加载非根实体给客户端。

如果是Web应用的话,客户端持有一个实体的副本,这时向服务器端提交修改,要传回第一个是修改后的实体副本,还有一个参数要传回就是它所属的Order的Id,这时服务器端你应该不会把原来加载的聚合放Session吧,这时还得加载整个Order的聚合,然后再把里面的OrderItem按照传回的副本更新成新的(当然中间要加入逻辑),如果按照全部保存的说法,然后再保存整个Order聚合,我的意思是最后的一步完全可以只保存更新的部分就可以了,没有必要全部保存。结果和全部保存是完全一致的,不管是内存里的对象还是数据库里的记录。

可以再讨论一下这个方法放在哪,我觉得最合适放的地方就是Order类中加一个editOrderItem(OrderItem orderItem)的方法。它完成逻辑判断后再调用OrderRepository,关于实体可不可以调用Repository或服务可以参考http://www.jdon.com/jivejdon/thread/37289Domain Events – 救世主的文章,可以用领域事件来做,对那个观点我也是有些不一样的看法的,已经发在那个的评论里了。

里面说的实体,指的是聚合内的实体。

[该贴被qiuriyuchen于2011-12-29 19:00修改过]

[该贴被qiuriyuchen于2011-12-29 19:05修改过]

猜你喜欢