zoukankan      html  css  js  c++  java
  • 一起探讨领域驱动设计——架构与建模

    领域驱动设计,挺好的,新做的一个商城也是基于这个思想来进行开发的。由此,想和大家一起分享一下应用这个思想在做项目中的一些领悟与经验,正好也能和大家一块探讨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 { getset; }
    10  
    11         /// <summary>
    12         /// 会员登录密码
    13         /// </summary>
    14         public virtual string Password { getset; }
    15  
    16         /// <summary>
    17         /// 会员可用金额
    18         /// </summary>
    19         public virtual decimal SafeMoney { getset; }
    20  
    21         /// <summary>
    22         /// 会员转账转出日志
    23         /// </summary>
    24         public virtual IList<MemberMoneyTransLog> SendMoneyTransLogs { getset; }
    25  
    26         /// <summary>
    27         /// 会员转账转入日志
    28         /// </summary>
    29         public virtual IList<MemberMoneyTransLog> ReceiveMoneyTransLogs { getset; }
    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 { getset; }
    10  
    11     /// <summary>
    12     /// 转入会员
    13     /// </summary>
    14     public virtual Member ReceiveUser { getset; }
    15  
    16     /// <summary>
    17     /// 转账金额
    18     /// </summary>
    19     public virtual decimal TransMoney { getset; }
    20  
    21     /// <summary>
    22     /// 转账时间
    23     /// </summary>
    24     public virtual DateTime TransTime { getset; }
    25 }
    1   

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

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

         无标题2

         原来,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的三层构架中,我们是怎么实现的呢:

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

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

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

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

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

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

  • 相关阅读:
    MongoDB安装
    前端构建工具gulp入门教程
    限制input输入类型(多种方法实现)
    【E20200105-1】Centos 7.x 下vsftpd配置文件常用配置详解
    【E20200102-1】centos 7 下vsftp的安装和配置
    【E20200101-1】Centos 7.x 关闭防火墙(firewall)和SELinux
    Linux下如何修改用户默认目录
    linux给普通用户增加ssh权限
    在IIS7中应用Application Request Routing配置反向代理
    vmware相关服务默认禁用 修改服务弹出服务拒绝访问解决办法
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2046333.html
Copyright © 2011-2022 走看看