一叶障目

.NET平台上的分层架构(很多朋友称其为“三层架构”),似乎是一个长盛不衰的话题。经常看到许多朋友对其进行分析、探讨、辩论甚至是抨击。笔者在仔细阅读了大量这方面文章后,认为许多朋友在分层架构的理解上存在两个比较大的偏颇:

1.没有从本质角度去理解分层的内涵,而只是了解其表象。

2.对分层架构的理解过于狭隘,只是少数概念,而又不够深入。

许多朋友言“分层”则必称“DAL”、“BLL”、“表示层”等概念,殊不知“DAL”的内部还有“Data Source 架构模式”、“Object-Relational Behavioral 模式”、“Object-Relational Structural 模式”等方面,而其中每个方面下下又有诸多具体模式,如“Data Source 架构模式”又有“Table Data Gateway”、“Row Data Gateway”、“Acitive Record”等等。再说“BLL”,大家都知道“BLL”是“业务逻辑层”,可是什么是“业务逻辑”?“BLL”又可以构建为“Transaction Script”、“Domain Model”、“Table Module”三种模式,各是什么意思?另外,分层也不仅只有“数据访问层”+“业务逻辑层”+“表示层”这一种分法,诸如“服务层”、“持久化层”、“应用控制层”的概念朋友们是否真的熟悉呢。

造成这种现象,我想很大一部分原因是因为大多数.NET平台的开发者(包括我在内)理解分层架构是从Microsoft的PetShop开始的。因为PetShop是官方的Demo,所以被众多.NET开发者奉为圣经,甚至成了.NET平台上分层架构的标准方案。我就曾看到许多朋友在我的博客中留下“分层架构还是PetShop最经典”、“想学分层还是看PetShop吧”、“你这是跟PetShop学得吧”这样的留言。朋友们太崇敬PetShop了,却忽略了一个事实:它仅仅是一个Demo。退一步说,即使它是一个实际应用的项目,这样通过一个具体项目去定义一个抽象概念的方式也是不科学的。

举个例子,一个人不知道“牛”是什么东西,于是请教一位奶牛场管理员,管理员迁出一头奶牛,告诉他:“这就是牛”。从此以后,如果有人问他“牛”是什么,他就会告诉别人“牛”是一种体型庞大,行动笨拙,性格温顺,身上有黑白斑块图案,还有一个好大的咪咪,可以挤奶供人喝。有一天,他听说西班牙有斗牛这项运动,他大惊道:“这怎么可以!牛那么温顺,怎么能用来斗呢!而且牛是用来挤奶喝的啊!”

故事中这个人犯了一个什么错误呢?他把“具体的一头奶牛”和“牛”这个抽象概念给划等号了。他认为牛就是“体型庞大,行动笨拙,性格温顺,身上有黑白斑块图案,还有一个好大的咪咪,可以挤奶供人喝”。殊不知这世界上还有黄牛、水牛、牦牛、斗牛、肉牛等各种牛。他没能做到“透过想象看本质”从而形成抽象概念,而犯了“一叶障目”的错误。

其实,许多朋友之所以对分层架构理解片面或偏颇,是因为与故事中这个人犯了相同的错误。当初,我们不知道何为“分层架构”,于是微软给了我们一个PetShop,说:“看!这就是.NET平台下分层架构的产品。”于是我们“恍然大悟”:“噢!这就是分层架构啊!”。就这样,我们把“分层架构”这样一个内涵和外延都极大的抽象概念和一个具体的Demo划了等号,从而也变成了故事中那个人——我们言分层架构必称DAL、BLL,我们做项目必然依照PetShop方式架构……

我们确实被PetShop“毒害”了。但这不是微软的错,更不是PetShop的错,就像在故事中,我们不能把罪责归咎于奶牛场管理员或那头奶牛。错在我们自己!当微软给我们PetShop时,我们应该在脑中清醒认识到:这是一个分层架构的Demo。而不是理解成了“这就是分层架构”。我们应该钻研、思考,从而抓住分层架构的本质,可是我们没有。与其说我们是被PetShop“毒害”了,倒不如说我们是被自己、被自己那种不良的学习习惯毒害了。我们仅看表象,还是只看了一个表象,然后就冒然对分层架构盖棺定论。而没能透过想象看本质。所以,我们同样犯了“一叶障目”的错误。

