一次关于DDD的讨论 从中看我对DDD的疑惑 (再次请banq解惑)
首先感谢@banq上次的回复和具体实例 但是我不得不说。。 理论性太强了落实到代码层面 还是不知道如何去做。 这几天也在看书 和 讨论 以下的讨论也恰是我对DDD所有的疑问 ,请banq 和 各位有 DDD实践的同行 帮忙看一下。 因为我觉得思维进入了死胡同了, 估计不仅是我,一定很多人有这些问题
===================================================
回答者 16:22:24
你目前重构的项目是:java的吗?
提问者 16:22:47
是啊!
提问者 16:22:51
急切啊!
回答者 16:23:30
一张表、一个实体类,一个DAO
回答者 16:23:43
目前是这种模式吗?
提问者 16:24:33
对啊!差不多
提问者 16:24:56
目前就是贫血那种 一个模块 一个service 一个DAO
提问者 16:25:00
可能多个表
提问者 16:25:05
一个或两个
回答者 16:25:09
问题不大!
提问者 16:25:18
使用hibernate DAO会每张表一个实体类
回答者 16:25:27
UI层的展示用的啥模型
提问者 16:26:01
UI就是这样 , struts Action 控制器里面 会调用service代码
提问者 16:26:16
调用完业务方法就跳转
回答者 16:26:38
渲染界面,返回的是实体类吗?
回答者 16:26:45
还是DTO
提问者 16:27:44
界面采用的是绑定技术 就是在action控制器里面定义model类, service为model类赋值的话, JSP标签会自动显示表单数据
提问者 16:28:05
如果不采用绑定的话, 应该需要拼DTO 或者VO类
提问者 16:28:14
就是一个数据对象
提问者 16:28:19
或者数据对象的List
提问者 16:28:26
现在是这样的。。
回答者 16:28:37
这样也挺好的,只要能满足要求。
回答者 16:29:11
一个思路:
1、确定目前项目的分层。
2、确定目前每个层的元素及其职责与实现技术。
3、确定目前层与层之间的交互方式(传入和传出的都是啥)。
回答者 16:29:47
下一步是:
1、确定目标项目的分层。
2、确定目标每个层的元素及其职责与实现技术。
3、确定目标层与层之间的交互方式(传入和传出的都是啥)。
回答者 16:30:44
横切面都是通过AOP实现的吧?
回答者 16:31:40
有用spring吗?
提问者 16:32:46
有啊
提问者 16:32:52
AOPspring 都有
提问者 16:33:06
就是一般贫血哪些东西 都有
提问者 16:33:32
我有个重构思路 说来你看看属于不属于DDD的思维啊
回答者 16:33:43
好,一起聊聊!
提问者 16:34:30
因为不同模块已经定了, 所以针对不同模块进行重构, 基本上这样 , 一个模块一个模块来重构
提问者 16:35:21
现在每个模块结构都一样 action service dao model
提问者 16:35:26
还有表
提问者 16:35:38
这个是老的
回答者 16:35:39
1、如何处理跨模块通信(API和SPI的问题)?
回答者 16:35:47
您接着说
回答者 16:36:01
我先把我的问题提出来,后面再找答案!
提问者 16:36:03
好
提问者 16:36:16
然后 谈一下重构
提问者 16:36:31
先把当前重构的模块 抽取出 它的领域模型
提问者 16:36:40
模型包含 业务属性和业务方法
提问者 16:36:56
建一个领域层
提问者 16:37:16
说白了就是加一个domain包, 把领域层对象放进去
回答者 16:37:45
2、如何组织领域模型(领域模型需要丰富的对象关系(对象图))?
3、是否还有原来的贫血模型?
提问者 16:38:49
我先说一下我的 新结构的思路, 然后 再找答案哈
回答者 16:38:54
好的!
提问者 16:38:56
嗯
回答者 16:39:04
我也是先写下来!
回答者 16:39:09
您尽管说
提问者 16:39:08
嗯
提问者 16:39:58
然后领域层有了 接下来在action里面 把 包含领域层业务的 老的service 剔除掉, 替换成领域对象的业务方法
提问者 16:40:48
如果一个领域模型的业务方法不能满足业务需要多个的话,建一个service 在service里面 调用 领域模型方法
提问者 16:41:19
action调用的service 方法 也都是多个领域模型方法的集合
提问者 16:41:39
里面涉及到数据库的增删改查, 因为是采用hibernate
提问者 16:42:20
老的model类 是hibernate的必需品, 针对的是表, 一张表 一个model类, 类似于数据对象
提问者 16:43:14
如果是查询 可以在DAO 查询后 , 对领域模型进行赋值, 如果是修改删除操作 也直接把领域对象传给DAO 让hibernate处理数据库操作
提问者 16:43:55
这个是我的思路, 其实 说白了 就是 把service的业务方法, 换成了 领域模型的方法。。
回答者 16:44:11
我来总结一下,您看对不对?
提问者 16:44:29
好的
回答者 16:46:05
领域层:领域模型、领域服务、仓储接口。
持久层:数据模型、仓储实现、DAO(Hibernate实现)
回答者 16:46:37
这两个层准备组织成这种模式吗?
提问者 16:46:46
有一点点差别
提问者 16:47:16
我的思路里 只有一个DAO 访问数据库
仓储接口和仓储实现 是没有的
提问者 16:47:30
因为我不大清楚 仓储接口和实现 的作用是?
回答者 16:48:33
或者我问个问题?
提问者 16:48:54
嗯 好
回答者 16:48:57
如何创建领域模型?
提问者 16:49:06
我的想法就是 直接new 了。。。
回答者 16:49:24
1、第一此创建
2、从数据库创建
回答者 16:49:41
引入仓储就是应对第2个场景。
提问者 16:49:53
比如 class UserAction {
doXXX(){
UserDomain u = new UserDomain();
}
}
提问者 16:50:01
我的想法停留在这里。。。。
回答者 16:50:02
领域模型可能包含的层次比较多。
回答者 16:50:09
是一个大的对象图
回答者 16:50:22
这个职责需要统一在一个地方。
提问者 16:50:55
这个还是难以理解
提问者 16:51:11
比如 要统一在哪里创建领域对象吗
回答者 16:51:23
是的
回答者 16:51:27
1、第一此创建(工厂)
2、从数据库创建、保存(仓储)
提问者 16:51:37
比如 传统的编程语言 是无法躲开 new 实例对象的啊
回答者 16:51:45
我的建议是引入:工厂和仓储。
回答者 16:51:53
如果简单的对象可以new
回答者 16:52:10
考虑这样一个场景,我这边是有这样的例子的。
提问者 16:52:15
嗯!
回答者 16:53:08
考核方案
--评分规则列表
--系数规则列表
--周期规则表列
回答者 16:53:37
完成考核方案的启动,需要同时加载(创建)这些对象。
回答者 16:54:10
如果不引入仓储统一这种组装过程,每个开发人员都要重复这种组装过程。
回答者 16:54:41
class 考核方案仓储
{
考核方案 GetById(方案Id)
}
回答者 16:55:08
为“根”领域对象引入仓储。
回答者 16:55:38
评分规则这些根对象之下的实体是不需要引入仓储的。
提问者 16:56:10
我理解一下
回答者 16:57:06
系统有三部分逻辑:
读取:渲染UI
CUD:组装领域模型的过程
计算:执行和使用领域模型的过程
回答者 16:57:37
我的建议是将这三种职责分开,定义不同分层策略和组织方式。
提问者 16:58:16
针对这个业务
回答者 16:59:02
针对你目前的情况:
回答者 16:59:25
先为读取逻辑定义一个分层策略和组织方式。
提问者 17:00:08
我说说对考核这个业务的理解吧
回答者 17:00:11
你目前的数据模型之间有导航属性吗?
回答者 17:00:24
好的
提问者 17:00:33
考核方案
--评分规则列表
--系数规则列表
--周期规则表列
完成考核方案的启动,需要同时加载(创建)这些对象。
我的理解是 考核方案就是三个列表的显示吧
提问者 17:00:56
就是查询 评分规则、 系数规则、 周期规则 这三项
提问者 17:00:57
对吧
回答者 17:01:17
对
提问者 17:01:43
那么领域模型 会存在 评分、 系数 、 周期 三者
提问者 17:02:09
还有考核方案 这个是领域模型的根
回答者 17:02:13
考核方案有一个"计算逻辑",计算考核分数,内部计算使用 评分、 系数
提问者 17:03:09
就是 在 考核领域对象里面 计算 评分和系数 把最终的数据返回?
回答者 17:03:17
对
回答者 17:03:49
计算逻辑:需要创建一个考核方案,考核方案的计算过程需要:评分和系数
提问者 17:04:20
那评分和系数 算不算领域模型呢?
回答者 17:04:39
算
回答者 17:04:45
不是聚合根
回答者 17:04:47
你目前的数据模型之间有导航属性吗?
提问者 17:04:57
啥是导航性啊
回答者 17:05:11
User
{
Roles
}
提问者 17:05:18
就是关联关系吗
回答者 17:05:22
对
提问者 17:05:25
一对多 多对多
提问者 17:05:26
有啊
提问者 17:06:11
然后继续哈
提问者 17:06:35
我拿考核系数 说我的思路吧 先从最小的说
回答者 17:06:38
这样你就有领域模型了
提问者 17:06:49
你看看我这个思路对不
回答者 17:07:18
你先说
提问者 17:08:42
考核方案,考核方案的计算过程需要:评分和系数
建立领域模型
class 考核方案{
评分 p;
系数 x;
public int 计算考核方案(){
int result = 0 ;
result += x.计算();
result += p.计算();
return result;
}
}
提问者 17:08:49
是这个意思吗
回答者 17:08:58
对
提问者 17:09:29
class 考核方案{
评分 p;
系数 x;
public int 计算考核方案(){
int result = 0 ;
p = new 评分;
x = new 系数;
result += x.计算();
result += p.计算();
return result;
}
}
提问者 17:09:46
p = new 评分;
x = new 系数;
在方法体里面new出来领域对象的实例
回答者 17:10:35
这种不行吧!评分需要从数据库中加载的。
回答者 17:10:54
领域层:领域模型、IDAO、IRepository
。
持久层:DAOImp,RepositoryImp
回答者 17:11:00
新的思路
提问者 17:11:24
好的 问题出现了 我改进一下 你帮我看一下对不啊~
回答者 17:11:35
好的
提问者 17:13:39
建立 评分DAO{
public 评分 读取评分(){
select * 。。。。
}
}
然后 在计算考核方案方法体里
class 考核方案{
评分 p;
系数 x;
评分DAO dao;
系数DAO dao;
public int 计算考核方案(){
int result = 0 ;
p = new 评分;
x = new 系数;
p = 评分DAO .查询评分();
x= 系数DAO .查询系数();
result += x.计算();
result += p.计算();
return result;
}
}
提问者 17:13:55
我目前的思路。。。
提问者 17:14:06
但是我觉得我这个思路不对。。
回答者 17:14:08
这个是服务,不是领域模型。
提问者 17:14:18
是啊 , 问题就在这里。。。。
提问者 17:14:22
不知道咋整了
回答者 17:14:26
CRUD的入口为IDAO
领域计算逻辑时需要加载领域对象,计算完需要持久化,这两种职责交给:IRepository
回答者 17:14:31
这样行不?
回答者 17:14:59
比如:如果系统不需要计算逻辑,只有CRUD,我没必要用DDD的。
回答者 17:15:33
如果除了CRDU之外,需要其它的“按钮”,我才引入DDD。
回答者 17:16:02
用户的逻辑很复杂,但是其创建过程就是CRUD。
回答者 17:16:24
至于其权限验证、等于认证,都是等CRUD完了,才能起作用的。
回答者 17:17:15
因此我觉得:CRUD和DDD同时使用才是信息系统开发的一种可行方式。
提问者 17:18:02
加载领域对象 的 含义 是指 从数据库查询数据并对领域对象赋值吧
回答者 17:18:19
因为你的数据对象已经有关系了
回答者 17:18:37
所以:你只需要按聚合来设计仓储即可。
回答者 17:18:52
比如:
回答者 17:19:18
User
{
Roles
IsInRole(string roleName);
}
提问者 17:19:50
我想想。。
回答者 17:20:09
需要的就是:划分聚合、识别聚合根、一个聚合设计一个仓储。
提问者 17:20:26
那 user 这个例子 user 是聚合跟吗
回答者 17:20:44
是的,这个例子很明显!
提问者 17:20:57
聚合跟的作用是啥?
回答者 17:21:17
拿那个考核方案为例:
提问者 17:21:20
嗯
回答者 17:21:42
评分规则必须隶属一个考核方案,单独加载出来没有意义。
回答者 17:22:03
划分聚合和划分模块一样
提问者 17:22:32
我想想 我应该如何改造刚才的代码
提问者 17:22:38
按照聚合的思路
回答者 17:22:49
一个聚合专注与完成一些功能内聚的任务。
回答者 17:23:04
还拿考核方案为例吗?
提问者 17:23:09
是啊
提问者 17:23:12
这个例子好!
回答者 17:23:25
我先写吧!
提问者 17:23:27
好!
提问者 17:23:46
似乎有点感觉了
回答者 17:23:59
首先是领域模型
考核方案
{
评分规则列表
系数规则列表
}
提问者 17:23:59
这个有一点像 对现实世界的抽象
回答者 17:24:16
这个模型Hibernate应该支持
提问者 17:24:31
嗯 典型的 一对多
回答者 17:24:57
I考核方案DAO
I评分规则DAO
I系数规则DAO
回答者 17:25:05
定义三个DAO,可以用泛型。
回答者 17:25:51
先不考虑计算之类的逻辑,有这些类,是不是足以完成CRUD之类的界面了?
提问者 17:26:50
是啊 这些 hibernate就完成了
回答者 17:27:15
现在有一个要求:开发一个“计算”按钮
回答者 17:27:48
为此:引入
I考核方案Repository
回答者 17:28:20
修改考核模型:
考核方案
{
评分规则列表
系数规则列表
计算();
}
回答者 17:28:34
只为聚合根引入仓储
提问者 17:29:05
嗯 考核方案就是 聚合根吗
回答者 17:29:10
是的