应用程序框架实战三十五:服务概述

  上一篇介绍了我对几种实体的认识,本篇将介绍几种服务的用法。

  预告一下本系列后续计划,本篇之后,准备进入实战演练阶段,先介绍如何快速解决CRUD操作,从如何使用PD数据建模到使用CodeSmith生成代码,先带你感受一下,再回过来介绍框架内部元素,以免你在阅读时昏昏欲睡。

应用服务介绍

  对于一个新的设计元素,可以先假定不需要它,等到确实认识到它的作用再引入。那么,应用服务为我们带来了哪些好处呢?

应用服务帮助简化表现层操作

  以MVC为例,如果没有应用服务,那么控制器将直接调用仓储,设置查询条件,转换DTO等。

  当控制器操作变得复杂,你会想办法把控制器代码分离出去。分离控制器代码的最好方法就是建立对应的应用服务,不要轻易的分离控制器本身,因为这会导致查找视图变得困难,并且破坏了约定,导致更高的维护成本。

应用服务为表现层提供唯一的API访问点

  当一个控制器操作变得复杂时,编写控制器的程序员需要了解更多的知识,这些操作是由哪些聚合、领域服务、基础设施服务等构造元素组装起来的呢,调用顺序如何?

  这是表现层应该关心的问题吗?

  表现层应该只关心界面展示,这是表现层的基本职责所在,其它职责尽量转移到后方。

  应用服务将细粒度操作打包为粗粒度操作,让编写表现层的程序员不再东张西望,他只需要记得一件事:需要访问任何后端的操作,找应用服务就对了。

应用服务为多个表现层减少冗余代码

  如果需要多个表现层,比如需要开发一个WPF程序,功能与MVC完全一样,这时候,你会发现表现层充斥大量重复代码。

  引入应用服务后,表现层代码更简洁,冗余代码更少,创建多个表现层更加轻松。

应用服务集成和装配领域层与基础设施层

  为了保持领域层的纯净,我们采用依赖倒置原则,领域层只拥有操作接口,具体实现却在基础设施层,最终要能运行起来,必须将它们组装到一起。

  如果没有应用服务,控制器将需要自己进行装配,这增加了表现层的负担,应用服务承接了这项工作,让表现层专心干自己的本职工作。

应用服务配合DTO优化远程调用性能

  当采用了分布式架构,为了提升性能,需要尽量减少远程调用次数。

  应用服务在分布式环境充当了远程外观,配合DTO一起解决这个问题。

应用服务与BLL的比较

  对于长时间采用传统三层架构的朋友,对于应用服务这个构造是无比的亲切,初步用起来以后,你发现这个应用服务就是BLL的翻版。

  BLL是传统三层架构的业务逻辑层,很多人直接用BLL来命名业务逻辑层的服务类,我暂时就用这个称呼。

  BLL是业务逻辑放置的场所,BLL将结果保存到Model实体类中,然后将实体类传递给其它层。

  应用服务用于协调领域层与基础设施层的操作,应用服务本身不应该完成复杂的业务逻辑。

  应用服务与BLL的关键区别在于,BLL直接完成业务逻辑,而应用服务会将业务逻辑委托给领域实体或领域服务。

  从代码上看,BLL主要通过操作Model的属性进行业务逻辑的处理,而应用服务通过调用领域实体或领域服务的方法完成所有业务操作。

领域服务介绍

  从前面的介绍,你应该已经发现应用服务是有用的,下面再来看看领域服务。

  在最理想的情况下,所有的业务逻辑都应该高度内聚到聚合中。但对于更复杂的业务,一般都有比较复杂的流程,这些流程上的控制,放入聚合并不合适,因为这些行为不属于某个聚合。

  如果把这些行为放到应用服务中,可能导致应用服务非常复杂,业务逻辑也从领域层脱离出来,使业务逻辑的管理更加困难。

  为了把业务逻辑重新移回领域层,需要添加一个领域服务。

  现在我们有了应用服务和领域服务两个构造元素,那么,对于一个复杂操作,应该将哪一部分放入应用服务,哪些又放到领域服务呢?

  书上介绍,业务逻辑可分为领域逻辑和应用逻辑,领域逻辑应该完全放到领域层,不能泄露到其它层去,而应用逻辑应该放到应用层服务。

  可以看出,说得相当抽象,哪种东西算得上领域逻辑,哪种又是应用逻辑?这个很难精确划分。

  一个经典的应用逻辑例子是事务控制,事务控制不是客户的需求,但却非常重要,所以把这种东西放到应用层,而不是领域层。

  那么发邮件的操作是领域逻辑,还是应用逻辑??有人说是领域逻辑,有人说是应用逻辑,还有人说要看情况。可谓众说纷纭,让你摸不着头脑。

  不过我们学习的目的不是进行学术辩论,而是切实的提升项目可维护性,所以不用太理会那些考智商的概念,下面谈谈我的用法。

  对于应用服务,我总是需要它,因为它可以简化表现层开发。

  对于某些比较简单的业务逻辑,如果发现建立领域服务并未产生太大价值,我就直接放入应用服务中。比如判断一个实体的名称不能重复,这是一个业务需求,首先考虑这个业务逻辑可以放入实体中吗?不能,因为实体本身无法了解自己的名称有无重复。其次考虑需要建立领域服务吗?对于这种简单业务逻辑,创建一个领域服务有点大炮打蚊子的感觉。我会直接将重名检测放入应用服务中,虽然导致有一点业务逻辑散落到应用层,但我未发现对可维护性造成影响。

  如果业务逻辑比较复杂,我一般会采用TDD方式推进业务开发,这种情况下,建立领域服务是一个极好的选择,因为领域层依赖低,单元测试更方便。

  不论应用服务,还是领域服务,开发的基本原则是,尽量将业务逻辑委托给领域实体,服务仅做调度的工作。

  基础设施服务比较好理解,就是提供一些基础支持服务,比如发送邮件等操作。

