• 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模式作个引子。

  • 相关阅读:
    Python使用MySQL数据库的方法以及一个实例
    【转载】关于Python中的yield
    利用KMeans聚类进行航空公司客户价值分析
    Python多线程编程
    搜索与排名思路整理
    两个UIView添加同一个手势只有最后一个有用
    缩放动画效果
    iOS通过UIAlertController弹出底部选择框来调用相机或者相册
    ios webview点击图片看大图效果及相应手势操作
    iOS开发:获取设备IP地址
  • 原文地址:https://www.cnblogs.com/daxnet/p/1785894.html
Copyright © 2020-2023  润新知