.NET 技术

WF,WCF,Software Factory,Design Pattern,etc.
随笔 - 28, 文章 - 0, 评论 - 181, 引用 - 13
数据加载中……

实战项目分析(一)

      最近接到一个临时任务:帮外国某知名公司分析一个项目架构。这个项目是两年前开发的,并且经过了几次升级。主要功能是管理客户、合作伙伴资料,提供在线业务等等,具体细节不用多说。

据客户说,他们在使用本系统的过程中发现了很多的问题,觉得已经不再满足他们的需求,希望我们能帮助他们评估一下当前的系统有哪些架构上的问题,并帮助他们发现未来可能发生的问题,从而决定是否需要开发新的系统

客户提供了很详细的文档,包括业务说明,系统架构,技术要点,部署方案等等。看完文档,对系统和客户期望有了一定的了解之后,开始干活儿!

系统是采用.Net技术构建的,基于.Net Framework 2.0,使用了ASP.NET, WinForm, WebService等技术,并使用了Enterprise Library中的Data Access CacheLog等功能。

我本人负责的是架构的分析。结合文档和源代码,没用1个小时,系统架构就很清晰了。其中发现了一些很普遍的问题,在这里跟大家分享一下:

1.     分层架构

分层架构是绝大部分企业软件都普遍采用的方案,但由于架构师水平的参差不齐,导致很简单的一个分层,出现了很大的差异。

大家都知道“3层架构”或者“多层架构”。有的理论里分3层,有的理论里分5层,还有分7层的。其实,在我看来分几层不重要,重要的是分层的目的。分层是为了什么的?简单的说就一句话:为了便于维护。

大家都知道,软件开发中“变化”才是永恒的。开发周期是死的(尽管可以一拖再拖,但总有一个发布的截至日期吧!),但后期的维护却是很不变得。不管是发布补丁也好,更新版本也好,其实都是为了能适应软件发布之后面临的各种各样的“变化”。

好,我们回到分层上来。分层,就是为了使系统结构更清晰,系统耦合性变小,使修改一处代码时,对其它的部分影响最小,这样就能以最小的代价应对变化带来的麻烦!所以,分层的第一要素,就是各层之间屏蔽细节,降低依赖,使各层具体实现变得透明(这也是SOA的其中一个重要思想)。我们可以通过各种办法来达到这个目的,面向接口编程,设计模式,架构模式等等,都可以帮助我们。

而我在此项目中看到的第一个重要的问题就是,系统分层不清楚。下面是分层的简图:

 

由上图可见,系统共分了 三层。但有一个问题,业务对象竟然贯穿了三层,这严重违反了分层的初衷。由于业务对象对数据存取层和展现层都可见,导致如果我们的业务对象发生了变化,就 要修改从数据存取、业务逻辑到展现层,这三个层次的所有相关代码。而且这个方案违反了分层的两个设计原则:

a.            下层对上层隐藏细节,只暴露接口。再此,本应属于业务逻辑层的业务对象被暴露到了展现层。

b.            上层对下层不可见。即下层不知道上层的存在,只提供接口。这里业务逻辑层的业务对象被数据存取层操作,会导致两个层之间纠缠不清,以至于会出现改动业务逻辑会影响数据存取方式的荒谬现象。

另外,强类型DataSet也有同样的问题(本应是属于数据存取层的,却被传递到业务逻辑层,甚至是展现层)

软件设计中有一个很重要的原则就是:依赖倒置原则(DIP

依赖倒置的意思是:调用者依赖被调用者的接口,而不是实现。

具体到此处就是:业务逻辑层应该依赖数据存取层的接口,而不是具体实现(强类型DataSet)。由于被调用者编程了抽象,而调用者变成了“实现”,所以与普通的面向对象的观念来说,依赖关系被倒置了,因此被称作依赖倒置原则。

依赖倒置在分层结构中是很重要的原则。希望每一个设计者都时刻把它记在心间吧,呵呵。

 

2.     面向接口编程

其实这跟上一个问题有密切联系。面向接口编程,是“依赖于抽象,而不是实现”的具体手段。不管是模块内部,还是个层次之间,面向接口是消除依赖的基础。

举一个简单的例子:本系统中业务逻辑层会调用数据存取层的方法,得到一些数据。比如调用一个PartnerAccess类的GetPartner的方法。PartnerAccess是数据存取层的一个具体类,负责Patrner表的所有增删改查操作。而业务逻辑层到处充斥着这样的语法:PartnerAccess partnerac = new PartnerAccess();partnerac.getPartner();

这就是一个典型的依赖于具体实现的方案。这样的后果是,业务逻辑层知道每一个数据表的数据结构,甚至是无需知道的细节,并且对数据层的每一个方法都了如指掌,到处都在使用。当我们开始修改PartnerAccess的其中一个方法的时候(比如增加一个参数)都要修改业务逻辑层的相关代码,但谁知道那些代码都在哪呢?只好重新编译吧,让编译器告诉我们。

而面向接口编程可以使我们避免这种问题。我们不再依赖于千千万万个PartnerAccess或者什么别的Access类,而是依赖一个IDataAccess的接口。这样,所有的数据存取都被标准化,我们的调用代码便的更简单,不会依赖任何数据库的结构,甚至不需要知道表的名字,有多少个字段等等。当我们增加一个OrderAccess类时,只需在数据存取层增加一个文件,一个类就好了,而不需要更改业务逻辑层的任何代码。

记住这个原则吧,它也可以说是面向对象的核心思想。会让你受益匪浅的!

3.     领域模型不清晰

从上面的图中可以看出来,本系统同时使用了两种领域模型,一种是业务对象(Business Object),一种是强类型DataSetStrong Type DataSet),并且在每个层次中都使用了。

