代码改变世界

胡乱说一下我对于 BO VO PO DTO 的理解

2010-12-18 11:44  Virus-BeautyCode  阅读(7134)  评论(22编辑  收藏

引言

 

 

本文中将向大家介绍我对于是使用实体的一些体验,欢迎大家拍砖。更欢迎提出不同或者相同的意见。

正文

刚开始学会使用实体的时候就是建立一个Entity类库,然后里面的实体被其他各层引用。大家传递和使用的都是这一个类库中的实体,包括前端和后台的项目都是引用这个类库,传递和操作这个类库中的实体。

image

就像上面的这幅图一样。每个都要添加对Entity的引用。每个项目都是这么做的,也没有发现什么不好的地方。

以前都是做一些小项目,或者是自己Demo一下。上面的做法也没有什么问题,而且看到别人的文章也都是类似这样的结构。后来在学习DDD(Domain Driven Design)的时候,看到了很多的概念。有DTO,VO,PO,BO,DAO,才发现怎么需要这么多的实体类库(有的不是类库,例如DAO)呢?觉得不可思议,这么多维护起来不是很麻烦吗?一下子还没有想到分开这么多类库的好处。

 

从今年年初开始接触一个大项目,到目前为止才搞完一半。主要的结构也是BLL,DAL,由于前端采用了纯Silverlight展示,所以访问数据库必须要借助服务类的项目(开始的时候我们还是用silverlight3,后来silverlight4可以使用tcp了,但是端口有限制,不适宜在互联网环境使用,容易被防火墙阻断,内网应用可以使用tcp),例如:webservice,wcf,最终我们使用的是WCF。我主要负责后台代码的编写,由于人少,后台我全负责了,包括dal,bll,wcf,一个人搞定。

 

wcf和silverlight之间传递数据使用强类型的实体和实体集合,但是silverlight不能添加普通类库的应用,它有自己的silverlight类库项目。当然了,silverlight也可以不添加类库,在添加服务引用的时候,会自动在silverlight项目生成wcf所需要的实体,在reference.cs文件(这个文件就是wcf在客户端的代理类,里面包括了服务的定义,也就是下图中红框框中的文件,想看这个文件,需要点击Solution Explorer的Show All Files按钮,就是解决方案管理器的左上角)中有。

image

 

于是我就建立了两个项目,一个是普通的类库,给后台的BAL,DAL,SERVICE引用;一个是silverlight的类库,给silverlight引用,他们的文件内容是一样的。当然了,需要在类和属性上添加序列化attribute。类上添加DataContract,属相上添加DataMember。

 

时间长了,在数据访问层就会有类似很多下面的代码。

 

<%displayname%>


 

 

  

就想着能否简化一些呢?因为每次查询都要写while。。。reader[“”],好像都很重复,想要少些一些这样的代码。就想到了好像有ORM这种工具可以帮助实现映射和持久化,找了几个,NH,微软的EF。发现学习起来不是很顺利,以前没有项目使用过,对于他们的内部不是很了解,抛出的一些异常也不是很好解决,如果这时候引入项目的话,肯定有风险,搞不好还会拖慢进度,决定放弃引入这些ORM工具,等待详细学习了解之后再做决定。

 

但是上面的代码还是要简化啊,至少在从数据库的reader到entity这一层面可以想办法简化赋值过程。想到了反射,利用反射给每个属性赋值,再加上attribute,给属性打上标记,方便属性和数据库查询结果列的对应。于是就写了一个EntityMapper的小工具,来辅助实现reader到enttiy和entitycolleciton的映射。详见:利用attribute实现简单的ORM

 

实际上没有ORM那么强大,就是一个reader到enttiy和entitycolleciton的映射的工具,以后想着要基于这个逐步完善成一个自己的ORM,这是后话了,暂且不提。

 

这时候发现减少了很多的工作量,而且代码也简单多了,没有了大量的reader[“”]到属性的赋值。

 

我们系统的业务是经常变化的,后期有添加的需求,有修改前期的需求。导致了,实体类库需要修改,增加属性,减少属性之类的。减少还好办,顶多就是数据库查询的多了,没有属性和它对应了,我不赋值就是了。增加属性就暴露了各层公用一个实体类库的问题,还是一个不小的问题。

 

例如我在一个类添加一个属性,由于reader到entity的映射是采用我写的这个工具,于是添加属性,添加attribute。新方法,新存储过程,测试,没有问题。但是,这个实体的其他方法就报错了,因为其他方法对应的存储过程没有查询新添加的字段,但是在映射的时候是根据属性映射的,就报错“没有找到新属性对应的列”。好吧,那就打开老方法的存储过程,添加对于新属性的查询。问题就出来了,本来是添加方法,却还要修改以前的存储过程,少的还好,多的情况的话,就需要修改大量的存储过程,我碰到最多的一个是牵连了10个存储过程。这也可以归结为紧耦合,依赖太强了,需要解耦。

 

当然,改也是copy,因为字段一样,但是这个问题应该有更好的办法,这样下去,维护量会越来越大。

 

于是想起学习DDD的过程中遇到的那些概念了,什么BO,PO,DAO,DTO,VO的。首先给大家说一下这些概念,然后讲解我对他们的使用方案。这些概念好像在Java中非常流行,使用java进行开发的人应该更加熟悉他们。

BO:Business Object,业务对象。主要是承载业务数据的实体。处理业务逻辑的时候使用,数据结构也是针对业务逻辑建立的。

PO:persistence Object,持久化对象。数据最终要存储,无论以何种形式存储,都必须要持久化。加入使用关系数据库存储,一个PO对应一条数据库的记录,或者是对象从数据库查询出来的结果集的一条记录。

DAO:Data Access Object,数据访问对象。包含一些数据库的基本操作,CRUD,和数据库打交道。负责将PO持久化到数据库,也负责将从数据库查询的结果集映射为PO。

DTO:Data Transfer Object,数据传输对象。一般用来在前段和后台的数据传输,数据结构的简历是基于网络传输的,减少传输的数据量,避免传输过多无用的数据。

VO:Value Object,值对象。主要用在前段数据和控件的绑定操作中,以键值对的形式存在。可以从DTO转化而来,这么做的好处就是减少对于DTO的依赖,进一步减少对应后端的依赖。还可以增加前段的可测试性,也就是没有DTO,也可以对前段逻辑进行自动化的单元测试,可以通过MockDTO来达到测试的目的。



 

 

image

我想通过上面的这幅图来表达我的想法。web,winform,silverlight,console代表不同的前端类型。Domain代表领域对象,也可以是BLL。

获取数据的过程:首先DAO从数据库获取结果集,转换为PO。Domain接受到DAO传递过来的PO之后,负责将PO转换为BO,再进行业务逻辑的处理。处理完毕,传递BO给service,service负责转换为DTO,传输给前端接收到DTO之后,首先转换为VO,然后再进行前端的业务处理。

提交数据的过程: 前端将数据整理为DTO,传输给service,service转换为BO传输给Domain,Domain转换为PO,调用DAO提供的数据持久化方法,持久化PO,DAO负责将PO持久化为数据库的数据。

 

 

 

 

好像还有一个概念叫做:POJO,这是java中的概念。POCO是.NET的概念。个人理解好像就是一类实体的统称,指的是实体没有操作,只要属性,简单实体,没有继承或者实现任何抽象的实体。凡是满足这个标准的实体都可以叫做POCO或者POJO。