这样的引擎,还是很空的,试着对某些具体方面提出解决方案看看,如权限,如业务,这些将如何实现呢?以OO思维来思考:假设这样你引擎已经设计好(接口可以调用),你如何表述到一个真实的系统上呢?若果只是单纯解关系的话,将会失去意义了。简单来,拿登录和转账来说说?

2011年04月26日 11:58 "@SpeedVan"的内容
简单来,拿登录和转账来说说? ...

那我就拿转帐来说说吧:
先描述一下转帐的业务需求:
1)源账户扣除转账金额,当然首先需要先判断源账户余额是否足够,如果不够,则无法转账;
2)目标账户增加转账金额;
3)为源账户生成一笔转账记录;
4)为目标账户生成一笔转账记录;

好了,如果从“对象+关系引擎+消息机制”的角度来设计这个需求的话,我觉得可以这样:
1)用BankAccount表示银行帐号类,用EventProcesser表示关系引擎,用transferEvent表示转帐的消息;
2)创建并发送一个转账消息给关系引擎,该消息包括源帐号,目标帐号,转帐金额,转账日期。示例代码如下:
EventProcesser.ProcessEvent(new TransferEvent(bankAccount1.Id, bankAccount2.Id, 1000, DateTime.Now));
3)关系引擎智能的解析该消息,然后自动获取源帐号对象和目标帐号对象,然后通知它们分别响应该消息;对于源帐号对象来说,它会响应该消息并做“转出钱”的操作,并且给自己创建一笔转帐记录;对于目标帐号来说,它会响应该消息并做“转入钱”的操作,并且给自己创建一笔转帐记录;

理想情况下,只要做这三步就可以实现转帐需求了。但实际上我们的关系引擎没有足够智能,它无法智能地分析这个转帐的消息。所以,我现在的做法是需要预先在程序启动时告诉关系引擎:转帐消息和该消息的响应者之间的关系,比如如何从该消息中提取源帐号对象和目标帐号对象,以及需要调用源帐号对象与目标帐号对象的哪个响应方法。

以下是我实现的具体代码(C#),奇怪,为什么jdon网站只支持插入java代码呢?难道是对C.NET有歧视,呵呵:


1 public class TransferEvent : DomainEvent //转帐消息
2 {
3 public TransferEvent(Guid fromBankAccountId, Guid toBankAccountId, double moneyAmount, DateTime transferDate)
4 {
5 this.FromBankAccountId = fromBankAccountId;
6 this.ToBankAccountId = toBankAccountId;
7 this.MoneyAmount = moneyAmount;
8 this.TransferDate = transferDate;
9 }
10 public Guid FromBankAccountId { get; private set; }
11 public Guid ToBankAccountId { get; private set; }
12 public double MoneyAmount { get; private set; }
13 public DateTime TransferDate { get; private set; }
14 }
15 public class BankAccount : DomainObject<Guid>
//银行帐号类
16 {
17 region Private Variables
18
19 private List<TransferHistory> transferHistories;
20
21 endregion
22
23 region Constructors
24
25 public BankAccount(Guid customerId)
26 : this(customerId, 0D, new List<TransferHistory>())
27 {
28 }
29 public BankAccount(Guid customerId, double moneyAmount, IEnumerable<TransferHistory> transferHistories)
30 : base(Guid.NewGuid())
31 {
32 this.CustomerId = customerId;
33 this.MoneyAmount = moneyAmount;
34 this.transferHistories = new List<TransferHistory>(transferHistories);
35 }
36
37 endregion
38
39 region Public Properties
40
41 public Guid CustomerId { get; private set; }
42 [TrackingProperty]
43 public IEnumerable<TransferHistory> TransferHistories
44 {
45 get
46 {
47 return transferHistories.AsReadOnly();
48 }
49 }
50 [TrackingProperty]
51 public double MoneyAmount { get; private set; }
52
53 endregion
54
55 region Event Handlers
56
57 private void TransferTo(TransferEvent evnt)
//源帐号响应转帐消息(做转出钱的操作)
58 {
59 if (this.Id == evnt.FromBankAccountId)
60 {
61 DecreaseMoney(evnt.MoneyAmount);
62 transferHistories.Add(
63 new TransferHistory(
64 evnt.FromBankAccountId,
65 evnt.ToBankAccountId,
66 evnt.MoneyAmount,
67 evnt.TransferDate));
68 }
69 }
70 private void TransferFrom(TransferEvent evnt)
//目标帐号响应转帐消息(做转入钱的操作)
71 {
72 if (this.Id == evnt.ToBankAccountId)
73 {
74 IncreaseMoney(evnt.MoneyAmount);
75 transferHistories.Add(
76 new TransferHistory(
77 evnt.FromBankAccountId,
78 evnt.ToBankAccountId,
79 evnt.MoneyAmount,
80 evnt.TransferDate));
81 }
82 }
83
84 endregion
85
86 region Private Methods
87
88 private void DecreaseMoney(double moneyAmount)
89 {
90 if (this.MoneyAmount < moneyAmount)
91 {
92 throw new NotSupportedException(
"账户余额不足。");
93 }
94 this.MoneyAmount -= moneyAmount;
95 }
96 private void IncreaseMoney(double moneyAmount)
97 {
98 this.MoneyAmount += moneyAmount;
99 }
100
101 endregion
102 }
103 public class TransferHistory : ValueObject
//转帐记录,是一个值对象
104 {
105 region Constructors
106
107 public TransferHistory(Guid fromAccountId,
108 Guid toAccountId,
109 double moneyAmount,
110 DateTime transferDate)
111 {
112 this.FromAccountId = fromAccountId;
113 this.ToAccountId = toAccountId;
114 this.MoneyAmount = moneyAmount;
115 this.TransferDate = transferDate;
116 }
117
118 endregion
119
120 region Public Properties
121
122 public Guid FromAccountId { get; private set; }
123 public Guid ToAccountId { get; private set; }
124 public double MoneyAmount { get; private set; }
125 public DateTime TransferDate { get; private set; }
126
127 endregion
128
129 region Infrastructure
130
131 protected override IEnumerable<object> GetAtomicValues()
132 {
133 yield return FromAccountId;
134 yield return ToAccountId;
135 yield return MoneyAmount;
136 yield return TransferDate;
137 }
138
139 endregion
140 }

//创建消息和发送消息的代码:
EventProcesser.ProcessEvent(new TransferEvent(bankAccount1.Id, bankAccount2.Id, 1000, DateTime.Now));

//创建关系并将关系告诉关系引擎的代码:
RegisterObjectEventMappingItem<TransferEvent, BankAccount>(
2 new GetDomainObjectIdEventHandlerInfo<TransferEvent>
3 {
4 GetDomainObjectId = evnt => evnt.FromBankAccountId,
//告诉框架源银行帐号对象是根据消息的FromBankAccountId作为唯一标识来获取的
5 EventHandlerName =
"TransferTo" //告诉响应的方法
6 },
7 new GetDomainObjectIdEventHandlerInfo<TransferEvent>
8 {
9 GetDomainObjectId = evnt => evnt.ToBankAccountId,
//告诉框架目标银行帐号对象是根据消息的ToBankAccountId作为唯一标识来获取的
10 EventHandlerName =
"TransferFrom" //告诉响应的方法
11 }
12 );

如果你要看我具体的代码,可以具体参考我在博客园的一篇文章,那里讲的比较具体。http://www.cnblogs.com/netfocus/archive/2011/04/17/2019152.html
[该贴被tangxuehua于2011-04-26 13:08修改过]
[该贴被tangxuehua于2011-04-26 13:14修改过]

2011年04月26日 10:16 "@banq"的内容
Java中就很成功应用了RBAC,将角色权限和特殊业务进行了分离 ...

从DCI的角度看角色和权限会更加灵活。权限是有业务决定的,不可改变,但是角色是可变的,因为不同的管理环境下,角色场景及其划分不同,这些角色拥有的权限也不同。这也就是说场景决定角色,决定权限的不同组合。

2011年04月26日 13:02 "@tangxuehua"的内容
对象+关系引擎+消息机制 ...

这个感觉就是消息总线,你所提到的关系引擎,就是对象对消息的关注注册。其实总体来说就是消息总线的架构,可以说就是EDA。

2011年04月29日 11:57 "@SpeedVan"的内容
这个感觉就是消息总线,你所提到的关系引擎,就是对象对消息的关注注册。其实总体来说就是消息总线的架构,可以说就是EDA。 ...

我的架构是面向关系编程,不是对象对消息进行注册,而是类对消息进行注册,这和普通的EDA有本质区别。我注册的是事件的类型和对象的类型之间的关系映射。
另外一点是,我的对象都是独立的,不会对其他任何对象有任何引用或唯一标识的关联。这点和一般的对象设计方式也完全不同。

2011年04月29日 13:02 "@tangxuehua"的内容
类对消息进行注册 ...

类对消息进行注册?可以理解为一类对象对某(类)消息进行注册么?若果是那样的话,那也是对关系声明而已,无论是谁关注事件,你只要用Event或者Message来关联,我认为都是EDA了。

独立的话,是与聚合相对。独立的话,DCI和NoSQL都有体现。我个人认为,对象过分独立,会带来事件复杂性,但独立又正好解耦,所以我还是觉得一个可控的度是关键。

确实也属于EDA的范畴。关于你提到的可能会导致事件的复杂的问题。我觉得如果所有对象都是独立的,不会导致事件的复杂,而是会让关系引擎要做的事情会更多。
比如,如果有A和B两个对象,他们之间有关系,A可能会通知B做什么事情,以前我们可能会用两种方式来让A知道B:1)A持有B的引用;2)A持有B的唯一标识;如果按照这两种方式来关联,则A让B做事情会很简单,因为A拥有B的信息或者拥有能找到B的信息。但是如果A和B是相互独立的,它们之间的关系是由关系引擎管理的。那此时如果A要让B做事情的话,对于A来说其实也很简单,不需要创建一个很复杂的消息。而只要创建一个简单的消息,这个消息只要包含A的唯一标识即可以及消息本身的意思。比如
class ChangeBStatus : Message
{
public Guid AId;
}
关键是关系引擎知道A和B的关系,所以它有能力根据A的唯一标识获取B,然后让B响应。

总结:
1)如果A和B之间本身保留了关系,那么关系引擎,或者你说的消息总线只需要做一个“转手”的动作即可,即将消息传递给B进行响应;
2)如果A和B之间没有任何关系,关系是由关系引擎维护,那么消息总系要多做一件事情。那就是首先要根据A所传递过来的消息获取一个关系列表,然后循环处理关系列表中的每个响应者类型,然后再根据A的唯一标识,就可以获取每个响应者的实例,从而让每个响应者去响应。

不知道你是否理解我的意思。
[该贴被tangxuehua于2011-04-29 16:35修改过]

2011年04月26日 10:55 "@tangxuehua"的内容
其实我现在还在思考另外一种模型设计的方式,简要描述如下:

该模型具有如下三个特点:
1)模型中的各个对象之间没有任何关系(无对象引用,无唯一键关联),并且对象的所有行为不需要暴露出来,全部私有(private)即可。因为对象的所有的行为都 ...

建设你看看SmallTalk的相关内容,估计对你有启发。

2011-04-25 17:03 "@tangxuehua
"的内容
:我发现现实生活中的客观存在,比如一个人,他虽然在一天中扮演了不同的角色,每种角色会具有不同的行为,然后大家自然而然地得出“动态注入行为”的思路。但我觉得恰恰相反,其实一个人本质上已经具有所有的行为了,只是在特定的场景下,也就是在扮演特定的 ...

我也想过类似的问题,
比如,我是一个人,在家里我是一个父亲角色,在公司我是一个职员角色,

问题来了,如果按照DCI的的思路,难道我在家里就没有作为一个职员的行为了?我就不能工作?这显然不符合逻辑