举个简单的例子:强类型DataSet被应用到ASP.NET的控件绑定上,用来显示数据。而业务对象被WebService暴露给客户端。

如果有人看过马丁。福勒的那本《企业架构模式》的话,应该会记得对领域模型的选择上有几种方案。其中业务对象和强类型DataSet都被提到了,并且说明了什么时候适用哪个模型。这里我不多说,感兴趣的朋友可以去看看那本书。

我想说的是,这里使用了两个模型并存的方案。这样就使得系统的领域模型不清晰,而且存在很多的冗余,例如出现了Partner业务对象和PartnerDS强类型DataSet并存的现象,尽管他们各有各的优缺点,但这样势必会造成领域模型的难于维护和代码可读性差的问题。

其实,特殊情况下,也可以两个同时使用,但要注意,由于业务对象是属于业务逻辑层的,而强类型DataSet是数据存取层的,所以他们都要在自己的范围内活动,不能被其它的层次存取。

到这里,有人可能会发现一个矛盾就是:使用单一业务对象的话,则会对数据存取层带来额外的开销,因为数据存取层不能知道业务对象的存在,就需要使用抽象,会带来一些代价。但如果使用单一的强类型DataSet的话,就会对业务逻辑层和展现层保留很多的内部数据细节,也会对系统架构造成一些影响,而且也不利于WebService的传输。

其实,一个合格的设计师,需要对这两种方案做各自的调整,都为自己所用,但只取他们的优势,而避免他们的劣势多带来的麻烦。

