冬Blog

醉心技术、醉心生活
posts - 94, comments - 757, trackbacks - 18, articles - 1
  博客园 :: 首页 :: 新随笔 ::  :: 订阅 订阅 :: 管理

谁决定谁?谁依赖谁?谁拥有谁?

Posted on 2008-06-13 15:58 冬冬 阅读(1879) 评论(33)  编辑 收藏 所属分类: Architecture

在一个软件系统中,谁决定谁?谁依赖谁?谁拥有谁?

其实,归根结底,是个顺序问题:先来的决定、拥有后到的,后到的当然依赖先来的。

从软件结构看,项目起源于需求,终结于主程序。

image

所以需求决定业务逻辑,业务逻辑实现了需求所规定的功能,是需求的直接体现,所以业务逻辑也称为领域模型。

业务逻辑进一步决定数据访问和UI层,其中数据访问服务于业务数据的持久化;UI层负责业务逻辑和用户的沟通。这两者都是细节。

然后,一切准备完毕后,主程序(或者叫系统入口)一声令下,Begin Work!

然后,依赖关系就是反过来的:

主程序(系统入口)显然依赖系统的所有部分。

数据层和UI层依赖业务逻辑。

业务逻辑在整个系统中谁也不依赖,它依赖于系统外的东西——需求!

DongBlog的例子:

image

唯一特殊的就是ASP.Net中的WebSite既是UI又是系统入口(FrontController模式),所以它不仅依赖于业务逻辑、还要依赖于数据访问。

以上的这个依赖关系产生了一个矛盾的地方:业务逻辑一方面决定了数据访问,使得数据访问依赖于业务逻辑,另一方面业务逻辑又需要调用数据访问,岂不是要个环状依赖?怎么办?接口!

在DongBlog系统结构中,Business层拥有两个接口:IEntityDataAccess和IDatabase,分别规定了实体类数据访问的要求和整个数据库的访问要求,借助这两个接口,业务逻辑一方面掌握了数据访问层的生杀大权——让你干什么那就得干什么,另一方面,不需要了解数据访问的实现——知道你能干什么就够了!甚至连数据库是谁实现的都不需要知道!管它谁负责数据持久化,能用就行!

image

就用一个叫做“微型系统”的软件公司来做比喻:公司三个人,老板B(usiness),程序员D(ataAccess)和营销U(I)。老板规定了可怜的程序员D的工作(定义并拥有接口),小D别无选择,让加班就加班,让出差就出差(实现),老板从来不问过程,只看结果(不需要知道细节)。U的待遇好一些,老板一半不太管他,只是常常拍着小U的肩膀说:反正咱们公司能干什么你都清楚,我也就不强制规定了(没有接口),你看着办吧。客户满意就是目的,现在不是强调客户体验嘛~~~~

所以有了接口之后,世界就和谐了。

Feedback

#1楼    回复  引用  查看    

2008-06-13 16:40 by 哦,奇怪      
很好很和谐:)

#2楼    回复  引用  查看    

2008-06-13 17:00 by 坏人      
DAL再依赖回来本身就有问题,并且DAL和BLL为什么不能放一起,不要太死板。

#3楼    回复  引用  查看    

2008-06-13 17:07 by Anders Liu      
第一个图里那三个玩意儿,怎么看怎么像MVC……

#4楼    回复  引用  查看    

2008-06-13 17:15 by Tristan Guo      
请问:业务逻辑=领域模型 吗?

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

2008-06-13 17:25 by 冬冬      
@坏人
将DAL和BLL分开的好处可以在网上搜一下,有很多。

@Anders Liu
哈哈,被你看出来了,其实我也看着像。但还是不太一样。

@Tristan Guo
这两个概念都没有很明确的定义,我个人的看法是领域模型是业务逻辑的表现形式。业务逻辑还可以有别的表现形式,比如《企业应用构架模式》中提到三种:Transaction Script、Table Model和Domain Model。

#6楼    回复  引用  查看    

2008-06-13 17:26 by 小寒      
决定,依赖,这二个关系不是等同的吧
比方说,A决定了B,但B不一定就依赖A
就像你最后说的例子,老板决定员工的工作,但员工的工作不一定就依赖老板
难道没了老板员工就不能工作了???

所以个人认为第二张图有点问题,其实不存在循环的

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

2008-06-13 17:28 by 冬冬      
@小寒
比喻毕竟和原来的事物还是有些差别。

在系统中,如果没有了业务逻辑,数据访问层也就失去了意义,所以说数据访问依赖于业务逻辑。

#8楼    回复  引用  查看    

2008-06-13 17:32 by 紫色阴影      
第二个图
DataAccess为什么要依赖Business?

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

2008-06-13 17:35 by 冬冬      
@紫色阴影
因为DataAccess的工作就是根据Business定义的数据操作来实现数据的持久化,所以DataAccess依赖于Business。

#10楼    回复  引用  查看    

2008-06-13 17:37 by 小寒      
数据访问其实可以不依赖业务逻辑的
每一个数据访问的类都做成一个原子类的访问实体
在业务逻辑层再对原子的类进行组合,组合成业务所需要的实体类和业务逻辑的执行方法
这样数据访层就独立出来了
业务逻辑层只是通过对数据访问层的组合运用来完成相应的功能

#11楼    回复  引用  查看    

2008-06-13 17:43 by 紫色阴影      
DataAccess层应该是纯粹的获取数据,而不应该和业务扯上关系,这样保证层次清楚。
像ls说的,业务层是起着中介者的作用,利用数据层的数据来进行业务处理,或者事务管理等。
数据层依赖的应该是数据对象或者Domain Object

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

2008-06-13 17:50 by 冬冬      
@小寒
如果数据访问不依赖与业务逻辑,哪么业务逻辑调用数据访问时就必须了解数据访问层的工作方式,也就是你说的“原子类的访问实体”,不知道这样理解对不对?

其实“原子类的访问实体”不也是一种接口吗?只是没有显式的写出来。而数据访问和业务逻辑都是遵循这一方式实现或调用。

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

2008-06-13 17:52 by 冬冬      
@紫色阴影
不知道你怎么定义“业务逻辑”和“数据对象”(Domain Object)的?能详细说一下吗?你采用的是不是“贫血对象”的方式?

#14楼    回复  引用    

2008-06-13 18:25 by rubyist [未注册用户]
这篇小文写得含糊不清

#15楼    回复  引用  查看    

2008-06-13 18:33 by 怪怪      
@冬冬
"将DAL和BLL分开的好处可以在网上搜一下,有很多。"

所以坏人才说死板 :)

比如像Blog这样的小程序, 很难说有什么业务逻辑, 仅仅是将Sql语句根据一定规则做出约束, 然后表现为非计算机用户可以接受的界面罢了, 它的业务实际上和数据存取是一而二, 二而一的。

当然不是说Blog或者小程序就不能分层,关键是额外的设计和代码, 在可以预见的时间内, 起到什么样的作用。只是实际上那些天天宣扬的好处, 现在大家都知道, 关键是具体问题具体分析。

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

2008-06-13 18:40 by 冬冬      
@怪怪
我觉得有些误会,这篇文章讨论的是这种分层方法(三层模式)各模块之间关系的问题。至于“分不分”、“还能怎么分”、“什么情况下应该怎么分”不是该文的讨论范围,这些当然还是要具体问题具体分析,呵呵。

#17楼    回复  引用  查看    

2008-06-13 19:10 by 紫色阴影      
@怪怪
的确,如果业务逻辑简单甚至没有,没必要分层了,那只会带来大的工作量

#18楼    回复  引用  查看    

2008-06-13 19:15 by 紫色阴影      
@冬冬
和什么血没有关系
比如,在service里面
void Transfer(user1Id, user2Id, 100)
{
user1 = userDao.find(user1Id);
user2 = userDao.find(user2Id);
user1.total -= 100;
user2.total += 100;
}
我的意思是,这里transfer是业务逻辑,user是domain,userDao是数据访问层对象

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

2008-06-13 19:21 by 冬冬      
@紫色阴影
我的用法是:

var user1 = Database.Users.GetById(1);
var user2 = Database.Users.GetById(2);
user1.GiveMoney(user2, amount, Databae)

user既是逻辑也是的domain。

“贫血对象”指的是只有数据没有逻辑的对象。

#20楼    回复  引用  查看    

2008-06-13 19:22 by 金色海洋(jyk)      
>>业务逻辑一方面决定了数据访问,使得数据访问依赖于业务逻辑,另一方面业务逻辑又需要调用数据访问,岂不是要个环状依赖?

对于这句话很不理解

业务逻辑决定了数据访问
务逻辑调用数据访问

这个矛盾吗?老板规定程序员要实现一个blog,程序员实现了这个blog,老板调用这个blog(或者叫做老板调用程序员写blog,再或者老板下命令:程序员开始实现blog)。这个好像一点都不矛盾吧。

难道说调用就是依赖吗?那么数据访问也没有调用业务逻辑呀,怎么说数据访问依赖于业务逻辑呢?

====================

简单也是要分层的呀,至少分出来一个help呀,先不管help是否可以独立成为一层,分出去是没有商量的。


还有一句不理解:如果说业务逻辑简单,分层会增加工作量的话,那么业务罗架复杂的,分层了反而会减少工作量吗?

#21楼    回复  引用  查看    

