simonw代码@痕记

应用系统架构设计

应用系统架构设计  Simonw@2005.4.24 如有转载请注明出处。
 

我们在做着表面上看似是对于各种不同应用的开发,其实背后所对应的架构设计都是相对稳定的。在一个好的架构下编程,不仅对于开发人员是一件赏心悦目的事情,更重要的是软件能够表现出一个健康的姿态;而架构设计的不合理,不仅让开发人员受苦受难,软件本身的生命周期更是受到严重威胁。这里我将针对在微软dotNet平台上做应用开发系统的一般架构流程设计做一个粗浅的讨论。

 

总体设计图 

 

表示层

表示层由UI(User Interface)和UI控制逻辑组成。

l         UI(User Interface)

UI是客户端的用户界面,负责从用户方接收命令,请求,数据,传递给业务层处理,然后将结果呈现出来。根据客户端的不同我们大体将应用程序分为BS(Browser-Server) 浏览器结构,CS(Client-Server)桌面客户端结构。

BS的优点是无需操心客户端,只需要部署维护好服务器即可。CS的优点在于强大的界面交互表达能力。RIA(Rich Internet Application)是为了融合这两种结构优点的一种技术,它依赖在客户端一次性安装一个通用解释器之后即获得强大的界面交互表达能力和无需部署具体客户端的方便性。具体的实现技术很多,例如微软的SmartClient, Avalon; Macromedia的Flex;以JS为基础的Bindows;Ajax等等很多。

 

l         UI控制逻辑

UI控制逻辑负责处理UI和业务层之间的数据交互,UI之间状态流程的控制,同时负责简单的数据验证和格式化等功能。具体的说在dotNet事件驱动的编程模型下,UI控制逻辑被自然的实现在了事件函数中,例如PageLoad事件函数,ButtonClick事件函数。在这些事件函数中,主要任务就是做UI控件与业务实体的数据交换与业务调用,但面对大量的数据交换工作量与维护量就成了最大的问题。而在复杂应用的系统中,状态与流程的管理是必须要考虑的因素,它们同样是业务逻辑的一部分,如果不加以封装的直接写在事件函数中将导致业务依赖表示层。下面分别讨论这两个问题。

 

1.         1.UI与业务实体之间的数据交互

此阶段负责数据交换的业务实体称为DTO(Data Transfer Object),处理输入时我们从UI控件的获得数据填入DTO再向下传播,处理输出时用户发出请求业务层会将数据以DTO的形式返出再赋给UI控件展现。因此需要一种方式来自动解决这样的来回赋值问题。遗憾的是dotNet下的不少控件虽然支持数据绑定但仍然没有一个现成完整的解决办法。我们可以自己设计一个Adapter按照某种映射关系来自动处理这样的绑定,这样的映射关系最好是UI控件与DTO属性的事先命名约定,以此种方式的约定作为映射关系无需增加任何配置文件和配置工作即可实现。

 

2.         2.状态与流程的管理

既然是业务逻辑的一部分就不应该耦合再表示层当中。MVC(Model-View-Controller)模式提供了实现这一目标的方法。Controller是整个方案的核心,它是一个流程管理器,来自UI所有的命令与数据经过Controller分发给业务层或其他UI,这样我们可以把流程,权限等逻辑单独封装,例如配置文件中,达到最大化的业务重用。dotNet下MVC的方案并不像Java下有那么多选择,目前有以下几种选择:

微软的UIPAB,它可以处理bs,cs下的流程跳转,可以使得相同的业务系统有webform和winform不同的展现方式。

开源的Mavrick.Net,它只适用于Asp.Net应用程序,它对流程,国际化,页面包装,xslt页面转换提供了很好的支持。

开源的Lattis,同样只适用于Asp.Net应用程序。

 

业务层

业务层封装了实际业务逻辑,包含数据验证,事物处理,权限处理等业务相关操作,是整个应用系统的核心。因此设计一个能够真实反映实际需要的业务层是非常必要的,我们将实际业务具体分为业务数据与业务操作两部分。

 

l         业务数据

业务数据又是业务逻辑的核心,最终业务数据将以一种固定的格式表现于内存中,在系统的各个层次间传输,充当DTO角色。表达业务数据的方式一般分为两种Table Model和Domain Model。

Table Model是将数据库中的表直接映射成为业务数据对象,这样的优点是适合于机器操作,ADO.NET直接提供了这种操作的便利,但对于复杂业务关系的表达就很不直观。只适合于业务需求与数据表对应关系很直接的需要快速开发的情况。通常我们选用Dataset或者强类型Dataset(Strong Typed Dataset),强类型Dataset支持编译时的类型检查,效率上要略高于普通Dataset。Dataset有很多方便的特性:无需自己编写维护类,支持序列化,数据副本保存,支持数据集合,对控件绑定支持效果好,微软提供了相应的生成工具以及持久方案。但缺点也是明显,复杂数据表现不直观,做为DTO在各个层次间传输,尤其是分布式环境,庞大的体积,相对缓慢的实例化对于性能造成很大压力。

Domain Model则是根据实际业务按照现实方式用OO思想建模,这样很适合业务复杂的系统。通常采用自定义数据实体(Custom Data Entity)方式表达。自定义数据实体,有着良好的性能,编译时的类型检查,数据表现方式非常直观符合实际业务的操作方式等优点,但需要自己定义维护类,在分布式环境下需要自己编写序列化方法。

综合各种因素考虑,虽然业务简单对应直接的系统我们以Table Model建模开发效率很高但难免保证系统日后不会变的复杂,因此出于复用性,扩展性,性能等方面选用Domain Model建模为佳。

 

l         业务操作

业务操作负责对业务数据进行各种业务相关的处理,例如验证,流向,整合,事物,权限等,但它不负责有关对数据源的操作。它与业务数据的关系设计有2种方式。

分离业务数据与业务操作,将业务数据单独封装到只有数据get,set的数据类中,这个数据类只充当DTO。将业务操作封装到独立的service类中与业务数据一起充当业务层。这样当系统不复杂的时候显的简单直观,而随着系统日益复杂,service类会变的杂乱,而将本身耦合紧密的数据与操作分离对于复用也是不利的因素。具体可参考Martin Fowler 的贫血的Domain Model一文,但我并不倾向于业务层直接访问数据源。

整合业务数据与业务操作,将业务数据与相关的业务操作封装在一起称为业务实体,业务实体作为统一的业务层为表示层提供服务,同时也负责作为DTO在各个层次间传输,我倾向于这样完整的Domain Model设计方式,每个业务实体都可以做为一个单独组件形式存在,对于组件化复用有着莫大的好处。

 

l         业务模块间的依赖

各个业务模块之间的依赖,有时候会是难以解决的问题,尤其是一些可以重复利用的业务组件,例如权限管理,邮件发送等等。管理好这些各种不同的业务组件是我们的目标,IoC容器为我们提供了最完美的方案,通过它将不同的模块注入到系统中我们可以在不知道这个组件存在的情况下调用它。但目前只有不成熟的Spring.Net一个选择,我们只有一声叹息,因此也就不多讨论了。

 

业务数据访问层

业务数据访问层是一个针对具体应用系统的专属层,它为业务层提供与数据源交互的最小操作方式,仅仅是业务层需要的数据访问接口,业务层完全依赖业务数据访问层所提供的服务。这些服务负责从业务层接收数据或返回业务实体,它屏蔽了实际业务数据与机器存储方式的差别。当然,数据层选用抽象的解决方案同样可以达到这个效果,但业务数据访问层最大的特点就是针对具体业务做抽象,而抽象的数据层访问方案是针对通用做抽象。往往业务中针对具体的设计生命力会变的更强,这样我们可以最大限度的保持了上层代码的复用性,当需要更换存储策略如果数据层访问差别太大,通过更换数据层无法解决问题的时候我们最多只需要更换业务数据访问层,而无需改变业务层。

 

业务数据访问层由DAO(Data Access Object)层和系统服务层两部分组成。DAO层为每个业务实体提供最基本的数据访问服务,系统服务层为系统全局提供与业务关系不大的通用数据访问服务,这两层处于系统中的同一个层次位置。

 

业务层与业务数据访问层关系图

 

 

数据层

数据层的宗旨就是为数据源提供一个可供外界访问的接口,我们应该选用一种能够提供数据源无关的抽象数据访问接口并通过在其下挂接各种不同的DataProviador来访问数据源的数据层组件,这样做便于移植到不同的数据源上。目前有以下3种数据层方案:

 

1.        1. 封装ADO.Net

这些数据访问组件都是基于ADO.Net的浅封装,它的优点在于封装层次低所以速度最快,我们可以手动组织sql语句用来适应复杂的操作以及个性的优化等。缺点是无法直接处理自定义数据实体方式的业务实体对象,需要将业务实体中的数据属性以参数形式传入传出。这样的方式虽然最为保险,但随着系统规模增大,开发效率,质量,,后期的维护,二次开发都变成尤为突出的问题,对开发人员的要求会变的越来越高。另外对于事物操作封装不是很好,无法提供声明性事物,经常会在业务层出现访问数据层的需要。这样的组件目前应用的很广泛,例如微软在EnterpriseLibrary中提供的DAAB(Data Access Application Block),还有以前的DAAB3.1。EnterpriseLibrary是个成熟的产品,包括了数据访问,异常,日志,缓存,加密,配置,安全等组件做为通用服务非常适合。

 

2.        2. OR-Mapping组件

ORM是最好的数据持久解决方案,它的优点在于能够以面向对象的方式操纵数据,因此可以直接处理自定义数据实体的业务对象,我们根本不用操心sql语句以及底层存储方式,这样极大的简化的代码提高了开发效率,对于日后维护扩展都带来极大的便利。缺点在于屏蔽了底层使得我们无法针对具体数据源做优化,而且对于复杂关联的sql操作有些力不从心,同时性能也差一些但辅助以缓存情况会好很多,而在dotNet下最大的问题就是没有一个成熟便宜的ORM产品供我们使用,全部都是beta版本和商业版本。这些版本或多或少都存在一些问题,以至于真正应用中需要经过仔细考察。例如NHibernate,Gentle.Net,XPO,Grove.Net等等非常多。

 

3.        3. DataMapper(SqlMapper)

SqlMapper为以上两种方式提供了一个折中的选择,它可以以面向对象的方式直接处理自定义数据实体的业务对象,同时可以根据与数据源与业务实体的映射关系执行手写的sql语句,这样完全使得我们可以针对具体数据源做优化,对于复杂操作同样可以胜任。目前只有iBatis.Net一个产品,它是一个java移至的开源项目,已经比较成熟,可以在无需编译的情况下随意替换DAO。

 

 

至此,整个架构方案的讨论已经完成,我们可以看出dotNet下可供选择的解决方案是那么的有限,反看Java世界,有那么多成熟可供利用的组件框架,流口水中...不过dotNet也正在走向成熟,我们需要时间等待。这个架构设计的思路只代表了我个人的理解,而且也并不是说所有的开发都是这么一套方案,在具体环境中需要做具体的调整。希望能起到一个抛砖引玉的作用。我的邮箱是i-simon AT msn.com,由于我经验尚浅,有不正确或不足的地方欢迎指正讨论,另外本文将根据技术的最新进展持续更新。

应用系统架构设计-补全版:http://simonw.cnblogs.com/archive/2005/10/27/263145.html

posted on 2005-04-27 10:59 simonw 阅读(23261) 评论(27)  编辑 收藏 所属分类: 开发思想

评论

#1楼  2005-04-27 12:58 James      

good!   回复  引用  查看    

#2楼  2005-04-27 13:40 JustinLee      

好文   回复  引用  查看    

#3楼 [楼主] 2005-04-27 13:56 simonw      

第一次发贴,大家多关照,还不知道随笔和文章有什么区别,摘要怎么放不到首页。   回复  引用  查看    

#4楼  2005-04-27 14:04 dudu      

请看博客园FAQ: http://www.cnblogs.com/dudu/articles/52480.aspx
  回复  引用  查看    

#5楼 [楼主] 2005-04-27 14:23 simonw      

终于搞定了,谢谢dudu:)   回复  引用  查看    

#6楼  2005-04-27 14:38 dudu      

你的文章写得不错!期待你的下一篇!   回复  引用  查看    

#7楼  2005-04-27 16:05 Milestone      

好   回复  引用  查看    

#8楼  2005-04-27 23:17 浅水滩      

好文章,我现在也在研究.NET下的架构,有空交流一下。