软件设计,何尝又不是一种取舍的艺术呢!

 (下一篇

 

posted on 2008-08-13 11:23 zhaojunqi 阅读(3384) 评论(29)  编辑 收藏 网摘 所属分类: 实战心得

评论

#1楼   回复  引用  查看    

期待楼主下面的文章!
2008-08-13 11:27 | zklove1006      

#2楼   回复  引用  查看    

很好的文章
2008-08-13 11:36 | Michael Xu      

#3楼   回复  引用  查看    

我现在有点眼高手低了!
我很清楚分层的目的,但是实现起来却还是把握不住!
为什么??可能是经验不够,一直都在小公司混,看不到大场面吧。
2008-08-13 11:37 | 小猴子      

#4楼   回复  引用    

分层是为了维护,你的功能发生改变了,你的视图层要变,业务逻辑层要变,还有所谓的数据存储层也要变,你的数据库可能也要改变,你的多层比单层维护起来很麻烦
2008-08-13 11:39 | lygwujian[未注册用户]

#5楼[楼主]   回复  引用  查看    

@小猴子
眼高手低不是坏事,眼高才能进步,手低可以练。
2008-08-13 11:42 | 赵俊其      

#6楼   回复  引用    

第三点都不知道想写什么,是dataset和对象尽量不要在一起共用吗?
2008-08-13 11:44 | lygwujian[未注册用户]

#7楼[楼主]   回复  引用  查看    

@lygwujian
不是不能一起共用,是说用的有问题。设计者等于使用了两套领域模型,用DataSet和对象各实现了一遍,他们的结构是相同的,只不过出现在各自适合的地方(DataSet用来绑定DataGrid,对象作为WebService的返回值等),这样冗余的模型是难于维护的。
请参考马丁福勒那本书的相关章节,呵呵。
2008-08-13 11:56 | 赵俊其      

#8楼   回复  引用  查看    

确实精彩
2008-08-13 11:58 | 大李      

#9楼   回复  引用  查看    

说归说,做起来往往都不是那么回事情的,软件开发没有理想主义,也没有绝对的好坏
2008-08-13 12:24 | jjx      

#10楼   回复  引用  查看    

另外,同时使用dataset和域模型也是存在着. dataset 在这里通常就作为一个通用的dto 对象. 省去了重复创建dto的麻烦. 类似ibm 现在的sdo一样. 在分布式应用中,这可能是少不了的一个过程. 域模型在分布式应用中传递是个非常难以掌控的过程
2008-08-13 12:28 | jjx      

#11楼   回复  引用    

怎样才能避免业务对象贯穿于展示层、业务逻辑层和数据存储层,能不能具体描述一下。比如现在有一个业务实体,当然需要从数据库获取数据填充实体,然后在业务逻辑层处理实体,最后显示出来,请问这种情况这样避免业务实体的贯穿问题?
2008-08-13 12:44 | duduniao[未注册用户]

#12楼   回复  引用  查看    

仔细看了一把,楼主 把 依赖倒置思想,发挥得炉火纯青
但小弟,对 领域模型的概念 不是 很清晰
而且有多种领域模型,请指点
2008-08-13 12:53 | 王孟军!      

#13楼   回复  引用    

照楼主的说法,如何实现分层之间的接口编程,如何数据库端出现变化不需要修改表现层和业务逻辑层,来个简单的例子吧,翘盼中!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2008-08-13 13:24 | guest[未注册用户]

#14楼   回复  引用  查看    

照楼主的说法,如何实现分层之间的接口编程,如何数据库端出现变化不需要修改表现层和业务逻辑层,来个简单的例子吧,翘盼中!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-------------------------------------------------------------------
同感,很期待这类型的文章
2008-08-13 13:47 | jailu      

#15楼   回复  引用  查看    

不错,还看不太懂!学习。。
2008-08-13 13:53 | attitude      

#16楼   回复  引用  查看    

13楼的有道理,面向接口不是万能的
2008-08-13 13:59 | 游侠_1      

#17楼   回复  引用    

楼主接口能解决,我跟你姓,把自己看的太高了吧
2008-08-13 14:01 | 4454546[未注册用户]

#18楼   回复  引用  查看    

楼主似乎不是为了OO而OO
2008-08-13 14:06 | 游侠_1      

#19楼   回复  引用  查看    

说得不是很能理解。
2008-08-13 14:11 | 时间太快      

#20楼   回复  引用  查看    

讲的太理论化了,没意思
2008-08-13 14:16 | rockshit      

#21楼   回复  引用  查看    

逻辑混乱,没分清楚概念
2008-08-13 14:17 | rockshit      

#22楼   回复  引用  查看    

本人觉得要根据实际项目,大公司大项目可以搞得复杂一些,小公司小项目没有必要,维护起来更麻烦。
2008-08-13 14:30 | OnlyLiu      

#23楼   回复  引用  查看    

读文章就是踢其糟粕,取其精华
能从中受益就是好文章.
顶了
2008-08-13 15:21 | 言行一      

#24楼   回复  引用  查看    

看了Lz的文章自己有此问题不解:
http://www.cnblogs.com/ASPNET2008/archive/2008/08/13/1266942.html" target="_new">http://www.cnblogs.com/ASPNET2008/archive/2008/08/13/1266942.html
2008-08-13 15:22 | 姜敏      

#25楼   回复  引用  查看    

DTO对象应该可以贯穿各个层次,但是如果是业务对象的话确实不妥
2008-08-13 17:17 | 亚历山大同志      

#26楼   回复  引用    

我感觉分层的好处最大的好处就是保证重用!
2008-08-14 09:51 | winbasic[未注册用户]

#27楼   回复  引用  查看    

WebService在分层架构里面能起到什么作用,怎么样体现,Web Service好像不能返回接口只能返回实体,谢谢!
2008-08-14 11:14 | 赵俊      

#28楼   回复  引用  查看    

好像.Net中的PetShop就是把Model贯穿与几个层之间的,不知道博主看了框架图了没?
对于这么做,我还是没有看明白,希望博主能够解释一下
2008-08-15 22:20 | Leon916      

#29楼   回复  引用  查看    

我现在在公司用的用的开发方法是
一般一个数据库表有他一套类 model data bussiness
data提供获取通常数据的方法,提供一般操作(增加,删除,修改),已经提供通用的查询数据的方法 (将where的条件提供给bussiness)
data类返回的数据如果是一条就是model
如果是一个dataset就是list<model>
达到bussiness调用data的时候不用知道data的表
因为model会提供足够的字段提示
我的bussiness仅仅提起到链接data和aspx。cs的作用
让aspx。cs不用直接调用data
我想问问
既然bussiness的功能就是data的功能
那么bussiness在这里这样写有什么优势呢?
这样写和直接调用data有什么区别呢?
2008-08-21 14:07 | 小眼睛老鼠      
发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1266647




相关文章:

相关链接: