应用程序框架实战七:分层架构的选择

  建立应用程序框架,首先要考虑的问题是,你准备采用哪种分层架构,然后根据应用程序框架的逻辑层次来确定需要创建的VS解决方案和程序集。

  如果项目很小,需求很简单,时间异常紧迫,且你手上没有任何积累,那么,单层架构将是首选,最简单的单层架构如下图所示(为了集中你的注意力,我把不相关的文件都删除了)。

  单层架构的主要优势是代码火力集中,干活直截了当,不像多层架构那样拐弯抹角,每个操作都需要层层传递。对于上图的User.aspx,所有相关代码都直接写到aspx页面或后置代码中,包括界面上的控件操作,业务操作和数据库操作。

  另外,单层架构比较符合初学者的习惯,初学者对编程语法和.Net 基本API尚且不熟,哪会过多考虑代码结构上的问题。

  使用单层架构,主要依赖于.Net强大的控件体系,拖控件,配属性,写事件处理函数,是单层架构下编程的真实写照,这也是.Net程序员在外人眼里一直保持的印象。

  对于主要包含CRUD操作的小项目,单层架构可以工作得很好,但如果项目有一些比较复杂的业务逻辑,比如订单流程 ,会发现代码很快会变成一团烂泥,失去方向。业务逻辑与表现层代码混杂在一起,其结果是界面上的任何修改都可能导致业务逻辑失败。

  为了提升代码复用能力,可以把每个页面都需要处理的操作,比如权限检查,日志跟踪,全局错误处理等,提取到一个PageBase的基类中,放到Base文件夹,另外可以创建一个Helpers文件夹来放置公共操作类。

  单层架构为下一个类似项目所作出的积累微乎其微,业务上大部分代码会被抛弃,有可能是技术的变化,比如之前使用Web Form,现在需要使用Win Form, 由于业务代码和表现层代码高度耦合,很明显无法再使用之前的代码。哪怕技术没有变化,要在之前开发的烂泥中新增和修改功能,将是举步维艰。有些逻辑上看似非常简单的需求,当你实际动手时,发现并不是这么简单,修改一处会牵动更多地方,所谓牵一发而动全身。

  对于技术上的积累,主要通过复制文件的方式供下一个项目使用。一个更好的办法是把技术积累单独提取到一个.Net类库中,如下图所示。

  这样就实现了业务与技术的分离,当下一个项目到来时,只需要把Util.dll引入即可。

  为了挖掘单层架构的极限能力,需要开发一套强大的自定义控件,把数据库操作、验证操作、权限操作等内容全部封装进去,在控件上设置数据库列名,不需要经过DataTable或实体对象,直接生成Sql发送到数据库。如果配上代码生成器,开发简单项目的速度惊人。

  对于单层架构,不论你怎么想尽办法,它对复杂项目都无能为力,使用单层架构来开发业务逻辑复杂的项目,最终得到的就是一团乱麻。提升代码质量可以在一定程度上缓解这些问题,但要从根本上解决,还必须向多层架构靠拢。

  从上面分析可以看到,单层架构不能胜任复杂业务领域,而且复用能力十分有限。由于应用程序框架的目标在于提升技术和业务两方面的代码复用,所以单层架构不是一个合适的选择。

  信息系统从根本上来说,是对数据库的操作,并且需要提供一套操作界面。如果按相关职责划分到不同的层中,会有助于项目的管理和代码清晰度的提升。分层架构是SRP(单一职责原则)的一个应用,把高度相关的东西聚集在一起,把各个层用接口连接起来,这样就能得到一个高内聚、低耦合的设计。

  目前.Net程序员普遍使用的分层架构是传统三层架构,架构示意图如下。

  传统三层架构的核心在业务逻辑层,表现层会把数据放到实体中,作为参数传递给业务逻辑层,业务逻辑层会使用过程式的代码来处理业务,如果需要访问数据库,则调用数据访问层。

  传统三层架构把表现、业务和数据访问分离到不同的层中,当切换表现层技术,或更换数据库时,只需要替换相关的组件即可,不需全部重写。

  天下没有白吃的午餐,在获得清晰度、重用性,扩展性等好处的同时,也带来了一些问题,主要是直观性降低,复杂度提升,工作量增大。在单层架构下实现一个功能,可能只需要一个方法就能完全搞定,理解起来也比较容易,进入三层架构以后,每个操作都被分离打散到不同的层里,每个层有一部分代码,需要在几个层里来回跳动,才能窥其全貌,所以直观性有所降低。另外,任何一个操作,需要在多个地方编写,工作量倍增。

  如果严格按照三层架构的要求来编写代码,表现层尽量不要有业务操作,更不能有数据操作,业务层只管逻辑,这样可以获得比较高的可维护性。但是很多初学者对架构职责没有认识,他们会随便找个地方放置代码,这个地方多半是表现层,这样一来,不仅代码质量很差,架构还很复杂,就得不偿失了。

  传统三层架构特别具有争议的地方是Model实体层。这些实体从表面上看,好像是业务对象,比如客户Customer,仔细观察这个类,发现里面全是属性,没有方法,它用来充当数据容器,在各层之间传递数据。

  有些人一直认为自己是在进行面向对象开发,毕竟c#是个面向对象的语言,又使用了三层架构,还创建了实体,不就是面向对象开发吗?实际上传统三层架构还是面向过程的,只是披上了面向对象的外衣。还有些人对是否面向对象开发不屑一顾,“管它这些理论干啥,咱只搞实际的”,没错,一般程序员确实可以不管这些道理,但如果要追求更高的封装性、复用性,可维护性,就必须向面向对象深入。

  面向对象的核心是根据概念建模,比如客户,虽然传统三层架构在Model层中确实创建了一个Customer类,但是Model层在三层架构中属于辅助地位,是一个可有可无的东西,如果把Customer类换成DataTable也一样可行。

  Model中的实体之所以用处不大,是因为违反了对象的特征,真正的对象是数据与操作的集合。有人把单纯的数据对象称为贫血模型,把具有数据和丰富操作的对象称为充血模型,意指只有属性的数据对象先天不足,营养不良,智商低,只会吃饭(数据),不会干活。

  为何对象一定要把数据和操作放到一起,才能发挥威力?因为这些数据与数据上的操作总是息息相关,且同时变化。把数据和操作封装到一起,可以为操作提供唯一访问点,主要好处是对操作集中管理,消除代码冗余。当数据发生变化时,相关操作多半需要同时修改,由于代码没有冗余副本,且在对象内部完成修改,对外界甚至不产生影响,可维护性大大提升。

  把业务对象的数据和操作分离以后,第一个影响是业务逻辑散乱,难以管理。由于操作没有一个统一的位置,所以没有人知道这些操作究竟位于何处?可能在业务层,也可能在表现层,还有可能在存储过程里。哪怕严格按照三层架构职责编写代码,所有业务逻辑都在业务层中,但可能很多个类都使用了这个实体,究竟某个操作在哪个类里?特别是在团队作战时,这个问题更加明显,如果他不能很容易找到他要的方法,那么他就会自己添加一个,从而导致冗余。第二个影响就是导致明显的代码冗余,而代码冗余是可维护性的天敌。每当数据发生变化或发现Bug,需要找到所有操作代码的副本进行修改,如果有遗漏就会埋下地雷。

  当然,CRUD简单操作不在以上讨论范围,上面主要指业务逻辑受数据的影响程度。

  可以看到,三层架构相比单层架构来说,已经有相当进步,但它还有一些缺陷可以改进,如果把Model实体层和BLL业务逻辑层合并,就可以得到面向对象模型,在《企业应用架构模式》中称为领域模型。不过Eric Evans在《领域驱动设计》中以更具体的方式指导如何使用面向对象进行开发。

  应该采用充血模型(面向对象)、还是贫血模型(面向过程)?要不要使用ORM,要不要使用DDD(领域驱动设计)?这些问题总是争论不休。不过我的建议是,如果你还准备在.Net界多混几年,不如提早进行,因为这是大势所趋,就好像单层架构最终被三层架构所取代。另外,领域驱动设计如果用得不好,充其量也就和三层架构差不多,所以你不需要有任何顾忌。

  本系列文章将采用Entity Framework和DDD分层架构演示应用程序框架的建设,所以本文暂不对DDD进行介绍。

  本文对分层架构的演化过程进行了简单介绍,为你选择适合自己的架构提供了一些参考,最后的结论是,无论你是否愿意,由于软件行业的发展,特别是微软技术的推动,你始终会走上面向对象之路,与其被动挨打,不如主动学习。

  有些朋友发现这个实战系列名不符实,全是废话,一句代码都没有,还有些朋友喜欢四处搜集源码,坐等干货。我想说的是,这个实战系列毕竟是介绍架构和框架的,如果你只拿到几行代码,没有真正搞懂如何为你的项目建立应用程序框架,代码无法形成一个体系结构,还是一团乱麻,那又有什么用。另外,我所提供的代码也是四处搜集整理,没有什么特别,你如果需要源码,直接百度就会多如牛毛,但我希望你能搞懂每个细节, 这样更有帮助。

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