以上的错误,笔者也曾经犯过!所以,在下文中,我想和朋友们一起分享一下我在反省自己的过程中,悟出的一些心得体会,希望能借此帮助更多朋友尽快走出“一叶障目”。

洞悉分层的本质

我们可以讨论如何分层,可以讨论分层的利弊,可以讨论分层有没有价值……但在这一切一切讨论之前,我们要先弄清楚一件事:分层的本质是什么?或者说:分层是怎么来的?如果这个问题不明晰,那么我们其他的讨论犹如“浮沙之上筑高台”,再精辟的言辞,如果没有一个牢固的基础,也是站不住脚的。

想要了解分层的本质,就不得不说说分工。分工可以说是劳动生产力上最大的改良,最初分工的好处体现在“比较优势”上,由于各司其职,每个人可以从事其最擅长的劳动,再加上单纯劳动所带来的劳动熟练度提升和减少了更换劳动时的损失,使得劳动生产率大幅提升。然而,随着社会的发展,我们发现某些特殊形式的分工不但可以提高生产力,还有另一些好处!为了理解这些好处,我们举个实际的例子。

今天是六一国际儿童节,一位母亲想给她的女儿买一个奶油蛋糕作为礼物。我们知道,蛋糕需要面粉、需要鸡蛋、需要牛奶等等,还需呀经过一系列复杂的加工和包装过程,但是这位母亲不需要关心这些,她只要去附近的超市直接买就行了。而超市里既没有养鸡场,也没有奶牛场,更没有种小麦的农民伯伯和烘焙蛋糕的工人师傅。这个简单的“买蛋糕”场景,大约可以用下图表示。

图1、制作蛋糕的分工

图1大约说明了一个蛋糕是如何从到达顾客手里的。可以看到,制作蛋糕不是一个单一的劳动,需要许多的分工,如果自底向上看,主要的分工包括:基础物质资料的种植生产、原料加工、蛋糕加工、商业销售。并不是所有分工都如上图这样,上图所示的分工,有一些特点,下面总结一下。

1.下层不知道上层的存在。例如奶牛厂生产牛奶,它不必知道牛奶被拿去做什么,可能被奶油厂收购去做奶油,也可能被雪糕厂收购了做雪糕,也可能被收购去做奶糖,总之,它只管完成自己的职责——生产牛奶,而对于它的上层一无所知。同样,奶油加工厂只管生产奶油,它不必知道奶油被拿去做蛋糕还是做摩卡咖啡。

2.每一层仅仅知道它的下一层(最后一层除外,因为最后一层没有下一层),而不知道另外的下层。例如,蛋糕厂只需知道从面粉厂、奶油厂和鸡蛋厂提取面粉、奶油、鸡蛋就行了,而不必关心面粉是怎么来了、奶油是怎么来的这些问题。

可以说,符合以上两点的分工就是分层架构的思想来源。下面说的稍微正式一点。所谓分层思想,就是这样一种分工:它将系统按不同的职责组织成有序的层次。其中,除最上层外,每一层仅提供若干服务供其相邻的上层使用,但不知道上层的存在;除最下层外,每一层仅调用其临近下层的服务。

所以,所谓“分层思想”,不过是一种特殊的分工形式。而计算机软件架构中的分层思想,是将这一思想应用于软件开发中的特例,而PetShop所使用的“DAL+BLL+PL”的方式,又不过是将这一思想应用于软件开发中的特例的特例。例如,如果某个系统的业务很简单,仅仅是增删改查,那么BLL就没有作用,“DAL+PL”的方式就可以很好完成,这也是很好的分层架构。再如,如果某个系统的业务很复杂,需要先规格化,再做运算,再做整理,那么“DAL+规格化层+计算层+整理层+PL”这种五层架构也是很合理的啊。如果某个系统BLL所暴露的接口太繁杂,那么使用Facade模式在BLL和PL之间加一个“Facade Service Layer”也是很正常的。再者,如果某个系统不需要数据存取功能,例如计算器程序,我们只是想把表示和业务(计算功能)分开,那么就没有DAL了,“BLL+PL”就是合理的。所以,用分层的思想进行架构,本质是“将系统按不同职责组织成有序层次……”这一段话描述的,而不是简单“将系统分成DAL+BLL+PL”,更不是“按PetShop的方式进行架构”。

下面,摘录一段Fowler在《Patterns of Enterprise Application Architecture》中对分层的定义:

When thinking of a system in terms of layers, you imagine the principal subsystem in the software arranged in some form of layer cake,where each layer rests on lower layer. In this scheme the higher layer uses various services defined by lower layer,but lower layer is unaware of the higher layer. Furthemore, each layer usally hides its lower layers from the layers above.

——Martin Fowler, 《Patterns of Enterprise Application Architecture》, P17

大致译文如下:

当我们说一个系统是分层架构的时候,你可以把这个软件想象成一个有很多层的蛋糕的样子,其中每一层放在它的下一层上。较高层使用诸多较低层定义和提供的服务,但较低层并没有察觉较高层的存在。另外,每一层都会对其上层隐藏更低的层。

——马丁 福勒, 《企业应用架构模式》, P17

但是,这里有一点需要声明:虽然说“DAL+BLL+PL”不等价于分层架构,而仅仅是一种实例。但同时我们要清楚的认识到,这个方式之所以如此流行,以至于微软的官方示例都这样架构,是因为对于许多系统,特别是大中型MIS系统,这种架构方式是应该优先考虑的。在这一节中,笔者绝对没有对“DAL+BLL+PL”进行批判的意思,相反,当开发系统时,这种方式可以优先考虑,然后可以根据系统的特点,进行一定得改良。笔者在本节所强调的是:不能把“DAL+BLL+PL”看做分层架构的本质,更不能和“分层架构”这个思想概念划等号。

分层架构的利弊分析

在理解了分层架构的本质的基础上,我们才可以放心大胆的对分层架构进行利弊分析。废话少讲,这一节我们直接切入正题。

分层架构的优点如下:

1.分离开发人员的关注。由于某一层仅仅调用其相邻下一层所提供的服务,所以,只要本层的API和相邻下一层的API定义完整,开发人员在开发某一层时就可以像关注集中于这一层所用的思想、模式、技术,这样,就等同于将分工带来的生产力提高优势引入软件开发。又如买蛋糕的例子,作为超市,只要知道下层API(如何从蛋糕厂获取蛋糕)和本层需要实现的API(把蛋糕销售给客户),就可以制定自己的业务模式很策略计划了,而不必关心如何种小麦、如何磨面粉、如何做奶油、如何做蛋糕等。这样,超市只需进行商业运作,而不必进行产业运作,如此专一,必然提高业务水平。

2.无损替换。想象一下,如果某家奶牛场倒闭了,奶油加工厂也要跟着倒闭吗?当然不会,它可以迅速更换一家奶牛场,因为各个奶牛场都可以实现“提供牛奶”这项服务。再譬如,如果某天国家出台政策,要求所有奶油厂必须从审查合格的奶牛场引进原料,恰好某奶油厂的合作牛奶供应商没能通过审查,那么,只要换一家通过审查的合作就行了。而且奶油厂内部的各个环节一动不用动,因为不同的奶牛场都可以提供“供应牛奶”这个服务。而如果奶油厂自己养牛生产牛奶,一旦遇到这个政策,还得自己去有关部门进行审查,调整相应业务流程,牵一发而动全身。程序中同样的道理,最常听说的可能就是迁移数据库了。

3.降低了系统间的依赖。还是蛋糕那个例子,如果某天蛋糕厂内部换机器了,或业务流程调整了,请问顾客需要关心吗?显然不用,因为顾客只调用超市提供的服务。而超市为顾客隐藏了下面所有产业细节。如果每一个顾客买一样商品,都要了解这个商品从原料生产到成型再到销售的一系列细节,岂不累死了。换做程序中,就如表示层只管调用业务层的服务,至于业务层下还有几层?各种数据是怎么来的?怎么存的?是真实的还是捏造的?都不需要了解,这大大降低了系统各职责之间的依赖。

4.复用。例如,你可以去这个超市买东西,我也可以去这个超市买东西。蛋糕厂可以从面粉厂提取面粉,馒头厂也可以。这样,同样的层就可以为不同的上层提供服务,达到了复用的目的。具体到程序中,例如气象局制作发布了一个“Service Layer”,用于提供天气预告信息。这样新浪、搜狐这些网站可以利用这个服务层提供的服务,制作天气预告页面,QQ也可以利用这个服务在它的聊天工具上添加天气预告,你自己做一个软件需要用到天气预告功能,也可以调用气象台的“Service Layer”。

说罢优点,再来谈谈分层架构的弊端:

1.级联修改问题。这个问题在现实中不好比喻,但在程序中相信很多朋友都明白。例如,一个人事管理系统,本来查看人员信息只能分页查看,而现在,需要增加一个功能:在分页的同时还能分部门。例如,可以查看“销售部的前50个人”,这样,为了这个功能所有层都需要修改。

2.性能问题。本来直来直去的操作,现在要层层传递,势必造成性能的下降。就如在购买蛋糕的例子中。顾客在享受分工带来的便利时,也要承受由于不同层的部门分布各地而造成的蛋糕价格上升,这是因为分层增加了成本,如运输、不同层间部门的协调管理成本等。

纵观以上分析,分层架构有利有弊。这是一定得,世上任何事物都有利弊,所以,把“分层架构捧上天”和“一棍子打死”这两种做法都是不明智也是不科学的。对待分层架构,我们的态度应当是明晰其本质和利弊,然后根据具体情况做出理性的分析和抉择。

从上面的分析可以看出,分层架构可以降低层内变化的成本,而对于API的变化非常敏感。如在级联修改中提到的“在分页的同时还能分部门”的新需求,就是对API进行的变动。API的变动对于分层架构是致命的,修改起来难度非常大。所以,一个简单的判断法则就是:如果您的系统层内频繁变动(甚至整层替换)可能性很大,而API变动可能性很小,就使用分层;而如果API可能会频繁变动,那就要谨慎使用分层架构了。

后面的话

其实,我想说的主要内容,就是前面三节了。不过还是有些话,想和大家唠叨唠叨。

这篇文章,不是一篇技术文章,所以通篇不提技术细节,而只是想帮大家澄清对分层的误解。最近看了很多对分层架构(或三层架构)的探讨,其中以批判居多,有的甚至认为分层就是个没用的垃圾东西。我想,产生这种想法的人,大致经过了以下阶段:听说分层,粗略学习分层、模仿使用分层、用得十分不爽、出来批判。

其实,任何技术都是客观的,都没有错误,错误在人,是人没有正确使用,或没有用到合适的地方。就像我们不能批判刀片不适合劈柴,也不能批判柴刀不适合刮胡子。一项技术想要发挥威力,关键要正确运用,而要正确运用,就需要有深厚的功底,需要我们努力学习,勤于思考。这不是一朝一夕的事情,要有持久的毅力。我们要争取做一个善于用功、善于把握事物本质的人,而不是一个用刀片劈柴、用柴刀刮胡子,然后大骂刀片和柴刀都是垃圾的人。

分层思想从来就不是软件架构中首先提出来的,我们天天上网用到的网络,都遵循OSI七层协议,网络结构的设计是分层思想合理应用的一个典范。另外,在许多其他工程技术领域,分层思想也是很普遍的。所以,不要把分层当成计算机人士甚至软件开发人士独有的能力,相对那些领域,将分层应用于软件架构的技术还很不成熟,还有许多事情等待我们去做。

最后强烈推荐一本书:Martin Fowler的《Patterns of Enterprise Application Architecture》,相信看完这本书,对于分层思想的理解和分层中具体模式的运用决策都会有大幅提高。你会明白,原来分层不是只有“DAL”、“BLL”,原来分层还有怎么多内在的东西。

最后祝大家六一快乐!:)

出处:http://www.cnblogs.com/leoo2sk/archive/2009/06/01/1494095.html