需要为服务抽取接口吗

  学习设计模式时,要求我们面向接口编程,但一些对象大师建议不要过度设计,以免导致不必要的复杂性,在《实现领域驱动设计》一书中提出,应用服务使用独立接口没有什么好处。

  虽然接口有不少作用,但一般来讲,驱动创建接口的真正需求来自多态。在一般的项目开发中,特别是前期,服务很少具有多态需求,那我们还要不要为服务建立接口?

  在没有使用Resharper的年代,我使用接口非常小心谨慎,因为从接口定位实现类非常困难,但自从用上了Resharper,接口不再成为障碍。

  另一方面,IOC框架的使用,让接口的使用进一步普及,我只需要将实现类绑定到接口,在程序中就可以自由使用了。虽然IOC可以直接绑定实现类,但总感觉有点奇怪。

  最后,对于跨层访问,采用接口互联也符合分层设计原则。

  对于采用了Ioc框架,并安装了Resharper这样强大的工具,接口不会给你造成任何不便,至于是否导致复杂性,则需要自己体会。

  代码中采用接口编程,可能在大多情况下都未产生实质的好处,但在需求发生变化时,只需修改Ioc配置替换相关实现,这个扩展性在不花成本的情况下还是划得来的。

服务命名约定

  当同时需要应用服务和领域服务,会发现服务取名也很困难。比如用户管理,应用服务叫UserService,如果需要领域服务,也叫UserService就发生冲突 ,但命名为其它可能又不合适。

  我的办法是应用服务统一以Service结尾,领域服务统一以Manager结尾,这样就可以简单的解决这个问题。对于和我一样的英文菜鸟,可能特别有用。

最新源码发放

  本次更新了EasyUi表格列绑定Combox和Combotree的功能。同时新增了一个图标管理模块,该模块包含对百度WebUploader上传图片控件的简单封装,服务端上传操作封装。

  WebUploader控件非常给力,且提供的Demo非常好用,我基本直接COPY过来简单修改就用上了。

  我在Data下载包中提供了一些图标,以方便大家操作。

  同时,该示例包含了领域服务,并提供了相关的单元测试,大家可以参考。

  下载截几个运行效果图。

  虽然这个界面包含了树控件的CRUD操作,但总的JS代码量还是比较少的。

下载时记得推荐

http://files.cnblogs.com/files/xiadao521/Applications.2015.4.8.1.rar

http://files.cnblogs.com/files/xiadao521/Framework.2015.4.8.1.rar

http://files.cnblogs.com/files/xiadao521/Data.2015.4.8.2.rar

.Net应用程序框架交流QQ群: 386092459,欢迎有兴趣的朋友加入讨论。

.Net Easyui开发交流QQ群(本群仅限Easyui开发者,非Easyui开发者勿进):157809322

谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/xiadao521/

时间: 2024-11-03 03:40:32

应用程序框架实战三十五:服务概述的相关文章

应用程序框架实战三十八:项目示例VS解决方案的创建(一)

进行项目开发的第一步,是创建出适合自己团队习惯的VS解决方案,虽然我已经提供了项目示例,但毕竟是我创建的,你直接使用可能并不合适,另外你如果尝试模仿重新创建该示例,中间可能碰到各种障碍,特别是项目间的依赖关系. 本文的目的是帮助.Net架构初学者能顺利搭建起适合自己的VS解决方案,我会在本文演示曾经用过的几种不同风格的目录结构,你可以根据自己的习惯选择一种并自行修改. 本系列假定你已经熟悉如何创建.NET类库等基础知识,并具有.Net开发经验,我不会详细到每一个细节.如果你是.Net初学者,尚未

应用程序框架实战三十六:CRUD实战演练介绍

从本篇开始,本系列将进入实战演练阶段. 前面主要介绍了一些应用程序框架的概念和基类,本来想把所有概念介绍完,再把框架内部实现都讲完了,再进入实战,这样可以让初学者基础牢靠.不过我的精力很有限,文章进度越来越慢,所以准备切换一下介绍顺序,把实战演练提前,以方便你阅读代码. 实战演练介绍 本系列实战演练共分两个部分. 实战演练第一部分介绍如何快速解决CRUD机械操作,这一部分我将手把手带领各位同学从搭建VS环境开始,创建程序集及各程序集间的依赖关系,以及引入依赖的外部DLL,并手工完成代码示例中Ap

应用程序框架实战三十:表现层及ASP.NET MVC介绍(一)

本文将介绍表现层及ASP.NET MVC的一些要点,特别是ASP.NET MVC的一些抽象和封装技巧,如果你对MVC还不了解,可以参考<ASP.NET MVC4 高级编程>,作者Jon Galloway等,这本书由ASP.NET MVC团队成员编写,相当不错. 表现层的职责 表现层的职责是展示和收集数据,将领域层的数据和逻辑展示出来,并收集用户输入的相关信息. 搞清楚表现层的职责以后,你就应该清楚,表现层不是你应该编写业务逻辑的地方,这也是分层架构的核心. 如果要展示一个计算值,不应该在表现层

应用程序框架实战二十五:查询条件(规约模式应用)

前面已经做了一些准备工作,本篇将介绍查询条件的封装,它是规约模式的一个应用. 规约使用一个对象来封装谓词,我之前已经介绍过它在验证方面的应用,本篇是规约模式在查询方面的应用. 规约的强大之处在于,能够将一堆杂乱无章的条件判断或查询条件封装起来,以一个清晰的概念来表达,并使得这些谓词具备了可复用的能力. 首先在Util.Domains项目的Repositories目录中创建ICriteria接口,这个接口表示一个查询条件,代码如下. using System; using System.Linq.

应用程序框架实战三十四:数据传输对象(DTO)介绍及各类型实体比较(转)

本文将介绍DDD分层架构中广泛使用的数据传输对象Dto,并且与领域实体Entity,查询实体QueryObject,视图实体ViewModel等几种实体进行比较. 领域实体为何不能一统江湖? 当你阅读我或其它博主提供的示例代码时,会发现几种类型的实体,这几种实体初步看上去区别不大,只是名称不同,特别在这些示例非常简单的情况下更是如此.你可能会疑惑为何要搞得这么复杂,采用一种实体不是更好? 在最理想的情况下,我们只想采用领域实体Entity进行所有的操作. 领域实体是领域层的核心,是业务逻辑的主要

应用程序框架实战二十六:查询对象

信息系统的查询需求千变万化,在仓储中为每个查询需求创建一个特殊方法,将导致大量乏味而臃肿的接口. 一种更加可行的办法是,在应用层服务中描述查询需求,并通过仓储执行查询. 为了能够更好的描述查询需求,可以将查询功能从仓储中抽取出来,专门创建一个查询对象. 查询最复杂的部分是条件过滤,这也是查询对象的主要职责.查询对象可以认为是规约模式的一个变种,允许查询对象动态创建查询条件. 在Util.Domains项目Repositories目录中,创建查询对象基接口IQueryBase,代码如下. usin

应用程序框架实战二十:映射层超类型

上一篇介绍了工作单元层超类型的封装演化过程,本文将介绍对Entity Framework映射层超类型的封装. 使用Entity Framework一般需要映射三种类型的对象,即实体.聚合.值对象. 聚合与实体映射的主要区别是:聚合映射单属性标识Id,并需要映射乐观离线锁Version,而实体的标识往往需要映射成复合属性,这样方便物理删除聚合中的实体.Entity Framework通过EntityTypeConfiguration进行实体映射. 值对象以嵌入值模式映射,这需要使用ComplexT

应用程序框架实战二十四:基础查询扩展 - 分页与排序

上一篇介绍了IQueryable的Where方法存在的问题,并扩展了一个名为Filter的过滤方法,它是Where方法的增强版.本篇将介绍查询的另一个重要主题——分页与排序. 对于任何一个信息系统,查询都需要分页,因为不可能直接返回表中的所有数据. 如果直接使用原始的Ado.Net,我们可以编写一个通用分页存储过程来进行分页查询,然后通过一个DataTable返回给业务层.不过进入Entity Framework时代,分页变得异常简单,通过Skip和Take两个方法配合就可以完成任务. 为了让分

应用程序框架实战三:什么是应用程序框架

什么是应用程序框架?正如框架是架构的具体实现一样,应用程序框架是应用程序架构的具体实现.不过这样说了等于没说,因为框架和架构都是如此抽象.为了获得直观的感受,下面先看看更具体的.Net Framework和第三方框架. .Net Framework为.Net程序员提供了一个庞大的类库,几乎任何开发任务在.Net Framework中都能找到答案.虽然如此,.Net Framework还是不能一手遮天. 例如,现在我们在项目中需要记录日志,一种办法是直接使用.Net Framework的文件操作类