基于.NET平台的分层架构实战(三)——架构概要设计

本文主要是对将要实现的架构进行一个总体的描述,使朋友们对这个架构有个宏观上的认识。这篇文章理论性的东西会偏多一点,从下篇开始,将进行实际项目的开发。这篇文章的许多内容摘自我的毕业论文。

架构基本原则:
这里,将描述一些在这个架构设计中的基本原则,其中很多都是经典的设计原则,不过针对分层架构的特点,用我自己的语言进行了描述。其中也有我自己提出的原则。

逐层调用原则及单向调用原则
现在约定将N层架构的各层依次编号为1、2、…、K、…、N-1、N,其中层的编号越大,则越处在上层。那么,我们设计的架构应该满足以下两个原则:

1.第K(1<K<=N)层只准依赖第K-1层,而不可依赖其他底层。

2.如果P层依赖Q层,则P的编号一定大于Q。

其中第一个原则,保证了依赖的逐层性,及整个架构的依赖是逐层向下的,而不能跨层依赖。第二个原则,则保证了依赖的单向性,及只能上层依赖底层,而不能底层反过来依赖上层。

针对接口编程,而不是针对实现编程
这里所指的接口,不是特指编程语言中的具体语言元素(如C#中由Interface定义的语言接口),而是指一种抽象的,在语义层面上起着接合作用语义体。它的具体实现,可能是接口,可能是抽象类,甚至可能是具体类。
我认为,从不同的视角,接口可以有以下两种定义:

1.接口是一组规则的集合,它规定了实现本接口的类或接口必须拥有的一组规则。体现了自然界“如果你是……则必须能……”的理念。

2.接口是在一定粒度视图上同类事物的抽象表示。注意这里我强调了在一定粒度视图上,因为“同类事物”这个概念是相对的,它因为粒度视图不同而不同。

具体到N层架构中,针对接口编程的意义在部分上是这样的:
现仍约定将N层架构的各层依次编号为1、2、…、K、…、N-1、N,其中层的编号越大,则越处在上层,那么第K层不应该依赖具体一个K-1层,而应该依赖一个K-1层的接口,即在第K层中不应该有K-1层中的某个具体类。

依赖倒置原则
在软件设计原则中,有一种重要的思想叫做依赖倒置。它的核心思想是:不能让高层组件依赖底层组件,而且,不管高层组件和底层组件,两者都应依赖于抽象。
那么,这个原则和我们上面的原则是否矛盾呢?其实并不矛盾。
因为这个原则定义中的“依赖”是指“具体依赖”,而上面定义中的依赖全部指“抽象依赖”。我对这两种依赖的定义如下:

具体依赖——如果P层中有一个或一个以上的地方实例化了Q层中某个具体类,则说P层具体依赖于Q层。

抽象依赖——如果P层没有实例化Q层中的具体类,而是在一个或一个以上的地方实例化了Q层中某个接口,则说P层抽象依赖于Q层,也叫接口依赖于Q层。

从这两个定义可以看到,所谓的依赖倒置原则,正是上面提到针对接口编程,而不是针对实现编程,两者在本质上是统一的。
综上所述,可以看出,本课题设计的分层架构,应该是这样一种架构:

1.N层架构的各层依次编号为1、2、…、K、…、N-1、N,其中层的编号越大,则越处在上层。

2.架构中仅存在一种依赖,即第K层接口依赖第K-1层,其中1<K<=N。

封装变化原则
封装变化的原则定义为:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混杂在一起。

开放-关闭原则
开发-关闭原则定义为:对扩展开放,对修改关闭。
具体到N层架构中,可以描述为:当某一层有了一个新的具体实现时,它应该可以在不修改其他层的情况下,与此新实现无缝连接,顺利交互。

单一归属原则
在这个架构中,任何一个操作类都应该有单一的职责,属于单独的一层,而不能同时担负两种职责或属于多个层次(实体类及辅助类可以被多个层使用,但它们不属于任何一个层,而是独立存在)。

层次划分:
目前,典型的分层架构是三层架构,即自底向上依次是数据访问层、业务逻辑层和表示层。
这种经典架构经历了时间的考验和实践的多次检验,被认为是合理、有效的分层设计,所以,在本文中,将沿袭这种经典架构,使用数据访问层、业务逻辑层和表示层的三层架构体系。

职责划分:
目前,在典型的三层架构中,对层次各自的职责划分并没有一个统一的规范,综合现有的成功实践和.NET平台的特殊性,在本文中将三层架构的职责划分如下:

数据访问层——负责与数据源的交互,即数据的插入、删除、修改以及从数据库中读出数据等操作。对数据的正确性和有效性不负责,对数据的用途不了解,不负担任何业务逻辑。

业务逻辑层——负责系统领域业务的处理,负责逻辑性数据的生成、处理及转换。对流入的逻辑性数据的正确性及有效性负责,对流出的逻辑性数据及用户性数据不负责,对数据的呈现样式不负责。

表示层——负责接收用户的输入、将输出呈现给用户以及访问安全性验证。对流入的数据的正确性和有效性负责,对呈现样式负责,对流出的数据正确性不负责,但负责在数据不正确时给出相应的异常信息。

模块划分及交互设计:
综合以上分析,可在宏观上将整个系统分为一下几个模块:
实体类模块——一组实体类的集合,负责整个系统中数据的封装及传递。
数据访问层接口族——一组接口的集合,表示数据访问层的接口。
业务逻辑层接口族——一组接口的集合,表示业务逻辑层的接口。
数据访问层模块——一组类的集合,完成数据访问层的具体功能,实现数据访问层接口族。
业务逻辑层模块——一组类的集合,完成业务逻辑层的具体功能,实现业务逻辑层接口族。
表示层模块——程序及可视元素的集合,负责完成表示层的具体功能。
IoC容器模块——负责依赖注入的实现。
辅助类模块——完成全局辅助性功能。

各模块间交互关系如下:


图3.1、总体架构图

 

作者:T2噬菌体
出处:http://leoo2sk.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
Tag标签: .NET,分层架构
posted @ 2008-06-17 10:25 EricZhang(T2噬菌体) 阅读(5973) 评论(32)  编辑 收藏 网摘 所属分类: .NET

  回复  引用  查看    
#1楼2008-06-17 10:33 | lele_wangman      
支持下,希望LZ快点写下文哈
  回复  引用  查看    
#2楼2008-06-17 10:40 | 2008年的梦想      
学习中。
  回复  引用  查看    
#3楼2008-06-17 10:55 | 瑞克梅塔      
不错
  回复  引用  查看    
#4楼2008-06-17 11:00 | 小寒      
1.第K(1<K<=N)层只准依赖第K-1层,而不可依赖其他底层。
2.如果P层依赖Q层,则P的编号一定大于Q。

其实就是单向单一依赖关系
那你的公共方法函数这些东西所在的层又如何使用,难道只有一层可以调用他们?

另外,你的那个图中的实体类,属于哪一层调用的
如果说只是单一依赖,那么实体类应该服务于数据访问层
那么你在表示层和业务逻辑层之间有用什么来传递,业务实体类么?
没见有啊

  回复  引用  查看    
#5楼[楼主]2008-06-17 11:05 | T2噬菌体      
--引用--------------------------------------------------
小寒: 1.第K(1&lt;K&lt;=N)层只准依赖第K-1层,而不可依赖其他底层。
2.如果P层依赖Q层,则P的编号一定大于Q。

其实就是单向单一依赖关系
那你的公共方法函数这些东西所在的层又如何使用,难道只有一层可以调用他们?

另外,你的那个图中的实体类,属于哪一层调用的
如果说只是单一依赖,那么实体类应该服务于数据访问层
那么你在表示层和业务逻辑层之间有用什么来传递,业务实体类么?
没见有啊
--------------------------------------------------------

是这样的,您说的公共方法函数(就是我所说的辅助类)和实体类都是独立存在的,不属于任何一个层,这里的依赖主要是指操作类的依赖。辅助类和实体类贯穿与整个系统中,负责公共操作和传值。

表示层和业务逻辑层之间是通过实体传值的,不只是这里,这个架构的所有层次间,甚至JavaScript与后台之间都是使用实体类传值的(通过JOSN序列化和反序列化实现)。实体类和辅助类贯穿整个架构,但是图中并没有明确表示出。

  回复  引用  查看    
#6楼2008-06-17 12:00 | 钢钢      
支持一下
尽快出来代码
  回复  引用  查看    
#8楼2008-06-17 14:19 | Superstone      
不错,不过在这篇里就能看出楼主学生的身份啦,太理论了,也许用作毕业设计正适合不过,支持一下。
  回复  引用  查看    
#9楼2008-06-17 15:17 | 陳龑      
不错,期待下文。。。
楼主可否详细讲解一下IoC在这里怎么用的,谢谢!

  回复  引用  查看    
#10楼[楼主]2008-06-17 15:28 | T2噬菌体      
--引用--------------------------------------------------
Superstone: 不错,不过在这篇里就能看出楼主学生的身份啦,太理论了,也许用作毕业设计正适合不过,支持一下。
--------------------------------------------------------

呵呵,理论结合实践嘛……

  回复  引用  查看    
#11楼[楼主]2008-06-17 15:40 | T2噬菌体      
--引用--------------------------------------------------
陳龑: 不错,期待下文。。。
楼主可否详细讲解一下IoC在这里怎么用的,谢谢!
--------------------------------------------------------

会有专门介绍IoC的,呵呵

  回复  引用  查看    
#12楼2008-06-17 15:55 | 小庄      
晕啊,楼主真是言简意赅啊!
我想问楼主,你业务层的接口是按照什么来划分的?数据访问层的接口呢?难道按照数据库中的表来划分吗?你的类怎么划分呢?
在OOP中就没有“添加,修改,删除”的概念,那是数据库中的东西,ORM的目的之一就是要屏蔽这些东西,在OOP中数据访问层是不存在的,或者说ORM代替了数据访问层,楼主的数据访问层其实就是数据库操作层,业务逻辑层的操作全部都是基于业务逻辑对象的相互作用和相互影响,不是数据库访问层的方法调用!

  回复  引用  查看    
#13楼2008-06-17 16:16 | 赵俊      
楼主写的理论性很强啊!不适合初学者!
  回复  引用  查看    
#14楼[楼主]2008-06-17 16:45 | T2噬菌体      
--引用--------------------------------------------------
小庄: 晕啊,楼主真是言简意赅啊!
我想问楼主,你业务层的接口是按照什么来划分的?数据访问层的接口呢?难道按照数据库中的表来划分吗?你的类怎么划分呢?
在OOP中就没有“添加,修改,删除”的概念,那是数据库中的东西,ORM的目的之一就是要屏蔽这些东西,在OOP中数据访问层是不存在的,或者说ORM代替了数据访问层,楼主的数据访问层其实就是数据库操作层,业务逻辑层的操作全部都是基于业务逻辑对象的相互作用和相互影响,不是数据库访问层的方法调用!

--------------------------------------------------------

欲知详情,请看下文,呵呵……

  回复  引用  查看    
#15楼[楼主]2008-06-17 16:45 | T2噬菌体      
--引用--------------------------------------------------
赵俊: 楼主写的理论性很强啊!不适合初学者!
--------------------------------------------------------

这是唯一一篇理论性的文章,后续文章将主要是实践

  回复  引用  查看    
#16楼2008-06-17 19:30 | Jeffrey Zhao      
原则……很多东西都是可以妥协的……
  回复  引用  查看    
#17楼[楼主]2008-06-17 21:19 | T2噬菌体      
--引用--------------------------------------------------
Jeffrey Zhao: 原则……很多东西都是可以妥协的……
--------------------------------------------------------

呵呵,那当然,任何原则都是死的,而人是活的,具体情况要具体解决

对了,我要在这里感谢你,你所主讲的ASP.NET AJAX 的微软WebCast对我学习这个框架帮助很大,也对我完成我的毕业论文起到了不可替代的作用。真是谢谢你!

  回复  引用  查看    
#18楼2008-06-17 21:48 | airwolf2026      
为啥俺读书的时候,却在玩游戏...而楼主却...-_-!!!...哈哈.俺现在都还和以前的同学连线游戏.
  回复  引用    
#19楼2008-06-17 22:53 | zhengxionghua[未注册用户]
用Nfx类库建立三层应用
http://forum.entlib.net.cn/showtopic-1363.aspx

  回复  引用    
#20楼2008-06-17 23:08 | zhengxionghua[未注册用户]
@小庄
如果"业务逻辑层的操作全部都是基于业务逻辑对象的相互作用和相互影响", 那程序到业务逻辑层就停止了,不会再往下调用了, 你觉得对么?
三层应用继续细分, 确实可以再分个业务外观层, 这样业务逻辑的一部分可以划分出来, 象你称作的 数据库操作层, 但并不是所有的情况下都需要, 问题会被复杂化, 也就是过度涉及的问题范畴了.

使用 Nfx 类库,真正的数据库访问,已经封装到内部,应用程序的DAL层只需要写数据库操作代码,也就是SQL或存储过程,调用基类的Database属性执行即可。然后BLL层控制事务,并在一个事务内调用DAL的一个或多个数据库操作,完成操作。这样,业务外观层的代码,已经被集成到BLL内部,完全不需要再多出一层,内部放很多简单的层间调用。

  回复  引用    
#21楼2008-06-17 23:34 | 编织套管[未注册用户]
浏览器net。
  回复  引用    
#22楼2008-06-18 09:52 | 静静[未注册用户]
快点写下文啦,想看实践。
  回复  引用  查看    
#23楼2008-06-18 11:35 | 小庄      
@zhengxionghua
数据访问层这个叫法在OOP中是不成立的,对OOP来说我们需要的只是业务实体的持久化,在业务层需要的时候调用一下持久化的方法(例如submitchange)就可以了,用业务层来调用数据访问层的添加修改删除操作方法是违背OOP的。

  回复  引用    
#24楼2008-06-18 12:28 | zhengxionghua[未注册用户]
@小庄
submitchange 如果不调用数据访问层的添加修改删除操作方法, 行么?

  回复  引用  查看    
#25楼2008-06-19 12:34 | Leon916      
博主写的很好,看了后很有感触。我这里有一个不情之请,你是否能公开你的论文,给我发一份。谢谢
  回复  引用  查看    
#26楼2008-06-19 16:21 | yuyoo      
楼主分析的很好,继续......
  回复  引用  查看    
#27楼2008-06-19 16:43 | yuyoo      
我们在实际的开发工作中,很多的时候把太多的时间放在具体的某个技术的实现上而忽略了整体的设计,汗啊......
  回复  引用  查看    
#28楼2009-03-03 09:49 | MIDI      
感觉像是面向对象原则的N层架构应用。呵呵,不过可以肯定的是楼主EQ很高。。。
  回复  引用    
#29楼2009-03-13 14:58 | 游客_cwx[未注册用户]
1.第K(1<K<=N)层只准依赖第K-1层,而不可依赖其他底层。
已经说明了逐层而且单向,可推论出
2.如果P层依赖Q层,则P的编号一定大于Q。

有必要分成两点吗?

  回复  引用  查看    
#30楼2009-03-14 11:37 | Ryanism      
业务逻辑层——负责系统领域业务的处理,负责逻辑性数据的生成、处理及转换。对流入的逻辑性数据的正确性及有效性负责,对流出的逻辑性数据及用户性数据不负责,对数据的呈现样式不负责。
表示层——负责接收用户的输入、将输出呈现给用户以及访问安全性验证。对流入的数据的正确性和有效性负责,对呈现样式负责,对流出的数据正确性不负责,但负责在数据不正确时给出相应的异常信息。

为什么这两个层都要对流入数据的正确性及有效性负责?另外,这两层都不对流出数据的正确性负责的话,那该由谁来负责呢?

  回复  引用  查看    
#31楼2009-04-07 17:24 | basibasi      
其中层的编号越大,则越处在上层,那么第K层不应该依赖具体一个K-1层,而应该依赖一个K-1层的接口


我喜欢这句话

  回复  引用  查看    
#32楼2009-04-18 22:21 | KAYAK      
引用:
“ 1.第K(1<K<=N)层只准依赖第K-1层,而不可依赖其他底层。
2.如果P层依赖Q层,则P的编号一定大于Q。
其中第一个原则,保证了依赖的逐层性,及整个架构的依赖是逐层向下的,而 不能跨层依赖。第二个原则,则保证了依赖的单向性,及只能上层依赖底层,而不能底层反过来依赖上层。”

感觉第1条已经说明了逐层向下和依赖的单向性这连个原则了。第K
层只准依赖第K-1层,k肯定大于K-1的呀,呵呵。




发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

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

0 1223559




相关文章:

相关链接: