用户和角色多对多问题,对象间关联处理问题

最近刚开始在学习DDD,想在项目中实践下,现在在弄权限管理部分,在建立用户和角色模型时候,经过分析后得出
(1)用户对象和角色对象是对多对关系,模型中加了个关联类叫角色分配类(就是给用户分配一个或多个角色)
(2)因为没有具体业务需求,我决定在界面上在添加用户时候为该用户分配一个多个角色,把用户和分配关系当成一个聚合进行持久化,替换更新,得出下面类
用户(用户账号,密码,角色分配列表,获取所有角色方法)
角色分配(用户,角色)
角色(角色名称)
这样行吗?
(3)因为一般只会关心用户有哪些角色,而不会关心一个角色有哪些用户使用,所以角色里没有用户列表和角色分配类列表;
用户和角色分配组成一个聚合,用户是聚合根,角色分配是聚合里的值对象,这样做可以吗
(4)我在做登录验证时候,通过界面调用应用服务层的方法
Login(string userAccount, string password)
{
调用UserRepository里的FindUserByUserAccountPassword(userAccount, password)
获取User领域对象,根据是否为空判断是否通过验证
}
这个用户登录功能的实现方式对吗?
因为聚合是一个整体,一起持久化,一起更新,是否一起查询出来,如果是一起查询出来,FindUserByUserAccountPassword我就准备建个视图,把用户,角色,用户角色关联表一起关联起来,查询出来,构建出用户对象,用户关联对象,角色对象,而我现在只是验证账号和密码是否有 ,按照以前做法只需要验证用户表,所以在这里FindUserByUserAccountPassword是否只构建用户对象,而不构建聚合里的值对象就是角色关联对象,一个是查询性能好,这是否违背DDD的聚合原理?

(5)另外我如果要在用户和一个员工关联,也就是把这个用户指定给了一个员工,员工有员工姓名和员工编号,我认为员工是单独的实体,假设员工和用户是一对一关联关系,那么用户就变成
用户(用户账号,密码,角色分配列表,获取所有角色方法,员工)
员工(员工姓名,员工编号)
这样在用户里引用了员工对象应该是对的吧,现在的问题是用户和员工之间应该是没有聚合关系,当我查询一个用户领域对象的时候,根据聚合概念不应该把员工对象构建出来,但是我有需求当登录后,要在界面上显示用户对应的员工的姓名和员工工号,那我怎么去获取这个信息呢?难道分两次去读取操作,另外领域对象查询出来后的操作是在内存里操作的,而其领域内不应该涉及技术性的东西,不能在领域对象内有使用Repository的功能,看了些资料说在service(不知道是应用服务,还是领域服务)再去调用员工的Repository去读取相关信息,这样的话是不是太麻烦,还要两次读取数据库,标准做法是什么样的?
总结下,就是DDD怎么处理处理各种聚合根间的关联或者说是对象间的非聚合引用关系

请各位老师指教下,比较具体的问题,真是有点迷惑,非常感谢


2011年12月29日 23:20 "@hushawn"的内容
用户和角色模型 ...

用户和角色模型不是一种天然结构上关联关系,任何用户都可以在系统运行过程中被管理员动态分配给任何角色模型,这是一种类似服务的功能模型,不是静态类模型。

建议结合DCI来分析。

刚学DDD,DCI还不熟悉,老师能看下我其他的描述对不对

2011年12月30日 10:45 "@hushawn"的内容
刚学DDD, ...

我个人意见:前面没有说清楚,非天然静态结构关系,不适合用DDD来分析。所以,DDD什么聚合原理对你的分析都不构成约束,不在DDD边界内。

你就把用户和角色分配当做一般业务活动来分析即可。

主要个问题是关联对象的数据怎么加载,一次全部加载还是按需加载
是不是,我界面需要单个用户的及其对应员工信息时,我就从数据库读取数据,把用户(此时就加载员工关联对象?不加载用户分配列表?)构建出来到内存?再通过用户.员工.员工编号 访问,如果用户.角色分配列表 就访问不到数据

而当我需要修改用户信息时候,因为界面上需要用户及其角色分配信息,所以这时候读取数据库,把用户及其相关的角色分配对象构建出来,员工引用对象就不创建,此时如果这样访问用户.员工.员工编号就不能访问
这是不是不符合DDD的repository的思想,又变成了DAO?
但是
如果一次性全部加载关联对象,外一有很多关联对象,那性能肯定有问题

[该贴被hushawn于2011-12-30 14:37修改过]

请高手赐教下,这个怎么弄的

这是数据结构问题,不是复杂的业务问题,无需ddd屠龙刀。

根据你具体采取的存储技术,如果是neo4j等nosql就可以一次加载,而是普通关系数据库,这儿个问题交给orm等hibernate框架去解决既可,也就是按需加载,缓存和数据库结合的方式。