时间: 2024-12-21 12:57:35

应用程序框架实战七:分层架构的选择的相关文章

应用程序框架实战十五:DDD分层架构之领域实体(验证篇)

在应用程序框架实战十四:DDD分层架构之领域实体(基础篇)一文中,我介绍了领域实体的基础,包括标识.相等性比较.输出实体状态等.本文将介绍领域实体的一个核心内容——验证,它是应用程序健壮性的基石.为了完成领域实体的验证,我们在前面已经准备好了验证公共操作类和异常公共操作类. .Net提供的DataAnnotations验证方法非常强大,Mvc会自动将DataAnnotations特性转换为客户端Js验证,从而提升了用户体验.但是客户端验证是靠不住的,因为很容易绕开界面向服务端提交数据,所以服务端

【WePY小程序框架实战四】-使用async&await异步请求数据

[WePY小程序框架实战一]-创建项目 [WePY小程序框架实战二]-页面结构 [WePY小程序框架实战三]-组件传值 async await 是对promise的近一步优化,既解决了promise链式then的这种写法壁垒,又让异步请求更像同步,若对async await不太了解的同学可以直接参考阮一峰老师的文章async 函数的含义和用法,这里我们只关注怎么在小程序wepy架构中如何使用. 依赖库 import 'wepy-async-function' app.wpy中启用 export

