彩票走势图

领域驱动设计案例:Tiny Library:应用服务层

转帖|其它|编辑:郝浩|2010-11-02 11:59:39.000|阅读 886 次

概述:Tiny Library使用应用服务层向用户界面层提供服务,具体实现是采用Microsoft WCF Services。在Tiny Library的解决方案中,是由TinyLibrary.Services项目为整个系统提供这一WCF服务的。按照传统的应用系统分层方法,TinyLibrary.Services项目位于领域模型层之上、用户界面层之下,它是UI与Domain的交互界面。TinyLibrary.Services的实现中,与DDD相关的内容主要是数据传输对象(DTO),至于如何编写与实现WCF服务,那是.NET技术上的问题,本文不会做太多的讨论。

# 慧都年终大促·界面/图表报表/文档/IDE等千款热门软控件火热促销中 >>

  Tiny Library使用应用服务层向用户界面层提供服务,具体实现是采用Microsoft WCF Services。在Tiny Library的解决方案中,是由TinyLibrary.Services项目为整个系统提供这一WCF服务的。按照传统的应用系统分层方法,TinyLibrary.Services项目位于领域模型层之上、用户界面层之下,它是UI与Domain的交互界面。TinyLibrary.Services的实现中,与DDD相关的内容主要是数据传输对象(DTO),至于如何编写与实现WCF服务,那是.NET技术上的问题,本文不会做太多的讨论。

数据传输对象(Data Transferring Object,DTO)

  在TinyLibrary.Services中,有一种特殊类型的对象,我们称之为数据传输对象。根据Fowler在PoEAA一书中的描述,DTO用于进程间数据交换,由于DTO是一种对象,它能够包含很多数据信息,因此,DTO的采用可以有效减少进程间数据交换的来回次数,从而在一定程度上提高网络传输效率。

  由于DTO需要跨越进程边界(在我们的案例中,需要跨越网络边界),因此,DTO是可以被序列化/反序列化的,它不能包含上下文相关的信息(比如,Windows句柄)。正因为如此,DTO中的数据类型都是很简单的,它们可以是原始数据类型(Primitive Data Types)或者是其它的DTO。

  有些朋友在阅读Fowler的PoEAA一书时,对于DTO的理解还是有困难,我在此将我的理解写下来,供大家参考

  1. 领域模型对象不负责任何数据传输的功能,这是因为,如果将领域模型对象用于数据传输,则势必需要在另一个层面(或者另一个进程中)产生一个领域模型对象的副本,此时才有可能在服务器与客户机之间产生一种数据契约,而这种做法违背了层内高内聚,层间低耦合的原则
  2. DTO是简单而原始的,并且是运行时上下文无关的。这是为了满足序列化/反序列化的需求。因此,DTO所包含的数据要不就是原始数据类型,要不就是其它的DTO
  3. 客户端需求决定DTO的设计。因此,DTO与领域模型对象并非一一对应的关系,相反,它是为了满足客户端需求而存在的

  在Tiny Library案例中,我选用了WCF的Data Contracts作为DTO的实现标准,因为这样做不仅能够基于客户端需求设计合理的DTO结构,而且还可以利用WCF的DataContractSerializer实现DTO的序列化/反序列化。不仅如此,WCF为我们在服务器与客户机通讯上提供了技术支持和有力保障。

  打开TinyLibrary.Services项目,我们可以看到一个IDataObject的泛型接口,其定义如下:

  public interface IDataObject<TEntity> where TEntity : IEntity

  {

   void FromEntity(TEntity entity);

   TEntity ToEntity();

  }

  我们可以要求所有的DTO都实现这个接口,以便使其具有将实体转换为DTO,或者将DTO转换为实体的能力。在Tiny Library案例中,我并没有强制要求所有的DTO都实现这个接口(也就是说,Tiny Library本身不规定DTO必须实现这个接口),我引入这个接口的目的,就是想说明,其实我们是可以这样做的:在我们的系统中为DTO设计好一个合理的框架,以便让DTO获得更强大的功能。引入这个接口的另一个目的就是为了编程方便:实现基于某个实体的DTO,只需要使其实现IDataObject接口即可,Visual Studio会自动产生方法桩(Method Stubs),无需手动编写代码。

  请注意TinyLibrary.Services.DataObjects.RegistrationData这个类,它与BookData、ReaderData有很大的区别,它并不是Registration实体的映射体现,换句话说,它所包含的所有状态属性,并不是与Registration实体所包含的属性一一对应。例如,RegistrationData这个DTO中包含书名(BookTitle)以及ISBN号(BookISBN)的信息,而这些信息都是来自于Registration的关联实体:Book。RegistrationData的设计完全是为了迎合用户界面,因为在UI上,我们需要针对某个读者列出他/她的借书信息,而RegistrationData包含了这所有需要的信息。

