领域驱动设计--小需求的疑问。望大神帮忙。

目前需求:1.用户可以关注店铺
2.店铺每天都有动态信息推送到系统中
3.用户可以查看最新动态(当然是自己关注店铺的动态列表)
其他的需求不继续详细说明。

我根据需求画了usercase 概括了需求内容。

我的分析的实体如下:

存在的问题:
1.用户进来之后要看自己关注过的店铺动态,因为动态是属于店铺实体的。那这样的话,关注-->店铺-->动态,按照这种方式来查询数据了。貌似有些不拖。因为动态数据多的话需要分页。
如何将 关注 和 动态 比较好的建立关系? 或者这部分数据是需要通过领域之外的方式来查询处理?
2.一直存在的困惑,就是持久化操作,比如说这个需求里面 的店铺有动态,那么我的店铺可以增加个动作来删除动态,那这操作之后数据持久化怎么做?领域操作的对象难道要在自己的领域方法里面直接调用持久化的 respository么?还是通过消息通知方式来做?如果消息通知的方式来做持久化操作,那如果保证事务完整性。

2013-02-28 12:51 "@zpp2025
"的内容
关注 和 动态 比较好的建立关系? 或者这部分数据是需要通过领域之外的方式来查询处理 ...

首先,领域模型的高聚合关系是反映业务领域本质的旋律,当然,如何实现方便的查询也是考量之一,查询是一种只读操作,关系主要为写操作而设计,读操作可以借助直接对Repository查询。

至于第二个问题,使用消息带来事务问题,引入Event Sourcing,也就是将每个事件持久化保存,必要时进行回滚播放追溯,这种方式可以替代传统刚性的使用架构技术的事务,Event Sourcing是一种偏业务的非常自然的事务。

感谢@banq 的回复
我的活跃店铺量在200W左右,如果店铺下面直接挂入动态。每天就算是1个动态。也至少200W个店铺动态。
这样的话我在数据持久化方面如果使用数据库存储势必会做分表。如果做分表就是使用店铺id作为分表的字段。

1.那这样就存在一个查询问题。一个登陆用户要看他所有关注的店铺的动态信息。登陆用户-->店铺动态信息。如果查询就存在跨表的合并数据问题,这样查询效率肯定不可取。
2.那这样的话,我会重新设计一个实体出来,关注动态? 来以人的维度来存储关注店铺的动态信息。如果这样做就会造就出非常多的冗余数据出来。A,B,C 三个人都关注了店铺,店铺如果有了动态消息之后,按照这样的设计就会分发给A,B,C三个人每人一条消息,那假如一个店铺被关注超过10w,那岂不是光一个店铺就要发送10w条消息出去,这样的设计势必造成很大的系统压力和数据冗余。

所以banq老师,有木有比较好的方式处理这种业务模型
[该贴被zpp2025于2013-02-28 14:15修改过]
[该贴被zpp2025于2013-02-28 14:16修改过]

2013-02-28 14:11 "@zpp2025
"的内容
所以banq老师,有木有比较好的方式处理这种业务模型 ...

你这个问题是因为动态信息量大引起的,这里我们先分辨一下店铺动态信息如果设计为值对象,也就是一旦发布,不允许修改,那么我们可以通过值对象共享的方式分享给那些关注的人,可以让关注者不断访问内存中店铺状态,如果一旦有新的动态信息,关注者进行读取即可。

从你的用例图可以看出,显然不是小需求,而是大系统。

从领域驱动设计的角度看,用例图分析中的界(也就是领域)的划分不太明显,这样可能对系统可扩展性不利。

关于商铺的动态信息,其实就是一个商铺报表。如果做成实体,显然是一种数据库级的数据冗余。如果直接放到内存里,查询速度应该没有问题,当然还可以考虑缓存复制。一般流量下,内存更新是很快的;如果流量大,可以考虑内存分区和集群。



[该贴被flyzb于2013-03-02 11:09修改过]

@flyzb
如果从存储角度去设计这个的存储 已经有了解决方案。
我们使用了分布式存储 hbase搭建的一个分布式存储。来做这个存储。查询效率完全可以保障。

但是关键就是使用了领域设计的角度去从看这些需求之后,我就有些疑惑,这些大数据量的问题。
我之前的设计的 关注-->店铺-->店铺动态。

按照这样的设计 就会有我之前说的 数据查询问题,人--店铺动态的查询问题。对于 @banq 说的值对象的共享设计。鄙人领域方面正在学习,不是特别的明白。

2013-03-04 13:44 "@zpp2025
"的内容
值对象的共享设计。鄙人领域方面正在学习,不是特别的明白。 ...

其实值对象共享实际就是内存状态共享,你之前思路是面向消息方向,考虑到其值对象以后,值对象既可以是共享,也可以是复制式,如同一个房间有很多插座,如果采取一个对象一个实例,实例数量非常大,耗费内存,采取共享以后一个内存只有一个,如果你采取分布式内存如Hbase,则可以直接实现。