Swift项目实战-基于分层架构的多版本iPhone计算器-免费直播公开课邀您参与

Swift项目开发实战-基于分层架构的多版本iPhone计算器-直播课 好消息:买本课程送相关讲师图书+50金币课程优惠券!还有更好消息,为了回馈新老学员,本次采取直播免费,录播收费的形式哦~! 购买本课程赠送关东升老师价值69元国内第一本<Swift开发指南>,并且赠送50元优惠券一张.69+50>99.(购买课程后请联系客服QQ:1575716557) 本课程采用QQ群直播方式进行直播,直播免费看了,完整的基于Swift项目实战,手把手教你做一个Swift版iPhone计算器. 直播

Swift项目开发实战-基于分层架构的多版本iPhone计算器-直播公开课

本课程采用Q Q群直播方式进行直播,价值99元视频课程免费直播.完整的基于Swift项目实战,手把手教你做一个Swift版iPhone计算器.(直播过程也有惊喜!)直播Q Q群:362298485(直播时点击群视频即可进入直播课堂)直播时间:8月26日(周二),9月2日(周四),每天20:00-22:00欢迎咨询客服Q Q:1575716557直播后希望继续深入学习了解本课程可在51CTO学院购买本课程,定价99元.购买课程更有惊喜:买课程送书,送优惠券了! 购买本课程赠送关东升老师价值69元国