现在我碰到的问题最麻烦的就是这个来回赋值的问题。我现在是决定用Maverick.NET来做MVC,它里面有支持DTO自动数据捕获的Controller,但是对于如下拉框(加载)、Datagrid(加载和获取)、复选框(加载和获取)等都没有一个很好的解决方案,不知你是否可以将你的想法再细说一下。


另外,我觉得你的架构中可以补充以下内容,那样可能就更完美了:

1、AOP做权限和日志。

2、还有似乎可以加一层Service Layer,这样可以隔离。

3、我们的设计中将DTO限制在业务层下传播,表现层用Form,有工具专门转行DTO和Form,这样可以实现统一的格式转换,也可以实现偶尔的DTO和页面显示不一致的要求。   回复  引用  查看    

#9楼 [楼主] 2005-04-28 09:55 simonw      

@all 谢谢大家的支持!

@浅水滩

你的blog我早就再看,虽然篇数不多,但样样都是精品,给我的启发很大,很高兴能与你交流。

1。UI与业务之间的绑定
我起初思考的只是针对于大面积的textbox的赋值,这样的话类型固定只需要根据命名上的映射关系即可识别。但如果加入其他类型的控件就需要单独做映射以加入控件的类型信息,比如在业务实体属性上加attribute标示对应的控件名称,类型,当然这样与ui绑定紧密了,写在单独配置文件中也成,具体还没尝试过。

2。大多aop框架都是从java移至过来的成熟性有待验证。对于声明性权限如果用mvc可以通过controller根据用户命令读取权限配置文件来实现,要不除aop也没什么好的办法了。对于声明性事务代替aop的方式,在cs下可以使用Singleton来控制连接。在bs下通过sessionid来标示唯一连接,这个方法正在试验中。

3。Service Layer是不是在表示层下做facade。 其实我觉的业务层就可以做这个facade了

4。增加form倒是不错的方式,不过也需要看具体情况,毕竟多加了一个转换步骤而且这个步骤不好自动完成,因为和具体业务逻辑紧密联系。这样的功能可以放在ui与业务层的转换适配器中,如果具体页面有特殊需求就继承通用adapter实现自己的。

其实很多东西我都没有实践过,只是凭空去设想难免有差错。目前正在挨个排队实践中。   回复  引用  查看    

#10楼  2005-04-28 10:15 hawkwang [未注册用户]

Good article.
I also write a paper to illustrate how to analyze and design application architecture, especially for product line development.
Any suggestion and comments are welcomed :-)

You can access my paper through http://beyondtest.objectis.net/hawkwang/interesting_field/software_analysis_design/software_architecture/illustration.pdf
  回复  引用  查看    

#11楼  2005-04-29 00:59 idior      

welcome to cnblogs.
good article.

btw 投稿被采用.
http://www.alphatom.com/content/view/196/69/   回复  引用  查看    

#12楼  2005-04-30 18:58 shanyou [未注册用户]

好文章,我现在也在研究.NET下的架构,我原来不知道dotnet底下还有这么多MVC的框架,业务模块间的依赖中提到spring.net,其实在dotnet底下还有一个更成熟的框架叫Castle,可以到我的blog上去看看,有空多多和你交流。我的那篇文章叫
DotNet软件开发框架 http://www.donews.net/shanyou/archive/2005/04/23/347792.aspx   回复  引用  查看    

#13楼 [楼主] 2005-05-02 23:28 simonw      

@hawkwang
正在拜读您的文章中...

@shanyou
有空学习castle,希望多交流:)   回复  引用  查看    

#14楼  2005-05-06 16:53 yanghx [未注册用户]

IBatis.net+Castle+ASP.NET+VPM站点事例
http://www.yjsoft.net/Archive/2005/03/03/6.aspx   回复  引用  查看    

#15楼  2005-05-06 20:37 Edwin Yip [未注册用户]

不錯!
長了知識!   回复  引用  查看    

#16楼  2005-12-08 19:52 BillyW [未注册用户]

建议:

DAO 不应该依赖 Business EntityB。Business EntityA依赖它已经足够了。这个依赖会付出维护的代价。

BillyW
Thu Dec 08 19:53:00 2005   回复  引用  查看    