2008-06-13 19:27 by 金色海洋(jyk)      
业务逻辑决定了数据库设计,决定了实体类的设计,决定了SQL语句如何编写,决定了UI如何设计,决定了页面如何跳转,决定了审批流程(其它流程)如何去流。

业务逻辑决定一切,呵呵。

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

2008-06-13 19:31 by 冬冬      
@金色海洋(jyk)
这个例子不是这个意思,我想表述的是:老板规定了“I程序员”这个接口,要求有“写程序”这个方法。然后人事部门找一个人满足这个接口,那么,老板就可以调用这个人的“写程序”这个方法了。所以老板规定了“程序员”的“写程序”方法,还会调用这个方法。

依赖是指向的意思,也就是多个DLL之间的Reference。

#23楼    回复  引用  查看    

2008-06-13 20:10 by 金色海洋(jyk)      
对呀,循环了吗?

#24楼    回复  引用  查看    

2008-06-13 20:10 by 金色海洋(jyk)      
多个DLL之间的Reference。

那纯粹是你的设计的问题。

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

2008-06-13 20:17 by 冬冬      
--引用--------------------------------------------------
金色海洋(jyk): 对呀,循环了吗?
--------------------------------------------------------
循环使用接口打破了,都依赖于接口就OK了。


--引用--------------------------------------------------
金色海洋(jyk): 多个DLL之间的Reference。

那纯粹是你的设计的问题。
--------------------------------------------------------
什么问题?

#26楼    回复  引用  查看    

2008-06-13 20:57 by 金色海洋(jyk)      
数据访问为什么要引用业务逻辑的DLL呢?

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

2008-06-13 21:05 by 冬冬      
@金色海洋(jyk)
我的思路是业务逻辑包含领域模型,领域模型不仅仅是数据对象,还包含作用在对象上的方法,像:

var user1 = Database.Users.GetById(1);
var user2 = Database.Users.GetById(2);
user1.GiveMoney(user2, amount, Databae)

user既是逻辑也是的domain。

数据访问层返回的是领域模型:User,也就是ORM的方法。该类型属于Business层,所以数据访问要引用业务逻辑的DLL呢。

还有一个原因是,业务逻辑层定义了数访问层需要实现的接口IEntityDataAccess和IDataAccess,数据访问层需要实现这两个接口,当然也就要引用业务逻辑的DLL。

我猜你是把领域模型作为数据对象来设计的,然后作用在领域模型上的方法单独剥离出来,负责操作领域模型和数据访问,对吗?

#28楼    回复  引用  查看    

2008-06-13 21:58 by 金色海洋(jyk)      
你第我第统统地没有懂。

其实我的是很简单的,我把SQL语句当作业务逻辑来处理,就是组合一个SQL语句交给一个help,就ok了。

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

2008-06-13 22:41 by 冬冬      
@金色海洋(jyk)
用一句我经常给客户说的话做结案陈词吧:“有空面谈!”呵呵o(∩_∩)o...

#30楼    回复  引用  查看    

2008-06-14 02:11 by 紫色阴影      
@冬冬
domain自身有逻辑没有关系,那是属于自身的行为
但是这样写,domain尝试做所有的事情太多,俗称胀血
var user1 = Database.Users.GetById(1);
var user2 = Database.Users.GetById(2);
user1.GiveMoney(user2, amount, Databae)

而且谁来组织这段代码呢,就是你的业务逻辑层
AccountService:

begin transaction
user1 = UserRepository.find(id1);
user2 = UserRepository.find(id2);
user1.transferOut(amount);
user2.transferIn(amount);
submit transaction

User引用DAO或者active Record来持久化
业务逻辑和domain自身的逻辑不要混淆,像事务处理,异常处理都可以放在业务逻辑层去,没有必要都放在domain里。


#31楼    回复  引用  查看    

2008-06-14 02:14 by 紫色阴影      
@冬冬
关于贫血和充血这些很早就已经讨论过了,http://www.cnblogs.com/blusehuang/archive/2007/07/06/domain_model_introduction.html
可以去看看J2EE development without EJB

#32楼    回复  引用  查看    

2008-06-14 07:44 by 金色海洋(jyk)      
我现在在北京,欢迎欢迎.
下个月可能就回沈阳了.

#33楼    回复  引用  查看    

2008-06-15 17:49 by 金色海洋(jyk)      
http://www.cnblogs.com/jyk/archive/2008/06/15/1222637.html
我的数据访问部分的代码,编译成dll,所有的项目都可以直接引用、调用。
传入SQL语句或者存储过程名称就可以了,对于存储过程的参数已封装了一下,更方便调用。

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-06-13 16:19 编辑过


相关链接:

历史上的今天:
2006-06-13 今天很烦