DDD是讲究分析和设计和二为一的,值对象作为一个桥梁,既链接了需求分析,又链接了存储设计实现。值对象打通了任督二脉。

相关主题:为什么领域建模

嗯,看了一下
主要说说我对这里查询的想法

我在自己实践DDD的过程,对非业务性的读,并不会走仓储,也不会通过根一个一个到达的方式去获取信息。

DDD是为业务考虑的,只是为了读的话,你可以自由发挥,通过直接性的查询会更好点。

有关分布式和效率的问题,实践甚少,不敢谈论

2013-03-04 14:33 "@tbyes
"的内容
对非业务性的读,并不会走仓储 ...

是这样,传统DB和内存是分离的,而像NoSQL这类分布式存储,其内部实际是分布式内存+分布式存储,所以,使用HBase等实际又能为值对象在内存中的修改提供支持,又能直接将内存中数据持久化到磁盘,一举两得。

2013-03-04 14:47 "@banq
"的内容
像NoSQL这类分布式存储,其内部实际是分布式内存+分布式存储,所以,使用HBase等实际又能为值对象在内存中的修改提供支持,又能直接将内存中数据持久化到磁盘,一举两得。 ...

是的,传统的DB中,还需要来做一份中间缓存,有了缓存,又牵扯到了“一致性”。而NOSQL是基于内存的,且大部分都有自己的持久化机制,不需要我们过问,所以让NOSQL来做DDD的后勤保障最好不过了。毕竟DDD的特性就得In-Memory。

banq,我的导师利奥和我常常提起你们对DDD的讨论,他会和我分享下心得,我也希望有一个您即时的联系方式,如果您不介意的话,我会常常“打扰”您的。

2013-03-04 15:06 "tbyes
"的内容
的导师利奥和我常常提起你们对DDD的讨论 ...

@brighthas 原来是导师啊,他的Node.js CQRS框架越做越牛了:node.js的 Domain framework JSDM

希望能够在Jdon经常和你们讨论,我不太常用即时工具,时间无法自己支配,被电脑驱使了,哈哈。

@tbyes @banq
对于读写分离 的方式就是为了保持模型的干净,不让模型以外的东西污染。
但是比如说:我现在的模型 关注-店铺--动态
其实需求里面就有 查询这个人关注店铺的动态。这个就是一个,先从人找到关注,在从关注的店铺里面找到 店铺动态。
问题1:对于值对象,难道就是我设计的店铺动态的存储方案?(存入habse集群),把之对象当做共享对象了。全部都存储在一个地方。查询的时候按照不同实体的id 来获取值对象?

问题2:需求?到底什么是领域模型中的业务,一个有关的查询就不算了么?设计到复杂的查询也就不设计到模型中,只有状态,行为的变化才算领域模型的行为?

2013-03-04 15:52 "@zpp2025
"的内容
问题1 ...

两个问题答案基本是“yes"。对于动态信息发布和查询这个功能,也算是业务,因为动态信息作为一个值对象,是其父对象实体店铺的一个状态,当一个店主发出新的动态信息,将激活领域模型实体店铺相关动态信息发布方法。

以Jdon分析法图分析:

实体是你的店铺实体,状态是动态信息值对象,当店主发布新信息命令(点按UI界面上发布按钮),命令Command将在场景中转为domain Event,event激活店铺实体的发布方法(比如: public void publish())。

而在发布方法内部,则是创建一个新的动态信息:

public void publish(){

//1.创建一个新的动态信息值对象
this.messageState.content = new Message();

//2.修改动态信息标识,让外部知道状态变化,有新的信息了。
this.messageState.hasNewMessage = true;


}


[该贴被banq于2013-03-04 16:23修改过]

感谢@banq
当然放入缓存或者其他存储方式(hbase分布式存储)。大家都可以共享到这些状态的变化。通过event驱动方式。这样值对象的共享直接节省掉了冗余的数据存储,让数据更加简单,也让领域模型清晰。

可是,这个就需要带来一种新的设计--消息。
人关注的店铺动态,变成消息通知的方式告知用户,你关注的店铺有新的动态。那这样算不算这种复杂关系处理的折中方式呢?而且消息也会累积的。数据会不断增长。最可怕的是一个店铺被10W的粉丝关注,一旦有数据出来,这个消息分发岂不是一次性要分发10w,如果店铺动态一天变化100次这个量级别......

按照你说的动态可以被共享,那我消息被持久化之后,只不过存储的是共享的属性值。但是消息本身却构成了巨量的数据。

2013-03-04 16:53 "@zpp2025
"的内容
人关注的店铺动态,变成消息通知的方式告知用户 ...

不需要消息告知用户,而是让用户定时读取店铺信息状态,也就是hasNewMessage 这个字段是否为真。这样节省了发送大量通知消息。

这也是面向消息和面向共享内存两者区别。一个是push推,一个Poll拉。