应用程序框架实战十三:DDD分层架构之我见(转)

前面介绍了应用程序框架的一个重要组成部分——公共操作类,并提供了一个数据类型转换公共操作类作为示例进行演示.下面准备介绍应用程序框架的另一个重要组成部分,即体系架构支持.你不一定要使用DDD这样的架构,使用单层架构和普通三层架构一样可以,不过你如果希望获得更进一步的复用性和封装度,使用更加面向对象的技术是必经之程. 我在2010年以前还在使用古老的ASP.NET WebForm和原始的Ado.Net.之前我有个观念:.NET技术发展太快,跟着微软屁股后面跑太累,所以只使用它一些原始的东西,自己封

应用程序框架实战十八:DDD分层架构之聚合

前面已经介绍了DDD分层架构的实体和值对象,本文将介绍聚合以及与其高度相关的并发主题. 我在之前已经说过,初学者第一步需要将业务逻辑尽量放到实体或值对象中,给实体“充血”,这样可以让业务逻辑高度内聚,并为你提供业务逻辑的唯一访问点.而聚合则是第二步,它将多个相关业务概念包装到单一的概念中,从而大幅简化系统设计,由于受传统数据建模思维影响,我在聚合方面吃过大亏,花了将近一年才真正用起来,为了你少走弯路,我会把一些要点总结出来供你参考. 什么是聚合? 聚合包装一组高度相关的对象,作为一个数据修改的单

应用程序框架实战十三:DDD分层架构之我见

前面介绍了应用程序框架的一个重要组成部分——公共操作类,并提供了一个数据类型转换公共操作类作为示例进行演示.下面准备介绍应用程序框架的另一个重要组成部分,即体系架构支持.你不一定要使用DDD这样的架构,使用单层架构和普通三层架构一样可以,不过你如果希望获得更进一步的复用性和封装度,使用更加面向对象的技术是必经之程. 我在2010年以前还在使用古老的ASP.NET WebForm和原始的Ado.Net.之前我有个观念:.NET技术发展太快,跟着微软屁股后面跑太累,所以只使用它一些原始的东西,自己封

应用程序框架实战十六:DDD分层架构之值对象(介绍篇)

前面介绍了DDD分层架构的实体,并完成了实体层超类型的开发,同时提供了验证方面的支持.本篇将介绍另一个重要的构造块——值对象,它是聚合中的主要成分. 如果说你已经在使用DDD分层架构,但你却从来没有使用过值对象,这毫不奇怪,因为多年来养成的数据建模思维已经牢牢把你禁锢,以致于你在使用面向对象方式进行开发时,还是以数据为中心. 当我们完成了基本的需求分析以后,如果说需要进行设计,那么你能想到的就是数据库表及表关系的设计,这就是数据建模.数据建模的主要依据是数据库范式设计,根据要求严格程度的递增分为

应用程序框架实战十七:DDD分层架构之值对象(层超类型篇)

上一篇介绍了值对象的基本概念,得到了一些朋友的支持,另外也有一些朋友提出了不同意见.这其实是很自然的事情,设计本来就充满了各种可能性,没有绝对正确的做法,只有更好的实践.但是设计与实践的好与坏,对于不同的人,以及处于不同的环境都有不同的诠释,这是一个仁者见仁,智者见智的问题.DDD非常抽象,以至于它的每一个概念,对于不同的人都有不同的看法,更何况基于DDD的.Net实践,就更难分辨哪一个用法更标准.更正宗. 我对DDD的认识虽然还很肤浅,用得也很山寨,但这可能更加适合初步接触DDD的朋友.还是那