从业务角度来看,入库、出库都需要记录,删除操作自然应该放在Respository。可是Respository并不知道Entity的细节,如何执行删除操作?
难道是要Respository创建一条消息,然后将消息依次传递给相关的Entity,Entity收到消息后删除自己?
从业务角度来看,入库、出库都需要记录,删除操作自然应该放在Respository。可是Respository并不知道Entity的细节,如何执行删除操作?
难道是要Respository创建一条消息,然后将消息依次传递给相关的Entity,Entity收到消息后删除自己?
实体永远无法删除自己,实体能自己删除自己就等于一个人突然从宇宙中消失。
也许你会说,人可以自杀呀。但是我们首先要清楚人的自杀是什么含义,应该只是让自己永远没有意识,让自己认为从这个世界上已经消失。但这不表示你真的从这个世界上消失了,世界上你还在,只有在火葬场的人把你火化了,你才真正意义上的消失了,从物理学角度来讲,此时你依然没消失,呵呵,因为你的原子还在。当然这个有点扯远了。
人可以自杀是因为他可以知道该如何让自己无法呼吸,比如咬舌自尽,割脉,投河自尽,等等。那么想想人为什么能有这种自杀的能力?因为他知道自己自杀需要哪些措施和信息。比如要咬舌,他自己知道只需要让自己的舌头断掉即可,要割脉只需要那把刀让自己的血液流光即可;这里,我们可以理解为,人的舌头,血液都是人这个对象的子对象,人因为拥有这些子对象,自然就可以控制和修改它们的状态。从这点来看,我们DDD内存中的一个对象,也可以自己修改自己的状态,但是他却无法让自己从内存消失。从内存消失才是等于人从宇宙消失,对吧?所以,你觉得Entity能自己删除自己吗?显然不能!前面我说道的撤销,销毁,禁用,转移到垃圾箱,其实都只是修改了一下实体自己的某个状态而已。并不是真正的删除。
只有独立于对象之外的其他对象才有可能删除它,就像只有另外一个人才能让你从这个世界上消失一样。那么谁能把对象从内存或数据库删除?很简单,就看你想把该对象从哪里移除?如果是从数据库移除,那就让负责处理数据库的那个类(可能就是你说的repository了)来做这个事情,一条delete sql即可;如果是in-memory模式,那可能就是将对象从某个集合移除。
个人感觉这是有史以来我说明为什么对象不能自己删除自己的最生动的一次解释了吧,哈哈!
我们谈论一个对象时,我觉得更多的应该是表达”对象有哪些能力“,或者”对象有哪些行为“,这两种说法都可以。但是对象有哪些职责,容易让人误解为能有意识的主动的去行驶这些能力,实际上对象暴露在外部的能力一般都是其他对象驱动然后去行驶的,比如repository.remove(entity)这个方法,表示repository有能力从它所维护的存储空间删除/移除一个entity,但并不表示repository会主动删除一个给定的entity,实际上一定是更上层的某个对象去通知repository行驶remove的能力。
"增删改"这个词语表达全面了是:“对XXX中的数据增删改”,本质上是一种DAO模式,或者说是数据库操作思考方式,而不是围绕业务模型的操作思考方式。
"增删改"在不同的业务场景下可以用其他词语替代:如增加帖子,实际是一种post动作,然后触发一个postedEvent事件。
"增删改"是技术黑客们不管业务场景,强制总结出来的一套模式用语,他们认为不管你业务千变万化,总是要用数据库,一夫当关,万夫莫开,就像REST中POST/PUT/GET/DEL总是被人对应解释为增删改一样。
但是这种习惯思维无法改变,就别当真钻进去探究个结果来,因为问题方向本身就可能不对。
也许可以换个说法,我们可以向仓储发出请求,获取若干实体,实体负责跟踪自身状态的变化,然后将之持久化。
using(DataContext context) 初始化场景
{
Entity entity = context.Respository
entity.SetData() 更改实体状态
context.SubmitChanges() 持久化更改
}
现在问题来了,context.SubmitChanges这个消息需要经过Respository么?
第一种实现:
DataContext -> Respository(这里操作DAO) -> Entity(更新IsDirty标识或内存状态)
第二种实现:
DataContext -> Entity(这里操作DAO) -> Respository(更新IsDirty标识或内存状态)
上面的箭头表示消息的传递路径,而不是直接的调用关系
个人比较喜欢第二种实现,仓储的职责仅仅是把实体拿出来或者放回去,而不操作实体内的数据。
就像看车库的门卫,他只要登记车辆进出的状态,而不需要修理汽车的内部零件。
1.context.Respository
2.entity.SetData()将这些EntityCollection标记为无效
3.context.SubmitChanges() 提交SQL更新,这个操作是由EntityCollection调用DAO实现的
4.Respository得到EntityDeleted的消息
此处为了省略,使用PendingChanges实体类替代消息,ISubmitable接口替代消息处理器。
|
|
|
此处为了省略,使用EntityIdentifier代替查询对象
|
|
|
|
图书的实体集合,容纳一堆图书
|
|