好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

一起探讨领域驱动设计——架构与建模

一起探讨领域驱动设计——架构与建模

领域驱动设计,挺好的,新做的一个商城也是基于这个思想来进行开发的。由此,想和大家一起分享一下应用这个思想在做项目中的一些领悟与经验,正好也能和大家一块探讨DDD,使我们能更好地理解领域驱动设计。 
这一篇就和大家分享一下我使用的一些用于架构与建模使用的组件。

在这里,个人推荐使用一个开源的DDD框架组件:SharpArch.dll

这里是它的官方网站: http://www.sharparchitecture.net/

大家可以下载完整包,里面的组件基本上包括了后面项目所要用到的一些开源组件。

下面就举一个简单的例子与大家一起分享。

     一。业务流程

         会员之间的金额转账。

     二。建模

        在DDD中,实体(Entity),即通过一系列连续性(continuity)和标识(identity ID)来定义 ,每个实体必须拥有自己的唯一ID,主键,如果没有一个ID标识,为每个实例加上一个具有唯一性ID,可能是内部使用。因此,对于每个实体来讲,都有一个自己的唯一标识ID。

        因此,在使用了开源组件SharpArch.dll后,我们的领域模型都会继承于该组件中的Entity类型。

        先上整体类图:

  

        模型代码:

01 /// <summary>

02      /// 会员基本信息

03      /// </summary>

04      public   class   Member :Entity

05      {

06          /// <summary>

07          /// 会员用户名

08          /// </summary>

09          public   virtual   string   MemberName {  get ;  set ; }

10  

11          /// <summary>

12          /// 会员登录密码

13          /// </summary>

14          public   virtual   string   Password {  get ;  set ; }

15  

16          /// <summary>

17          /// 会员可用金额

18          /// </summary>

19          public   virtual   decimal   SafeMoney {  get ;  set ; }

20  

21          /// <summary>

22          /// 会员转账转出日志

23          /// </summary>

24          public   virtual   IList<MemberMoneyTransLog> SendMoneyTransLogs {  get ;  set ; }

25  

26          /// <summary>

27          /// 会员转账转入日志

28          /// </summary>

29          public   virtual   IList<MemberMoneyTransLog> ReceiveMoneyTransLogs {  get ;  set ; }

30  

31          public   Member()

32          {

33              this .SendMoneyTransLogs =  new   List<MemberMoneyTransLog>();

34              this .ReceiveMoneyTransLogs =  new   List<MemberMoneyTransLog>();

35          }

36      }

1  

1    

01 /// <summary>

02 /// 会员金额转账日志

03 /// </summary>

04 public   class   MemberMoneyTransLog :Entity

05 {

06      /// <summary>

07      /// 转出会员

08      /// </summary>

09      public   virtual   Member SendUser {  get ;  set ; }

10  

11      /// <summary>

12      /// 转入会员

13      /// </summary>

14      public   virtual   Member ReceiveUser {  get ;  set ; }

15  

16      /// <summary>

17      /// 转账金额

18      /// </summary>

19      public   virtual   decimal   TransMoney {  get ;  set ; }

20  

21      /// <summary>

22      /// 转账时间

23      /// </summary>

24      public   virtual   DateTime TransTime {  get ;  set ; }

25 }

1    

      此时,大家可能会纳闷,上面说的实体必须有唯一ID,而现在这2个实体定义中,并没有ID啊,这样的模型会不会有问题呢??

      不用急,让我们一起来解开他们的基类Entity类:

     

     原来,Entity类已存在Id属性,其类型为INT,这个时候有的铜子可能会说,实体所对应的主键不一定是INT型呀,有可能是自定义规则的字符串或者是Guid,不急不急,人家老外的东东不会那么2。我们仔细查看Entity时,发现其父类为EntityWithTypedId<int>。。。这里大家可能就知道应该怎么做了吧。。。

想用string型的,那就将模型继承于EntityWithTypedId<string>;想用Guid,那么就继承EntityWithTypedId<Guid>。

     OK,回到现有的2个模型中,实体唯一ID被分离了。这样,至少从代码上来说,DDD的思想实现了,在实体中,唯一ID没有什么用途,仅作为一个标识。

     三。业务逻辑的实现

     回到DDD中,DDD的好处是什么,是什么,是什么,没错,是业务逻辑高内聚。内聚到哪里呢??没错,就是内聚在模型中(充血啊,充血啊)!!个人认为那种贫血模型和传统的三层没有什么两样。。。

    回到代码中,我们要实现一个会员的转账,就是把会员A的钱转到会员B的口袋中,顺带加个转账日志。。

    实现如下:

01 /// <summary>

02      /// 会员基本信息

03      /// </summary>

04      public   class   Member : Entity

05      {

06          // 属性略了..

07  

08          public   Member()

09          {

10              this .SendMoneyTransLogs =  new   List<MemberMoneyTransLog>();

11              this .ReceiveMoneyTransLogs =  new   List<MemberMoneyTransLog>();

12          }

13  

14          /// <summary>

15          /// 会员转账

16          /// </summary>

17          /// <param name="recieveMember">收钱的</param>

18          /// <param name="transMoney">转多少钱</param>

19          public   virtual   void   TransMoney(Member recieveMember,  decimal   transMoney)

20          {

21              // 校验下你的钱够不

22              if   ( this .SafeMoney < transMoney)

23                  throw   new   Exception( "你个货,没那么多还想转账啊!" );

24  

25              // 我口袋的钱放血

26              this .SafeMoney -= transMoney;

27  

28              // 收钱的人收钱

29              recieveMember.SafeMoney += transMoney;

30  

31              // 来个日志

32              var transLog =  new   MemberMoneyTransLog

33                                 {

34                                     ReceiveUser = recieveMember,

35                                     SendUser =  this ,

36                                     TransMoney = transMoney,

37                                     TransTime = DateTime.Now

38                                 };

39  

40              // 我的转出日志里面加条记录

41              this .SendMoneyTransLogs.Add(transLog);

42          }

43      }

      好了,给Member模型加了针“会员转账”的血了,有的哥们又会问了,这样打鸡血的好处是??

      好吧,被你凹凸曼的造型打败了,回到传统BLL + DAL的三层构架中,我们是怎么实现的呢:

建个BllMember业务逻辑类; 加个方法TransMoney,使用三个DAL方法:给转账的人减钱,给接收的人加钱,再写条转账日志;

      嗯嗯,差不多了,OK了,再回头看看代码。OMG,事务,事务!!得套个事务!嗯,加个事务的代码。。

       好了,实现了,看代码,业务逻辑在哪里,业务逻辑体现了么, BLL中夹杂着大量的DAL与事务(可能还有LOG4NET的日志记录)代码。 这些代码与业务逻辑有关系么?没有任何关系,这些代码只是数据持久化的体现 。。。回头再看看DDD中的实现,是不是觉得神精气爽??

     到了这里,各位朋友可能会有不少疑问:

  实体的属性与方法为什么是虚(virtual )的呢?(好吧,你猜到了,数据持久化用的是NHibernate)   实体的数据持久化是怎样的呢?   业务逻辑实现了,我们的在UI层需要怎么调用呢?   “会员转账”的方法中,并没有事务呀,事务是怎么实现的呢?

     呵呵,由于时间的关系,老婆要我交差了,诸位请听下回分解。。

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于一起探讨领域驱动设计——架构与建模的详细内容...

  阅读:39次