我是直接写SQL

数据库是SQL,而且用.net/c#

系统想用DDD,后面可能有比较复杂业务,我觉得应该整个系统应该保持一致,有些地方又不用DDD思路做

如果按需要加载,那我的Repository不是要写不同的对象构建方法吗
如某时刻领域对象User只读取了Roles列表,过会儿又需要用员工Employee信息,User.Employee这个时候再去数据库读取User的相关Employee,不是推荐领域对象不直接访问Repository吗,领域对象应该跟技术无关是吗

public class User
{
public IList<Role> Roles { get; set; }
public Employee Employee
{
get
{
从数据库加载员工信息?
}
}

}

根据原理,user跟技术相关了,肯定不行的吧

那只能根据业务需要,在Repository提供不同的User对象构建方法了,有的方法是加载User的时候同时加载关联对象Roles ,有的方法就加载关联对象Employee,有的方法两个都加载,调用哪个根据不同业务需要来决定??老师是这样的吗

另外如果在Repository一次性全部读取关联对象,根据聚合原理,用户和员工不是聚合关系,不应该一起读取出来的,虽然可能用不到DDD,但是我觉得也要从DDD思路去考虑,老师我这样有啥问题,多谢指教
[该贴被hushawn于2011-12-30 16:19修改过]

2011年12月30日 15:55 "@hushawn"的内容
不是推荐领域对象不直接访问Repository吗,领域对象应该跟技术无关是吗 ...

先搞清楚技术和思想哪个脚步快,爱因斯坦提出相对论时,当时技术条件无法验证,DDD提出这样先进思想,如果你使用SQL这样旧的技术装备当然无法满足,至少要用Entity Framework这样框架吧。

下图是DDD + CQRS + Event Sourcing的.NET落地实现架构图:



[该贴被banq于2011-12-31 08:33修改过]


这个有点复杂,我只是把系统的分页查询还有些复杂的查询单独分离出个查询子系统,数据库还是同一个,直接用存储过程查询;

另外实体间的引用,我准备参考网上资料,聚合内部实体间用对象引用(强关联),非聚合间的弱关联用ID来引用,不用对象来引用;这应该可以吧,主要是领域那部分能适用需求的变化,我们的目的应该也就是这样,不要死死追求什么完全面向对象

另外,我看了资料服务分应用服务,还有领域服务,也看了领域服务的转帐例子描述,我上面说的ID关联,我这个根据ID获取ID所关联对象的方法,不知道要写在领域服务里,还是应用服务里??

2011年12月31日 09:18 "@hushawn"的内容
我这个根据ID获取ID所关联对象的方法 ...

不能为了OO而OO是对的,但是也不能超越OO的底线,采取数据库方式,这就是混乱了,这个底线就是:对象关联必须直接引用,不能在一个对象内部直接将另外一个对象ID作为自己的字段,以Java为例子:

Class A{
int bId; //B对象的Id

}
这是不可以的。必须如下:
Class A{
B a; //直接引用b;

}

至于你如何在A中将B加载出来,这是ORM底层框架的事情,采取一次性加载或懒加载都可以,jivejdon虽然也采取SQL,但是采取Jdon框架事件机制,是在getB()方法中进行懒加载,即用即加载。

当然以上原则不适用于CQRS中直接从数据库查询Query渠道。


有个延迟加载例子,只是用了.NET里的委托技术,来实现延迟加载

public class Article {
public int ArticleID { get; set; }
public int CategoryID { get; set; }

// 文章所属分类
protected ArticleCategory category;
public ArticleCategory Category
{
get
{
if (category == null)
{
if (CategoryLazyLoader != null)
{
category = CategoryLazyLoader(CategoryID);
}
else
{
category = null;
}
}
return category ;
}
}
// 文章分类延时加载器(委托)
public Func<int,Model.ArticleCategory>CategoryLazyLoader { get; set; }
}

本来 Category 属性是直接获取分类数据(Repository),这里通过在外边注册数据获取方法来实现,这种方法有没有违反领域对象的业务与技术分离的原理,一般原理是领域对象里不能调用Repository,这里知识通过委托间接调用了,有没违反这些原则???


另外,我在创建对象时候,用一个参数来确定是否延迟加载是否可以?

另外,聚合内的非聚合根子对象是否可以延迟加载???

另外,能否同时有引用ID和引用对象,因为,我要去延迟加载的时候,如果没ID,就不知道要加载哪个对象了???
Class A{
int bId; //B对象的Id

}
这是不可以的。必须如下:
Class A{
B b; //直接引用b;
int bId;
}

老师在吗?快帮忙下

[该贴被hushawn于2011-12-31 09:56修改过]
[该贴被hushawn于2011-12-31 10:35修改过]
[该贴被hushawn于2011-12-31 11:09修改过]
[该贴被hushawn于2011-12-31 11:11修改过]