应用层职责

  在《Entity Framework之领域驱动设计实践》系列文章中,我曾经提到过,根据DDD,应用系统分为四层:展现层、应用层、领域层和基础结构层。在Tiny Library案例中,TinyLibrary.Services充当了应用层的职责,它不负责处理任何业务逻辑,而是从更高的层面,为业务逻辑的正确执行提供适当的运行环境,同时起到任务协调的作用(比如事务处理和基础结构层服务调用)。

  public void Return(string readerUserName, Guid bookId)

  {

   try

   {

   using (IRepositoryTransactionContext ctx = ObjectContainer

   .Instance

   .GetService<IRepositoryTransactionContext>())

   {

   IRepository<Book> bookRepository = ctx.GetRepository<Book>();

   IRepository<Reader> readerRepository = ctx.GetRepository<Reader>();

   Reader reader = readerRepository.Find(Specification<Reader>.Eval(r => r.UserName.Equals(readerUserName)));

   Book book = bookRepository.GetByKey(bookId);

   reader.Return(book);

   ctx.Commit();

   }

   }

   catch

   {

   throw;

   }

  }

  上面的代码展示了“还书”操作的具体实现方式,我们可以看到,位于应用层的WCF Services仅仅是协调仓储操作和事务处理,业务逻辑由reader.Return方法实现:

  public void Return(Book book)

  {

   if (!book.Lent)

   throw new InvalidOperationException("The book has not been lent.");

   var q = from r in this.Registrations

   where r.Book.Id.Equals(book.Id) &&

   r.RegistrationStatus == RegistrationStatus.Normal

   select r;

   if (q.Count() > 0)

   {

   var reg = q.First();

   if (reg.Expired)

   {

   // TODO: Reader should pay for the expiration.

   }

   reg.ReturnDate = DateTime.Now;

   reg.RegistrationStatus = RegistrationStatus.Returned;

   book.Lent = false;

   }

   else

   throw new InvalidOperationException(string.Format("Reader {0} didn't borrow this book.",

   this.Name));

  }

配置文件

  TinyLibrary.Services是整个案例的服务供应者(Service Provider),因此,整个系统服务器端的配置与初始化应该由TinyLibrary.Services启动时负责执行。因此,基于服务器的系统配置应该写在TinyLibrary.Services项目的app.config中。其中包括:Apworks的配置、Unity(或者Castle Windsor)的配置、WCF Services的配置以及Entity Framework所使用的数据库连接字符串的设置。

  从实践角度考虑,在基于CQRS体系结构模式的应用系统中,各个组件的初始化和配置逻辑应该位于WCF Services的Global.asax文件中,以便在WCF Service Application启动的时候,所有组件都能成功地初始化。我将在今后的CQRS案例中进一步描述这一点。

  至此,Tiny Library的服务端部分基本介绍完毕,回顾一下,这些内容包括:领域建模、仓储实现和应用服务层。下一讲将简单介绍一下Tiny Library的Web界面的设计与开发。由于本系列文章的重点不是讨论某个技术的具体实现,因此,在下一讲中不会涉及太多有关ASP.NET MVC的细节内容。

 

标签:

本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@pclwef.cn

文章转载自:网络转载

为你推荐

  • 推荐视频
  • 推荐活动
  • 推荐产品
  • 推荐文章
  • 慧都慧问
扫码咨询


添加微信 立即咨询

电话咨询

客服热线
023-68661681

TOP