#17楼 [楼主] 2005-12-08 22:57 simonw      

请看本篇的补全篇关于依赖的部分就是再说这个问题。   回复  引用  查看    

#18楼  2006-03-10 13:57 Yorck Zhou [未注册用户]

@浅水滩
现在我碰到的问题最麻烦的就是这个来回赋值的问题。
这个问题可以使用Microsoft .Net 2.0新增加的FormView和DetailView来解决。
参考以下:
http://www.asp.net/QuickStart/aspnet/doc/ctrlref/data/detailsview.aspx   回复  引用  查看    

#19楼 [楼主] 2006-03-10 19:23 simonw      

现在monorails下的databinder功能解决来回赋值问题就是不错的   回复  引用  查看    

#20楼  2007-02-09 10:17 冉露 [未注册用户]

你这个层次模型是架构设计的一个方面,这是软件逻辑架构,软件架构除了逻辑架构之外,还包括系统的物理架构和系统架构,物理架构用来描述系统是如何部署的,有哪些物理设备以及如何协作,系统架构主要描述一些非功能性的特征,如可用性,伸缩性和性能等。   回复  引用  查看    

#21楼  2007-02-17 02:53 海水火焰 [未注册用户]

文章内容不错,不过感觉是书本内容居多!
我最近把自己做架构设计的一个开发模板包装
成产品RKWeb1.1发布了!
这个产品是基于asp.net项目开发的,能有效
的节省60%以上asp.net开发时间,是一个asp.net Web控件集+
数据处理模板的产品,产品与业务逻辑无关。属于应用程序和.net
Framework之间的中间模板。用该模板开发的银行信托系统已经在3家
银行使用,非常稳定。是一个架构设计的实战例子。
有时间大家来谈论一下!
我的blog: blog.csdn.net/hnwanghb
产品网站地址:www.hositech.com

  回复  引用  查看    

#22楼  2007-08-15 16:44 紫珊瑚 [未注册用户]

文章写得很好 对我的帮助很大
但是还有一些问题不是很明白:
例如:
如果在系统架构中加入控制层时应该怎样设计呢 ,我最近在写内部设计 对控制层不清楚   回复  引用  查看    

#23楼 [楼主] 2007-08-16 18:33 simonw      

控制层负责和业务层交互, 比如textbox1.Text=User.Name 或者你控制button的显示或者隐藏等等   回复  引用  查看    

#24楼  2008-03-04 15:33 <>neilzhang      

但业务数据访问层最大的特点就是针对具体业务做抽象,而抽象的数据层访问方案是针对通用做抽象。
==============================

能否详细举例解释下上面这句话?

写的非常好,很多组件都想试试,如果有个具体的Demo就好了。
  回复  引用  查看    

#25楼  2008-03-11 13:34 JACK.Wang [未注册用户]

开发技术,系统分析,架构,项目管理,公司运营,外包,合作
群将满,不发言的踢出
群号:47763528
  回复  引用  查看    

#26楼  2008-04-09 00:07 蓝奇高级验证码识别引擎QQ:631753663 [未注册用户]

出售蓝奇高级验证码识别引擎,可准确识别新浪动网淘宝CSDN等多种复杂验证码。

输出为一个标准DLL,可供VB,VC,Delphi,C#.NET,VB.NET,模拟精灵,按键精灵等多平台调用,调用方法简单,几行代码即可完成。独具特色的边缘检测字符分离、旋转倾斜纠正和通用字符匹配算法(无论字体和大小), 使得该引擎对于像新浪、动网、淘宝、CSDN等多种验证码均有不错的识别率,是一款效果较为理想的验证码识别引擎。附详细的调用实例和代码注释等相关技术文档。

官方网站 - http://www.purejoy.cn/yzm_advocr
识别效果怎么样一试就知道 - DEMO下载 http://www.purejoy.cn/yzm_advocr/advocr.rar
  回复  引用  查看    


标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2005-10-28 02:44 编辑过
 
另存  打印
最新IT新闻:
· 深藏33年Unix操作系统bug被消灭
· Expression Studio 2.0 中文版发布了
· 雅虎首页大变脸 阿里风格上身
· 万名Linux使用者向暴雪请愿Linux版《Diablo III》
· 56.com我乐网将全面转行 退出视频行业