你真的了解分层架构吗?——写给被PetShop"毒害"的朋友们的更多相关文章

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

    在应用程序框架实战十四:DDD分层架构之领域实体(基础篇)一文中,我介绍了领域实体的基础,包括标识.相等性比较.输出实体状态等.本文将介绍领域实体的一个核心内容——验证,它是应用程序健壮性的基石.为了 ...

  2. .NET应用架构设计—重新认识分层架构(现代企业级应用分层架构核心设计要素)

    阅读目录: 1.背景介绍 2.简要回顾下传统三层架构 3.企业级应用分层架构(现代分层架构的基本演变过程) 3.1.服务层中应用契约式设计来解决动态条件不匹配错误(通过契约式设计模式来将问题在线下暴露 ...

  3. .NET应用架构设计—再次了解分层架构(现代企业应用分层架构核心设计元素)

    阅读文件夹: 1.背景介绍 2.简要回想下传统三层架构 3.企业级应用分层架构(现代分层架构的基本演变过程) 3.1.服务层中应用契约式设计来解决动态条件不匹配错误(通过契约式设计模式来将问题在线下暴 ...

  4. DDD分层架构之领域实体(验证篇)

    DDD分层架构之领域实体(验证篇) 在应用程序框架实战十四:DDD分层架构之领域实体(基础篇)一文中,我介绍了领域实体的基础,包括标识.相等性比较.输出实体状态等.本文将介绍领域实体的一个核心内容—— ...

  5. ABP(现代ASP.NET样板开发框架)系列之3、ABP分层架构

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之3.ABP分层架构 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)” ...

  6. Apworks框架实战(四):使用Visual Studio开发面向经典分层架构的应用程序:从EasyMemo案例开始

    时隔一年,继续我们的Apworks框架之旅.在接下来的文章中,我将逐渐向大家介绍如何在Visual Studio中结合Apworks框架,使用ASP.NET Web API和MVC来开发面向经典分层架 ...

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

    前面已经介绍了DDD分层架构的实体和值对象,本文将介绍聚合以及与其高度相关的并发主题. 我在之前已经说过,初学者第一步需要将业务逻辑尽量放到实体或值对象中,给实体“充血”,这样可以让业务逻辑高度内聚, ...

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

    上一篇介绍了值对象的基本概念,得到了一些朋友的支持,另外也有一些朋友提出了不同意见.这其实是很自然的事情,设计本来就充满了各种可能性,没有绝对正确的做法,只有更好的实践.但是设计与实践的好与坏,对于不 ...

  9. 应用程序框架实战十四:DDD分层架构之领域实体(基础篇)

    上一篇,我介绍了自己在DDD分层架构方面的一些感想,本文开始介绍领域层的实体,代码主要参考自<领域驱动设计C#2008实现>,另外参考了网上找到的一些示例代码. 什么是实体 由标识来区分的 ...

随机推荐

  1. 设计模式--命令模式C++实现

    命令模式C++实现 1定义 将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求队列或者记录请求日志,可以提供命令的撤销和恢复功能 2类图 角色描述: Receiver接受者角色,就 ...

  2. IOS-实战分享:实时美颜滤镜是怎样炼成的

    作者:琨君 原文链接:http://www.jianshu.com/p/945fc806a9b4 本文获作者授权转载 背景 前段时间由于项目需求,做了一个基于GPUImage的实时美颜滤镜.现在各种各 ...

  3. SSH 暴力破解趋势——植入的恶意文件属 DDoS 类型的恶意文件最多,接近70%,包括 Ganiw、 Dofloo、Mirai、 Xarcen、 PNScan、 LuaBot、 Ddostf等家族。此外挂机、比特币等挖矿程序占5.21%

    SSH 暴力破解趋势:从云平台向物联网设备迁移 | 云鼎实验室出品 from: http://www.freebuf.com/articles/paper/177473.html 导语:近日,腾讯云发 ...

  4. 【51nod-1289】大鱼吃小鱼(思维)

    这道题我想的方法是用栈,每次去出栈顶两元素,比较是否相对,相对的话合并后压入栈.类似括号匹配. #include <bits/stdc++.h> using namespace std; ...

  5. 使用 Express 实现一个简单的 SPA 静态资源服务器

    背景 限制 SPA 应用已经成为主流,在项目开发阶段产品经理和后端开发同学经常要查看前端页面,下面就是我们团队常用的使用 express 搭建的 SPA 静态资源服务器方案. 为 SPA 应用添加入口 ...

  6. UML_01_画图工具

    一.推荐工具 1.processon 在线画图,类型丰富 www.processon.com 2.StarUML staruml.io 破解方法 StarUML 3.0.2 (Crack + Keyg ...

  7. C++复制控制:拷贝构造函数

    一.拷贝构造函数是一种特殊构造函数,具有单个形参,该形参(常用const修饰)是对该类类型的引用.与默认构造函数一样 ,拷贝构造函数可由编译器隐式调用.拷贝构造函数应用的场合为: (1)根据另一个同类 ...

  8. redis的String类型以及其操作

    Redis的数据类型 String类型以及操作 String是最简单的数据类型,一个key对应一个Value,String类型是二进制安全的.Redis的String可以包含任何数据,比如jpg图片或 ...

  9. 词频统计 ——Java

    github地址 :https://github.com/NSDie/personal-project 一.计划表 PSP2.1 Personal Software Process Stages 预估 ...

  10. 剑指offer--51.表示数值的字符串

    正则好舒服, ------------------------------------------------------------------------------------------ 时间 ...