`
taowen
  • 浏览: 190623 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

领域模型的价值与困境

阅读更多
很久以前大家就关于这个方面有很多讨论了。前两天我又挖了一个坑来集思广益,非常感谢没有把我的帖子投为新手帖的同志。我不是在装傻,只是想让大家跳出自己的立场,从根本的价值出发来考虑问题。之前有很多讨论,都是在讨论我又发明了一种新方法可以让领域模型充血啦,等等之类的。当提出一个解决方案的时候,一定要有明确的问题。那么领域模型的价值是什么?为什么没有被广泛应用,其困境在哪里?

价值

数据,一定是数据。做企业系统,最核心的东西一定是数据。关于数据,人们有许多需求,但是最根本的一点就是,数据要是对的。在关系数据库的上下文下,为了保证数据是对的,我们有外键,我们有COLUMN的数据类型,我们有主键,我们有constraint,我们有很多很多。但是很多时候还不够,一堆数据在业务上是不是合法的,超过了上述的检查方法的能力范畴。这个时候,以DBA为中心的思考就会导致:我作为DBA,管理这些数据,如果数据出了问题,那就是我的责任了。所以我必须要阻止愚蠢的事情,而我显然是最知道什么是正确数据的人,所以你们(程序员)要访问我的数据,就必须通过我的存储过程。

这种方式显然遇到了问题。问题是很多方面的,有人员素质问题,有工具支持问题。更重要的是,虽然存储过程起到了防火墙的作用,阻挡了外界可能的对数据一致性的破坏,但是其内部却是脆弱的。数据对于包裹它的存储过程都是开放的,写存储过程A的人,可能对数据的假设与写存储过程B的人对数据假设是不一致的。两个人必然只有一个是正确的,但是从数据出发找到修改它的地方并不容易,从而给数据的质量埋下了隐患。

存储过程的问题,就是面向过程的代表。面向对象的主要特征,封装就是为了解决这个问题发明的。把数据放置于对象内部,要修改对象所封装的数据,就必须通过对象所提供的外在行为。有如下图所示。



回到数据的正确性这个问题。程序员不同于DBA,给出的解决方案是领域模型。其实领域模型,只是面向对象的另外一个名字而已。通过把数据封装在领域模型的内部,我们就可以限制模型的使用者对数据的修改,什么值是对的,什么样的值是不对的。具体列出来有:

构造函数
可以确保在创建的时候已经有了所有的必填项
public class Person {
  public Person(String firstName, String lastName) {
  ...
  }
..
}


无Set方法
不能任意的改变值,必须通过特定的合法性检验
public class Publication {
  private State currentState;
  public State publish(Channel to) {
  ...
  }
...
}


关联
可以保证外键,以及强制约束两个表之间数据的关系
public class Cargo {
  public void attachItinerary(final Itinerary itinerary) {
    // Decouple the old itinerary from this cargo
    itinerary().setCargo(null);
    // Couple this cargo and the new itinerary
    this.itinerary = itinerary;
    this.itinerary.setCargo(this);
  }
...
}


一致性
冗余字段的同步更新得到强制
public class ShoppingChart {
  private List<OrderItem> items;
  private int sum; //冗余字段
  public void dropIntoChart(OrderItem newItem) {
    sum += newItem.sum();
    items.add(newItem);
  }
  ...
}


当然,面向对象不光是封装一个特性,它还有继承和多态。所以作为面向对象的另外一个名字,它自然也有继承和多态这个好处。具体到程序里就是

枚举值
不要通过对枚举值的判断来决定程序的路径
// 过去
public void publish(ChannelType channelType, Publication publication) {
  if (channelType.equals(ChannelType.RETUERS)) {
    ...
  } else if (channelType.equals(ChannelType.BLOOMBERG)) {
    ...
  } ...
}
//现在
public interface Channel {
  void publish(Publication publication);
  ...
}
public class RetuersChannel implements Channel {
  ...
}
public class BloombergChannel implements Channel {
  ...
}


数据的含义
另外一个好处是,对数据的访问被集中起来了。所以,从数据出发,很容易发现计算出值并修改它的地方。这就方便了我们去理解数据的含义。数据本身是没有任何意义的,数据只有被使用才有意义。只有理解了数据的上下文的含义,才能编写更多的行为去操作数据。在写新的行为的时候,我们必然要参考过去的行为是怎么理解数据的含义的。这个查找的过程越容易,越有助于我们写出正确的逻辑,也越有助于我们发现过去已经写过一样的行为了,那我就不用写了,也就是所谓的复用。

所以,理论上来说,面向对象或者说领域模型是非常适合我们的日常的企业信息系统开发工作的。但是,实践中,却遇到了很多问题。

困境

框架的约束
如Robin所言
robin 写道
如果你用的是Spring,没啥说的,必须贫血,你想充血也充不起来;
如果你用的是RoR,也没啥说的,直接充血,你想贫血也未必贫得下来;

这就是一个基本事实。Spring作者也坦言(Rod Johnson, JAOO, 2006),Spring的编程模型基本上是EJB的延续。从架构和分层的角度,它们是一脉相承的。这种分层的架构决定了,行为在Service里,数据在Entity里。这种做法成为“最佳实现”,不是偶然的,是框架设计给你的必然结果。矛盾的集中体现在于Entity无法被注入(当然你可以注入,但是这是不推荐的做法)。Spring后来尝试修复这个问题,引入了AspectJ来做Entity的注入。不过仍然不是主流,因为框架的阻碍只是一个小问题,更大的问题在于这个Java的OOP实现本身就有问题。

语言的约束
在很多的讨论中,反对“充血”领域模型的同志都提到。把逻辑集中到领域模型中,会造成类的膨胀。在我个人的实践中,近千行的Entity类定义也是有的。而且这些行为往往不稳定,往往流程高度相关,复用程度并不是想象的那么高。
原因是因为,数据并没有所谓的内在行为。你不用它,它就没有行为。所以数据有什么行为,其实是使用者赋予的。而同样的数据往往会在不同的场合(context)下被使用。正如这位同志所言:
coolnight 写道
我们的系统有很多模块组成, 各模块基本上通过数据库来共享信息。
主要的模块大致有: 核心系统(非web), 网站、 bbs、 网站后台,核心系统后台,BI, 推广员等等
原来的打算是写个rich domain model供各模块来使用,以便代码重用减轻个模块开发的工作量
一个简单的例子, User 原有 changePassword, getFriends, addFriend ... 等等方法撇开配置以及获取User对象的复杂性不谈, 实际开发中发现, 这些东西重用的地方太少了,对网站来说很好的rich domain model, 在网站后台里面就麻烦,很多方法在那里根本就是对后台开发人员的干扰,而很多方法对核心系统、BI等等根本就毫无用处。

所以上面所画的图就得改一下了:



同样,很多同志也发现了这个问题,并给出了解决方案
Quake Wang 写道
用mixin实现领域模型是最简洁的做法, 但是受到Java语言的限制,得用大量mark性质的interface和composite模式,反而会让整个结构变得复杂。
相比C#的namespace或ruby自定义dsl(acts_as_xxx),在java玩领域模型需要更多的功力,推荐一下Rickard Öberg的qi4j,它将composite oriented发挥到了极致:
http://www.qi4j.org/

ray_linn 写道
我建议用C#的扩展方法,或者Java用mimix(不知道打错没有),Domain还是POJO,方法在适当的时候“黏合”(C#通过 namespace引用)到POJO上,让POJO “充血”,而在充当VO,DTO的时候,POJO又可以“放血”回到“贫血的”状态上。

这个问题基本上是暴露了Java的OOP实现的缺陷。C#的Partial Class, Extension Method和Ruby的Mixin就是对这种“一个class定义一切”的做法的改进,允许行为被分片定义,而不是集中定义在一个文件中。

现状
很多同学都谈到现状不是完全的没有领域模型,而是所谓的贫血的领域模型。之所以会这样,就是前面所说的困境。正如很多朋友所说的,这未必是一件坏事,比如可以避免核心的domain过度膨胀。不过也未必是一件好事,理由我能想到这么几点:

越俎代庖
这是我们的DAO经常干的事情。比如我们有两个domain,Publication(一份文档)和Distribution(一次分发)。它们两者之间是聚合关系,也就是distribution必然属于一份publication。如果是重写的领域模型,我们可以通过publication来控制对distribution数据的添加。比如
public void distribute() {
  if (isDeleted()) {
     throw new InvalidDistributionException();
  }
  distributions.add(new Distribution(this));
  lastDistributionDate = new Date();
}

但是一旦有了DistributionDao,就不一样了。
distributionDao.save(new Distribution(somePublication));

哈哈,啥控制都管不了我了吧。很多时候Dao就是这么无法无天的。如果说domain model是数据的防火墙,那么dao就是顶级黑客。由于Entity不能注入等实现和宗教信仰上的限制,往往导致了service对DAO的滥用,就会造成上面这样的by-pass domain model的情况。

舍近求远
明明我有一个contact
public class Contact {
  private List<ContactNote> contactNotes;
...
}

但是我不能直接去取我的contactNotes(性能啊性能,hibernate会load all啊)。所以我得写个查询
from ContactNote where contacted = thisContact and ...

我有一个contact就应该能够让我
contact.allMeeetingContactNotes();

但是不行,你得
contactNoteDao.findMeetingContactNoteByContact(thisContact);

舍近求远也

职责混杂
根据我理解的Service层的现状是:有一部分是作为domain model对外的接口,提供了事务安全等服务,真正是一个“service”对web层提供服务;有的呢,则是当作一些可重用的行为被注入到其他的service之中;而有的呢,则纯粹是一些utils。当然,不过可能是我用的不对吧,或者理解有误。

距离啊距离
数据和行为距离很远。造成了不容易理解数据含义,因为你光看entity压根不知道它会被怎么用。漏掉一些servie中细微的用法,就可能会造成很大的bug。
同时这也更加让可重用的逻辑更难被发现,从一定程度上鼓励了大家各自发明,一份逻辑写n遍,还n遍都不一样。。。
当然啦,这个问题没有想象的那么大,毕竟贫血的领域模型,还是鼓励大家把“只和这个对象,不和外部接口有关的”的逻辑放到对象本身的。问题的大小,取决于团队的稳定性,素质和职业操守。

术语
如nihongye同学所说,xxxService不是domain language。当然啦,讨论这种问题最终是仁者见仁智者见智的。

解困

你真的要解困吗?其实你未必被困住了。你可能根本不需要领域模型,特别是在Java/Spring这种实现下,难以实现。正如很多同志所言,我用贫血模型用得很好。那就行,自己好,就是真的好。但是有朝一日,觉得贫血模型不再适合你了,不妨去了解了解qi4j(如果你不想换语言),或者投入rails的怀抱吧。
  • 大小: 7.9 KB
  • 大小: 12.8 KB
分享到:
评论
45 楼 redhat 2009-11-03  
pufan 写道

在我看来,服务即契约,面向服务设计即面向契约设计,契约可大可小,大至异构系统间互通的设计,小至一个方法的定义,都应该以契约驱动,将变化封装,何有粗细粒度之分。

这里讨论的粗细粒度是指所提供的接口能够解决粗粒度的问题,我不想让你的暴露接口解决问题时,比如发送一份邮件,别人要调用你的暴露的其他接口例如: 判断这个邮件有没有发件人,有没有收件人,然后调用你的send接口发邮件。而应该暴露一个借口sendMail即可。不懂得粒度的粗细 ,如何设计程序? 举个很简单的j2ee应用的例子:难道你没有使用过任何session fasade模式?

pufan 写道

你举的是产品的面向第三方接口的例子,先不说目前产品和项目的应用谁多谁少,就拿上述产品例子中访问数据库的代码量来说,需要DAO设计的代码有其他代码20%吗?
按28原则视之,以20%的可能性要求剩下80%也要如此设计,这不是大忽悠是什么?

首先,dao层的出现并不只是为了切换不同的数据库,如果这样有hibernate就够你使用了,dao层并未由于hibernate兴起而不复存在,因为他们是解决不同的问题产生的!
第二,这也不代表你完全有权利选择一种orm工具,其他orm工具你有权利不选(特别是“政治”问题) 。
第三,dao的提炼,可以让domain和service更关注自己的业务,每一个对象功能最好专一,完成自己本分的事情,这是面向对象语言设计的基本原则!
第四,你的20-80原则只可能说明你开发设计的软件具有此特征,不需要dao层可能适合你的项目,不一定适合其他所有项目!特别是复杂的,和大型电子商务项目!
44 楼 timshaw9791 2009-09-19  
引用
很久以前大家就关于这个方面有很多讨论了。前两天我又挖了一个坑来集思广益,非常感谢没有把我的帖子投为新手帖的同志。我不是在装傻,只是想让大家跳出自己的立场,从根本的价值出发来考虑问题。之前有很多讨论,都是在讨论我又发明了一种新方法可以让领域模型充血啦,等等之类的。当提出一个解决方案的时候,一定要有明确的问题。那么领域模型的价值是什么?为什么没有被广泛应用,其困境在哪里?


你认为DDD的价值在哪里?广泛应用大概是个什么景象?

我说点自己的想法,
1.DDD中的Domain Model是一个概念模型 ,ddd中说这要求我们用通用语言来交流它感知它,可赶上了时候咱们基本上用oo来分析它(DDD可没说一定要OO,倒是提了UML和XP的不好),用java来实现它,这里隔着一层加一层呢,然后我们对着java代码平头论足这块贫血这个充血,还有指着一滩POJO说是Domain Model的,似乎有点刻舟求剑的意味。

2.我认为DDD那本书提出的那个应用框架让我原来的想法更加清晰更加系统。这就是DDD暂时给我这样的程序员最大的好处,但同时我认为他也并没有提出其他什么新鲜东西(但估计制止了需求人员抱着UML吓唬客户的歪风邪气),可能当年书刚出来的时候可能是。有人说DDD让大伙儿让设计更贴近领域需求,难道在这之前我们的需求人员就不是站在客户的角度考虑问题么?OO不是用来更好的模拟显示世界的是非曲直么,DDD顶多算是再提一次醒吧了,然后我们继续OO,跟他有p关系。在我心中,感觉比OO的横空出世差老鼻子远了,感觉,只是感觉啊,这段大家别拍


43 楼 pufan 2009-09-15  
redhat 写道

不是搞组件开发的玩充血就是自找没趣,还是SOA思想来的实在。
这点我觉得你没有理解soa要解决的问题,soa是更具粗粒度(相对于组件),这个只是对于暴露给其他客户端(相对概念)统一借口,并不包含实现,我以为,在soa设计中,考虑的是如何暴露你服务接口,而这些服务的具体实现,并不会全是采用面向服务的设计(其实根本很少,意义不大,只能造成比ejb更尴尬的局面),也就是说,soa主要关注于与外界粗粒度的通信的接口,如何提供恰当的粗粒度。

SOA到底要解决什么问题?不要被什么市场化的SOA宣传理论所迷惑,那都是忽悠至上的言论。

在我看来,服务即契约,面向服务设计即面向契约设计,契约可大可小,大至异构系统间互通的设计,小至一个方法的定义,都应该以契约驱动,将变化封装,何有粗细粒度之分。

redhat 写道


DAO也是个大忽悠
这个只能说明你们目前面向的应用和业务太少,不能说明其他的问题,或者在你们的项目确实没必要,当然,我不认为一个好项目必须提供dao层。orm的更换应该是很多人遇到的,主要和第三方已有的软件结合,或者是必须和第三方软件结合(可以说是政治问题),这样,往往导致dao层更换,但是服务层是不会受影响的。

你举的是产品的面向第三方接口的例子,先不说目前产品和项目的应用谁多谁少,就拿上述产品例子中访问数据库的代码量来说,需要DAO设计的代码有其他代码20%吗?

按28原则视之,以20%的可能性要求剩下80%也要如此设计,这不是大忽悠是什么?


42 楼 redhat 2009-06-08  
pufan 写道
企业应用?OO? 切莫被flower给忽悠了,不是搞组件开发的玩充血就是自找没趣,还是SOA思想来的实在。

DAO也是个大忽悠,持久层更换的需要,谁遇到过。

还有那个qi4j,写个helloworld都复杂无比,知道有这么个东西就行了,嘿嘿






不是搞组件开发的玩充血就是自找没趣,还是SOA思想来的实在。
这点我觉得你没有理解soa要解决的问题,soa是更具粗粒度(相对于组件),这个只是对于暴露给其他客户端(相对概念)统一借口,并不包含实现,我以为,在soa设计中,考虑的是如何暴露你服务接口,而这些服务的具体实现,并不会全是采用面向服务的设计(其实根本很少,意义不大,只能造成比ejb更尴尬的局面),也就是说,soa主要关注于与外界粗粒度的通信的接口,如何提供恰当的粗粒度。


DAO也是个大忽悠
这个只能说明你们目前面向的应用和业务太少,不能说明其他的问题,或者在你们的项目确实没必要,当然,我不认为一个好项目必须提供dao层。orm的更换应该是很多人遇到的,主要和第三方已有的软件结合,或者是必须和第三方软件结合(可以说是政治问题),这样,往往导致dao层更换,但是服务层是不会受影响的。

当然,最近ror的兴起,使得传统模式的企业开发又一次面临挑战

41 楼 lichuan 2009-02-14  
pufan 写道
企业应用?OO? 切莫被flower给忽悠了,不是搞组件开发的玩充血就是自找没趣,还是SOA思想来的实在。

DAO也是个大忽悠,持久层更换的需要,谁遇到过。

还有那个qi4j,写个helloworld都复杂无比,知道有这么个东西就行了,嘿嘿






我大概片面理解你的话,在我看来DAO的实现取决于数据的用例。比如,至少可以列举几种实现:

1. SQL DAO,涉及复杂查询的小型数据
2. File DAO, 涉及简单查询的大型数据(比如多媒体)
3. LDAP DAO,涉及集成现有用户认证系统的用户数据
4. MEM DAO,少量频繁访问的数据
5 DUMMY DAO : 0

等等
40 楼 unsid 2009-02-12  
qi4j莫非是那个用java写函数式程序的组件?
39 楼 pufan 2009-02-12  
企业应用?OO? 切莫被flower给忽悠了,不是搞组件开发的玩充血就是自找没趣,还是SOA思想来的实在。

DAO也是个大忽悠,持久层更换的需要,谁遇到过。

还有那个qi4j,写个helloworld都复杂无比,知道有这么个东西就行了,嘿嘿




38 楼 redhat 2009-01-24  
taowen 写道

价值
数据,一定是数据。做企业系统,最核心的东西一定是数据。


我这边想表达一下我的看法:
数据是我们企业系统的要管理的对象,一个软件最核心的是如何“有效的管理数据”,而不“只是”保证数据正确,保证数据的正确,不管难度多大,都比较是低层面的。


领域模型,我认为它建立起来的一个原因是OO的发展,使用OO来接解决一些问题。
领域模型关注的地方,在于汲取业务知识,复杂问题简单化,抽象成建立清晰简单的模型。

领域建模,我觉得,领域模型比较能够解决复杂业务的东西,但是,开发比较慢,因为,他要不断提纯模型,是的模型能够准确清晰的表达业务含义。

但是一个有效的模型的建立,会使得以后解决问题都有清晰的思路。

对于领域模型,处理类的方法膨胀问题,方法有很多种。
但是,大多数出现的问题,都是对于领域没有提纯好,本来的两个domian的问题,非的一个domain类处理。

我个人认为:
对于领域模型使用上的最大问题,是如何建立良好模型的问题,这里会有好几个问题一般会遇到:
1.把那些方法认为是service的,那些认为是domain的,这个robin说过(我理解的大致是:一般性的要到dao的方法),eric也说过(我理解的大致是:那些是过程性的行为,比如营业员的柜台收钱,适合service)。
2.类里的方法膨胀,我认为大多是没有提取好业务模型所致,本来的两个domian的问题,非的一个domain类处理。还有些本身就很庞大的类。需要借助pub/utils 这些来避免,当然还有很多其他途径,建议看完ddd之后,“应用它所提出的方法”搞完一个项目之后,再做评价。

看了一些评论,发现大多数只是简单的做过一些很简单的实验性的东西,不能够说明很多问题。




37 楼 fxwdl 2009-01-21  
xredleaf 写道
据个例子,我有个Member类,对应数据库的member表,我页面上有个grid,列里显示出member表的全部列,这个Member类分个人会员和企业会员,如果是企业会员的话我要去查企业会员表,个人会员的话个人会员表,我这个domain model调用了dao去查数据库,domain model里可不可以去调用dao查数据库(原因?),我这里该怎么改才比较好。

会员类
pubic class Member {
    private Long id;
    private Integer type; //会员类型 (1,个人会员 2,企业会员)
    ......
    private ICompanyMemberDao companyMemberDao = BeanUtil.getBean("companyMemberDao");

    ......

    /**
     * 取得公司的名称
     */
    public String getCompanyName() {
        if (type == 1) {
            return getPerson().getPersonName();
        } else {
            return getCompanyMember().getCompanyName();
        }
    }
    
    /**
     * 获取企业会员
     */
    public CompanyMember getCompanyMember() {
        if (this.companyMember == null) {
            this.companyMember = companyMemberDao.findCompanyMemberByMid(id);
        }
        return this.companyMember;
    }
    
    /**
     * 获取个人会员信息
     */
    public Person getPerson() {
         if (this.person == null) {
             this.person = personDao.findPersonByMid(id);
         }
         return this.person ;
    }
}






我有些晕,为什么会存在Member这个对象?难道不应该只是存在CompanyMember和Person吗?然后查找的行为放在Manager中去,或者将各自的行为放在各自的QueryObject放在Entity上,再或者以静态的方法存在于各自的Entity上?
36 楼 jason_help 2008-12-31  
深深体会过被DAO限制的痛苦,是时候消除DAO层了。
35 楼 berek_quyj 2008-12-30  
读完后,感觉一点想法都没有了,一下子世界清静了许多。
34 楼 xredleaf 2008-12-24  
据个例子,我有个Member类,对应数据库的member表,我页面上有个grid,列里显示出member表的全部列,这个Member类分个人会员和企业会员,如果是企业会员的话我要去查企业会员表,个人会员的话个人会员表,我这个domain model调用了dao去查数据库,domain model里可不可以去调用dao查数据库(原因?),我这里该怎么改才比较好。

会员类
pubic class Member {
    private Long id;
    private Integer type; //会员类型 (1,个人会员 2,企业会员)
    ......
    private ICompanyMemberDao companyMemberDao = BeanUtil.getBean("companyMemberDao");

    ......

    /**
     * 取得公司的名称
     */
    public String getCompanyName() {
        if (type == 1) {
            return getPerson().getPersonName();
        } else {
            return getCompanyMember().getCompanyName();
        }
    }
    
    /**
     * 获取企业会员
     */
    public CompanyMember getCompanyMember() {
        if (this.companyMember == null) {
            this.companyMember = companyMemberDao.findCompanyMemberByMid(id);
        }
        return this.companyMember;
    }
    
    /**
     * 获取个人会员信息
     */
    public Person getPerson() {
         if (this.person == null) {
             this.person = personDao.findPersonByMid(id);
         }
         return this.person ;
    }
}




33 楼 xredleaf 2008-12-24  
robbin 写道

所以最终我对这个问题的总结就是:

一、只要技术框架能够实现,尽量使用领域模型

二、无论Java还是Ruby,必须消灭DAO和TransactionScript

三、领域模型不必All-in-One,Java可以分割为 1个entity bean和几个business bean,而Ruby可以分割为1个model和几个mixin的module。




对于第3点能举例说下么,我不是很明白在代码中应该怎么写?
32 楼 lprince 2008-12-24  
业务与技术要分开,却一定需要懂业务也懂技术的人。
31 楼 chyichin 2008-12-23  
赞同上面几位说过的一些话
简单 实用 应该是最好的 OO的思想就是现实的体现
30 楼 xpf7622 2008-12-23  
能否把此篇制成PDF 以飨食大家
29 楼 llade 2008-12-10  
bloodrate 写道
有人说DAO可以屏蔽数据库的底层实现细节,让程序代码能够在不同数据库之间切换,这是很难做到的,一般情况下DAO只是出于不重复造轮子来封装一些数据库级别的操作,并且业务开发人员不必去操作RS,SQLException这样的数据库级别对象而已,至于屏蔽数据库差异,但凡是某个接口可以输入整句sql,开发人员就有可能传入只有某个数据库才能运行的语句,所以除非能让开发人员彻底不需要写sql(Hibernate这样),屏蔽数据库差异是不可能的.

治愈SQL和关系数据库照成的隔阂几乎不可能,SQL本质就是输出一个2维表,想在一个2维表里面表示一个树形的东西,冗余是不可避免的,想不冗余就要多次往返了,多次往返的代价要在可以接受的范围,要么就寄望强大的缓存,强大的索引系统(lucene)。
28 楼 bloodrate 2008-12-08  
有人说DAO可以屏蔽数据库的底层实现细节,让程序代码能够在不同数据库之间切换,这是很难做到的,一般情况下DAO只是出于不重复造轮子来封装一些数据库级别的操作,并且业务开发人员不必去操作RS,SQLException这样的数据库级别对象而已,至于屏蔽数据库差异,但凡是某个接口可以输入整句sql,开发人员就有可能传入只有某个数据库才能运行的语句,所以除非能让开发人员彻底不需要写sql(Hibernate这样),屏蔽数据库差异是不可能的.
27 楼 classcast 2008-12-05  
我越来越觉得灵活是如此的邪恶,以至于人们接二连三的发明各种各样的实现方式。效率似乎在决定采用哪种策略中不知不觉被丢失了。

也许python之道能给人一点启示,解决一个问题,只需要一种方式。Java的领域模型从一定角度来看似乎是对开发人员有这样的假设,假设开发人员丝毫不懂领域知识,假设开发人员总是想恶搞,而通过代码机制来约束开发人员不该做的事情。

在尝到如此讲究的建模方式的种种好处和各式苦头之后,感觉还是实用是王道。解决得了实际问题是王道,Java被一些自作聪明的所谓架构设计师搞得庞杂无比后,越来越像浓妆艳抹的女人,让人忍不住怀念她曾经单纯时的美。

一点牢骚,还望各路神仙手下留情
26 楼 snomile 2008-12-03  
taowen 写道
但是一旦有了DistributionDao,就不一样了。
distributionDao.save(new Distribution(somePublication));

哈哈,啥控制都管不了我了吧。很多时候Dao就是这么无法无天的。如果说domain model是数据的防火墙,那么dao就是顶级黑客。由于Entity不能注入等实现和宗教信仰上的限制,往往导致了service对DAO的滥用,就会造成上面这样的by-pass domain model的情况。



这里我有个疑问,希望taowen兄解惑一下。如果distribute()方法中直接访问数据库,把新产生的Distribution保存到数据库中,这样就避免了在service中直接引用distributionDao,这样做有什么问题吗?

另外为什么service中会存在 distributionDao.save(new Distribution(somePublication));  这样的代码呢,这明显违反了系统的设计原则。虽然我们不能保证系统中所有的dao都是被领域模型包装起来的,但是起码应该能保证已经被领域模型包装起来的dao,就决不允许有其他service对其引用。这样的设计原则我们已经坚持了很久,也没有发现什么问题。反之,肆意破坏这个原则,会导致本来应该封装到领域模型中的逻辑泄露到service中,进而导致下面这个问题:

taowen 写道
数据和行为距离很远。造成了不容易理解数据含义,因为你光看entity压根不知道它会被怎么用。漏掉一些servie中细微的用法,就可能会造成很大的bug。
同时这也更加让可重用的逻辑更难被发现,从一定程度上鼓励了大家各自发明,一份逻辑写n遍,还n遍都不一样。。。




相关推荐

Global site tag (gtag.js) - Google Analytics