zoukankan      html  css  js  c++  java
  • EntityFramework之领域驱动设计实践【扩展阅读】:服务(Services)

    服务(Services)

    从本讲开始,所涉及的DDD话题可能与EntityFramework关系不大了。网友千万别骂我是标题党,呵呵。由于这部分内容并非是特定于EntityFramework的,更多的是在介绍模式及实践心得,所以EntityFramework的内容就会偏少了。为了使得针对一些话题的讨论能够延续下去,我仍然将这些文章安排在本系列中,希望读者朋友能够谅解。我也在标题中标注了【扩展阅读】,表示所讨论的内容已经不仅仅局限于EntityFramework了。

    为了表示补偿,透露一下EntityFramework 4.0的最新特性:EF CTP 4.0在“代码优先”开发模式以及提升开发生产率方面做了重要改进。EF CTP 4.0引入了两种新的类型:DbContext和DbSet。DbContext是ObjectContext的简化版。详情请见http://www.infoq.com/news/2010/07/EF-CTP-4

    言归正传,本文将对DDD中的又一重要角色:服务(Services)做一些简单的介绍。提起服务,很多朋友都会想到“SOA”。而在领域驱动设计里,服务贯穿于整个系统的各个层面。根据应用系统的领域驱动分层思想,服务被归类为:应用层服务、领域服务以及基础结构层服务。应用层服务为表现层提供接口,根据DDD的思想,应用层很薄,不承担任何业务逻辑的处理,仅仅是起到coordination的作用。因此,应用层服务也不会牵涉到业务逻辑。在CQRS模式中,Command Service以及Query Service就是应用层服务。基础结构层服务是显而易见的,比如,邮件发送服务、数据服务、事件总线等等。这些服务是与领域无关的,只跟技术实现相关。假想这样一个例子:将货物从仓库A转移到仓库B,如果转仓成功,则向仓库管理员及操作员发送SMS。这是仓储管理领域常见的业务需求,经典的写法类似如下:

      1: public class TransferService : IDomainService
    
      2: {
    
      3:     public void Transfer(Warehouse a, 
    
      4:                          Warehouse b, 
    
      5:                          Item item, Qty qty)
    
      6:     {
    
      7:         using (IRepositoryTransactionContext ctx = Ioc.GetService<IRepositoryTransactionContext>())
    
      8:         {
    
      9:             Inventory oItemInA = a.GetInventory(item);
    
     10:             if (oItemInA.Qty < qty)
    
     11:             {
    
     12:                 // raise not enough inventory event or exception
    
     13:                 throw new Exception();
    
     14:             }
    
     15:             Inventory oItemInB = b.GetInventory(item);
    
     16:             if (oItemInB == null)
    
     17:                 oItemInB = b.CreateInventory(item);
    
     18:             oItemInA.Qty -= qty;
    
     19:             oItemInB.Qty += qty;
    
     20:             ctx.SaveChanges();
    
     21:         }
    
     22:     }
    
     23: }
    
     24: 

    在上面的伪代码中,我们已经看到了领域服务(Domain Service)的影子。在DDD里,领域服务用以处理那种“放在哪里都不合适”的业务逻辑。比如上面的转仓业务,从面向对象的角度看,既不是仓库应有的操作,也不是货物(Item)的行为。为了明确领域对象的职责,DDD将这样的业务逻辑“抽”出来,置于领域服务当中。对于发送SMS的需求,就需要由应用层服务通过“协调”进行处理了。比如:在调用了领域服务并获得响应以后,根据响应结果以及外部配置信息,进而调用基础结构层服务(SMSService)实现SMS的发送。

    看到这里你会发现,其实哪些业务应该放在实体中,哪些需要使用服务来处理,并没有一个绝对的标准。还是那句老话:凭经验。你还会发现,如果从实体将业务逻辑全部“抽”到服务里,实体将成为仅包含getter/setter的对象,于是贫血模型产生了。正因为DDD提倡面向领域,并将通用语言和领域模型摆在很重要的位置,因此,DDD是不主张贫血模型的。

    个人认为,领域服务的引入,增加了模型的抗需求变更的能力。我们可以通过需求分析,找出业务逻辑中易变的部分,以领域服务的方式“注入”到领域模型中,今后若有需求变更,则可以无需更改任何现有组件,完成业务处理逻辑的改变。[TBD: 这样的想法还有待进一步证实]

    有关领域服务的内容,本文暂且讨论这些。读者朋友可以在实践中提出问题,然后在此与大家分享讨论。本文还引出了一个话题,就是应用层服务的协调问题。比如,本文的例子中,是在应用层服务中调用SMSService实现SMS发送,如果直接将这部分内容写在应用层服务中,势必降低系统的扩展性。比如,今后希望不仅要发送SMS,而且还要发送Email。DDD的解决方案是引入事件模型。在完成转仓操作时,向事件总线(Event Bus)发送事件,由事件订阅者Subscriber捕获并处理(Handle)事件。于是,今后我们只需要实现一个“WarehouseTransferSendEmailEventHandler”的事件处理器,并在Handle Event的调用中,向相关人员发送Email即可。NServiceBus就是一款经典的基于.NET的企业级应用通信的框架,在基于事件的DDD架构中,NServiceBus发挥了重要作用。

    从下一讲开始,我将着重讨论领域事件以及Event Sourcing,并对DDD的CQRS模式作个引子。

  • 相关阅读:
    标签的讲解
    属性分类
    LeetCode 003. 无重复字符的最长子串 双指针
    Leetcode 136. 只出现一次的数字 异或性质
    Leetcode 231. 2的幂 数学
    LeetCode 21. 合并两个有序链表
    象棋博弈资源
    acwing 343. 排序 topsort floyd 传播闭包
    Leetcode 945 使数组唯一的最小增量 贪心
    Leetcode 785 判断二分图 BFS 二分染色
  • 原文地址:https://www.cnblogs.com/daxnet/p/1785894.html
Copyright © 2011-2022 走看看