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

Domain Object贫血vs富血(DDD)和spring roo到ruby的扯淡

阅读更多
引子:
前几天,小胖和我说他们公司CTO批他了,说他写的代码不够OO,不够DDD。细问才知道他们CTO在推DDD(领域模型驱动设计).我当时给他的观点是,JavaEE应用是天生贫血的,并不能像ruby之类的语言做到很好的富血,做到DDD。因为这些观点也是N年前讨论过的问题,我记得冒似robbin当年还下过定论:Java天生是贫血的。所以有了ruby之流做RAD快速开发。但当seam到spring roo的出现与完善,富血DDD在Java里也变得可行起来(此论言之尚早,拭目以待)。我以前也和别人争吵过哪个更好,现在我的思想又受到了一些冲击,你呢?世界在发展,我们的思想是不是也应该与时俱进呢?

贫血vs富血

我们来回顾一下。在企业架构模式中,业务层的实现一般有两种模式:一种是事务角本模式(Transaction script),另一种是领域模型模式(Domain Model)。这两种分别对应贫血和富血。好吧,我们不说这些扯淡的东西,我们简单点说。

所谓贫血,就是一个对象里只有属性,如java中的pojo,要实现业务,要依靠service层来实现相关方法,service层的实现是面向过程的,也就是所谓的transaction script.我们现在大量的分层应用action->service->dao->entity的方式就是这种贫血的模式实现。

//贫血代码示例:
Account.java
Public class Account{
	private String name;
	private Long num;
    get set…

}

AccountService.java
Public class AccountService{
	public List findAccountBySomething(String abc){}
	public void addAccount(Account a){}
	public void  otherBiz(){}

}


所谓的富血就是属性和方法都封装在Domain Object里,所有业务都直接操作Domain Object。Service层只是完成一些简单的事务之类的,甚至可以不用service层。也就是直接从action->entity.

//富血代码示例
Account.ava
Public class Account{
	private String name;
	private Long num;

	public List findAccountBySomething(String abc){}
	public void addAccount(Account a){}
	public void  otherBiz(){}
}


前人总结的一些贫血和富血的对比:
贫血模型的优点:
1.被许多程序员所掌握,许多教材采用的是这种模型,对于初学者,这种模型很自然,甚至被很多人认为是java中最正统的模型。
2.它非常简单,对于并不复杂的业务(转帐业务),它工作得很好,开发起来非常迅速。它似乎也不需要对领域的充分了解,只要给出要实现功能的每一个步骤,就能实现它。
3.事务边界相当清楚,一般来说service的每个方法都可以看成一个事务,因为通常Service的每个方法对应着一个用例。
其缺点为也是很明显的:
1.所有的业务都在service中处理,当业越来越复杂时,service会变得越来越庞大,最终难以理解和维护。
2.将所有的业务放在无状态的service中实际上是一个过程化的设计,它在组织复杂的业务存在天然的劣势,随着业务的复杂,业务会在service中多个方法间重复。
3.当添加一个新的UI时,很多业务逻辑得重新写。例如,当要提供Web Service的接口时,原先为Web界面提供的service就很难重用,导致重复的业务逻辑(在贫血模型的分层图中可以看得更清楚),如何保持业务逻辑一致是很大的挑战。

富血的优点是:
1.领域模型采用OO设计,通过将职责分配到相应的模型对象或Service,可以很好的组织业务逻辑,当业务变得复杂时,领域模型显出巨大的优势。
2.当需要多个UI接口时,领域模型可以重用,并且业务逻辑只在领域层中出现,这使得很容易对多个UI接口保持业务逻辑的一致(从领域模型的分层图可以看得更清楚)。
富血的缺点是:
1.对程序员的要求较高,初学者对这种将职责分配到多个协作对象中的方式感到极不适应。
2.领域驱动建模要求对领域模型完整而透彻的了解,只给出一个用例的实现步骤是无法得到领域模型的,这需要和领域专家的充分讨论。错误的领域模型对项目的危害非常之大,而实现一个好的领域模型非常困难。
3.对于简单的软件,使用领域模型,显得有些杀鸡用牛刀了。
4.对于事务等的处理,如果完全DDD,java支持得不够好。

关于Spring roo
引子中小胖公司就是采用Roo完成DDD富血开发。Spring Roo is a popular open-source rapid application development (RAD) tool for Java developers. ,使用命令行或工具操作来生成自动化项目,操作非常类似于rails。Roo可以很好的支持富血的Java DDD。关于roo我也不想说太多,因为我也没有亲自实战过,大家可以google一下。

如果采用roo,只需要
对于一个业务实现
@Entity  
@RooEntity  
@RooJavaBean  
@RooToString  
public class Employee {   
    @NotNull  
    @Size(max = 200)   
    private String name;   
  
    @Temporal(TemporalType.TIMESTAMP)   
    private Date birth;   
}  

//EmployeeController代码如下: 
@RooWebScaffold(automaticallyMaintainView = true, formBackingObject = Employee.class)   
@RequestMapping("/employee/**")   
@Controller  
public class EmployeeController {   
} 


在这里出现了一行@RooWebScaffold注解,做过rails的人会想,这不是rails里面的scaffold吗?没错,通过@RooWebScaffold注解,EmployeeController自动获得了curd的所有功能。

好了,就这么简单,一个domain,一个Controller,我们就可以发布到tomcat中运行了。

引用
Roo功能目前还不很强大,比如还不能根据配置的数据库链接直接从数据库表来生成Entity,以及前端表示层使用JSP和绑定Spring MVC(基于Spring 3.0支持REST)等,但是这些改进目标已经纳入其roadmap中。说一下另一个框架Seam对于富血也做得不错了,但还不是完全富血,同样也绑带了JSF。

这两天又看了一下spring roo,修正一下观点,新的spring roo版本已经可以用DBRE reverse from db.并且好像也很强大。

1.一个entity只需要写属性就行,get set免。
2.简单的增删改查功能,一键生成。
3.通过aspectJ实现编译时aop,完全不影响性能。
4.可以动态查询:如findPersonByNameAndMinMax(),相当于like name,between min to max这样的功能。
5.用Spring roo实现DDD,只有两层Controller层和Entity层。另外Service层(可选),Dao不推荐用了。
6.jms,email,json序列化支持
7.其它说明:开发需要JDK1.6以上。
8.不想用roo时,可以快速删除annotation和相关的jar包等文件。
...

Spring roo架构overview:




AspectJ实现编译时AOP




自动动态查询,自动生成





想了解更多roo内容,可以看看此文:使用Spring Roo ,感受ROR式的开发
http://www.iteye.com/topic/443059
还有看官方文档:
http://www.springsource.org/roo

关于ruby
Ruby我没有使用过,只是有所了解。所以不敢评价。但是当spring roo等框架成熟起来后,java语言就能很好的支持富血,更好的OO,更好的DDD,也能支持web快速开发了。那么我们能不能斗胆问一句:基于Java的开发者,想实现ror一样的web开发,还需要ruby吗?我们没必要向ruby转了吧?


小结:
我们对于新的东西,总是会有一种天生的阻抗性。就如我习惯了贫血的开发模式,有了更OO的spring roo出现时,我内心里还是不大看好它。正如前些天一些人说spring越来越庞大,不好一样,我想他们也是内心的阻抗性在起作用。但一个人要有一个更高的视野,时刻准备一些东西,当风暴来临时,可以从容面对

ps:说一下我的观点:
我是多年的贫血模型的实践者,基本上并没什么特别觉得不适的地方,虽然贫血模型不那么OO。其实我对于这个spring roo并不是特别看好。但是需要去了解它,和关注它。(可能又要修正了,看发展情况吧)

国外有一篇文章对spring roo的观点,我是比较赞同的:
When to use Spring roo?
http://java.dzone.com/articles/when-use-spring-roo?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+javalobby%2Ffrontpage+(Javalobby+%2F+Java+Zone)
我抽出几个重要的出来:
What is Spring Roo?
“Spring Roo is a lightweight developer tool that makes it fast and easy to deliver instant results. Best of all, you code 100% in Java and get to reuse all your existing Java knowledge, skills and experience.

Spring Roo is awesome for CRUD-Clients!

Spring Roo is good for learning Technologies!

Spring Roo is NOT good for complex Projects (yet)...

Conclusion: Spring Roo is a nice Tool => Become a Part of the Community!
My final conclusion: You should know Spring Roo, because it is nice, but you should also know when to use it and when to use something else. Use it to create CRUD applications or to learn technologies. Do not use it (yet) for complex, large projects.

基本上结论就是,你必需知道spring roo,因为它确实很好,但是你必需知道什么时候使用它,什么时候使用其它的开发方式。用它来生成简单的,业务不复杂的crud应用,或者只是用来学习新技术。不要使用spring roo来做复杂的,大型的项目。

别人的一些观点:
1.Martin Flowler批贫血的文章:AnemicDomainModel
http://martinfowler.com/bliki/AnemicDomainModel.html

也许在现有的技术和框架支持下,Java的DDD和富血应用已经开始成熟。ruby是否可在java界休止了呢?等待先行实践者分享经验。

欢迎拍砖,谢绝谩骂!

附件为:spring roo快速学习文档
  • 大小: 37.3 KB
  • 大小: 39.3 KB
  • 大小: 87.3 KB
  • 大小: 87.3 KB
分享到:
评论
58 楼 ppgunjack 2011-04-18  
数据库就像建楼的地基,你建大厦开始就需要考虑进去这部分的特性,而且必须考虑,这是所有业务最后能落地的部分,可用效能安全都会依赖这块的设计
企业应用即使你不以数据库为中心,至少也应该以数据和数据流向为中心,这和面向数据库设计并不矛盾
所有的设计都是平衡的产物,很不幸DB是其中有一票否决的成员,看看现在云的研究论文都集中在哪一端
57 楼 itvincent 2011-04-18  
sulong 写道
下面是一些示例代码,
//以下是UI层代码
@Component
public class SomeAction {
    private SomeApp someApp;
    
    private String id;
    
    public String execute() {
        try {
            this.someApp.someBusinessMethod(this.id);
            return "success";
        } catch (SomeBusinessException e) {
            ...
            return "fail";
        }
        
    }
    
    @Autowired
    public void setSomeApp(SomeApp someApp) {
        this.someApp = someApp;
    }
    
    ...
}

//以下是应用层代码
@Component
public class SomeApp {
    private SomeRepository someRepository;
    
    public void someBusinessMethod(String id) {
        SomeEntity someEntity = this.someRepository.findById(id)
        someEntity.executeSomeBusiness();
    }

    @Autowired
    public void setSomeRepository(SomeRepository someRepository) {
        this.someRepository = someRepository;
    }
}

//以下是领域层代码
public interface SomeEntity {
    public void executeSomeBusiness();
}

public class SomeEntityImpl implements SomeEntity {
    private SomeTable someTable;
    
    private SomeRepository someRepository;
    
    public SomeEntityImpl(SomeTable someTable) {
        this.someTable = someTable;
    }
    
    public void executeSomeBusiness() {
        this.someTable.setSomeAttribute("someValue");
        ...
        
        this.someRepository.update(this);
    }
    
    @Autowired
    public void setSomeRepository(SomeRepository someRepository) {
        this.someRepository = someRepository;
    }
    
    public SomeTable getSomeTable() {
        return this.someTable;
    }
    
}

@Component
public class SomeRepository implements ApplicationContextAware {
    private SomeDao someDao;
    
    private ApplicationContext applicationContext;
    
    public SomeEntity findById(String id) {
        SomeTable someTable = this.someDao.findById(id);
        SomeEntityImpl someEntityImpl = new SomeEntityImpl(someTable);
        this.applicationContext.getAutowireCapableBeanFactory().autowireBean(someEntityImpl);
        return someEntityImpl;
    }
    
    public void update(SomeEntity someEntity) {
        SomeEntityImpl someEntityImpl = (SomeEntityImpl) someEntity;
        this.someDao.update(someEntityImpl.getSomeTable()));
    }
    
    @Autowired
    public void setSomeDao(SomeDao someDao) {
        this.someDao = someDao;
    }
    
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

//以下是基础设施层代码
@Entity
@Table(name = "some_table")
public class SomeTable {
    @Id
    private String id;
    
    ...
}

@Component
public class SomeDao extends HibernateDaoSupport{
    ...
}



分层必然会使某些代码变得麻烦,但是,我觉得当领域足够复杂的时候,是有必要牺牲的。


这样的分层的路我也曾经走过,结果就是大量的类中剩下大量的垃圾代码,正在有用的代码没几行。如此增加开发人员的工作量得不偿失,特别是需要修改业务的时候。总结下来,必要的分层是界面/业务,其它的分层都是需要根据实际情况而定的,不能一概而论
56 楼 itvincent 2011-04-18  
fireflyc 写道
peterwei 写道
fireflyc 写道
哎,看了楼主的介绍我觉得,楼主对领域驱动设计认识似乎是有问题的,而且我感觉基本上很多人都是存在这样的一个误区。

认为service和entity和DAO合并在一起,就是领域驱动了,比如之前是
Student,StudentDAO(或者泛型DAO),StudentService现在只有一个Student来干所有的事情。

我觉得持有这种观点的人是对DDD的理解有问题的,DDD中最为突出的5辆马车,Entity,Value Object,Factory,Service,Repository,这里可是也有entity也有vo,也有service的。只此一例来证明楼主对DDD的理解有偏差。

我承认我对DDD了解不深。那么,DDD应该是吧数据和逻辑放在一起吧?service层应该不管逻辑的事吧,应该只是浅浅的一层。那么dao,service是可有可无吧,从spring roo可以看到一斑。如果你有DDD实践经验,那么你认为DDD在国内是否已经成熟,是否可以在实际的项目中推广了?推广的难度是什么?为什么大家还在用贫血的模式?



可以先这样理解DDD,它是一种设计方法,与传统的面向数据库设计对立的方法。当面对一个系统的时候不应该想到的是有哪些表,先折腾数据库结构;这种设计方法只能让所有的业务逻辑更加凌乱系统模块各个都紧密的耦合在一起。DDD的做法是先进行系统的分析,识别出一些对象,Entity VO Service Repository之类的,其实就是一个为对象分配职责的过程,注意这个中间不涉及到任何数据库相关的东西,没有表结构的概念。所以重要的问题不在于用的是什么roo还是ruby之类的,重要的是你的设计是否真正的做到了不是以数据库为中心的设计,这个才是DDD真正的意义所在。


这个说出了核心
55 楼 peterwei 2011-04-18  
fireflyc 写道
peterwei 写道
fireflyc 写道
哎,看了楼主的介绍我觉得,楼主对领域驱动设计认识似乎是有问题的,而且我感觉基本上很多人都是存在这样的一个误区。

认为service和entity和DAO合并在一起,就是领域驱动了,比如之前是
Student,StudentDAO(或者泛型DAO),StudentService现在只有一个Student来干所有的事情。

我觉得持有这种观点的人是对DDD的理解有问题的,DDD中最为突出的5辆马车,Entity,Value Object,Factory,Service,Repository,这里可是也有entity也有vo,也有service的。只此一例来证明楼主对DDD的理解有偏差。

我承认我对DDD了解不深。那么,DDD应该是吧数据和逻辑放在一起吧?service层应该不管逻辑的事吧,应该只是浅浅的一层。那么dao,service是可有可无吧,从spring roo可以看到一斑。如果你有DDD实践经验,那么你认为DDD在国内是否已经成熟,是否可以在实际的项目中推广了?推广的难度是什么?为什么大家还在用贫血的模式?



可以先这样理解DDD,它是一种设计方法,与传统的面向数据库设计对立的方法。当面对一个系统的时候不应该想到的是有哪些表,先折腾数据库结构;这种设计方法只能让所有的业务逻辑更加凌乱系统模块各个都紧密的耦合在一起。DDD的做法是先进行系统的分析,识别出一些对象,Entity VO Service Repository之类的,其实就是一个为对象分配职责的过程,注意这个中间不涉及到任何数据库相关的东西,没有表结构的概念。所以重要的问题不在于用的是什么roo还是ruby之类的,重要的是你的设计是否真正的做到了不是以数据库为中心的设计,这个才是DDD真正的意义所在。

你就扯吧。要是像你这样的就是DDD,那我想大多数人都在搞DDD了。现在一般OOAD,都是面向业务需求划分domain,进行系统分析,划分各个模块职责(service就相对于某问题域的代理),然后才转化为数据库。很少有人一开始就搞数据库。什么意义都没有用,落地应用,并能提高效率才是王道。要不然搞了一堆理念,搞起来更花时间,让开发人员更不爽,项目时间成本也上去。这样的理念如果不能改进,还是不要的好。
54 楼 peterwei 2011-04-18  
sulong 写道
下面是一些示例代码,
//以下是UI层代码
@Component
public class SomeAction {
    private SomeApp someApp;
    
    private String id;
    
    public String execute() {
        try {
            this.someApp.someBusinessMethod(this.id);
            return "success";
        } catch (SomeBusinessException e) {
            ...
            return "fail";
        }
        
    }
    
    @Autowired
    public void setSomeApp(SomeApp someApp) {
        this.someApp = someApp;
    }
    
    ...
}

//以下是应用层代码
@Component
public class SomeApp {
    private SomeRepository someRepository;
    
    public void someBusinessMethod(String id) {
        SomeEntity someEntity = this.someRepository.findById(id)
        someEntity.executeSomeBusiness();
    }

    @Autowired
    public void setSomeRepository(SomeRepository someRepository) {
        this.someRepository = someRepository;
    }
}

//以下是领域层代码
public interface SomeEntity {
    public void executeSomeBusiness();
}

public class SomeEntityImpl implements SomeEntity {
    private SomeTable someTable;
    
    private SomeRepository someRepository;
    
    public SomeEntityImpl(SomeTable someTable) {
        this.someTable = someTable;
    }
    
    public void executeSomeBusiness() {
        this.someTable.setSomeAttribute("someValue");
        ...
        
        this.someRepository.update(this);
    }
    
    @Autowired
    public void setSomeRepository(SomeRepository someRepository) {
        this.someRepository = someRepository;
    }
    
    public SomeTable getSomeTable() {
        return this.someTable;
    }
    
}

@Component
public class SomeRepository implements ApplicationContextAware {
    private SomeDao someDao;
    
    private ApplicationContext applicationContext;
    
    public SomeEntity findById(String id) {
        SomeTable someTable = this.someDao.findById(id);
        SomeEntityImpl someEntityImpl = new SomeEntityImpl(someTable);
        this.applicationContext.getAutowireCapableBeanFactory().autowireBean(someEntityImpl);
        return someEntityImpl;
    }
    
    public void update(SomeEntity someEntity) {
        SomeEntityImpl someEntityImpl = (SomeEntityImpl) someEntity;
        this.someDao.update(someEntityImpl.getSomeTable()));
    }
    
    @Autowired
    public void setSomeDao(SomeDao someDao) {
        this.someDao = someDao;
    }
    
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

//以下是基础设施层代码
@Entity
@Table(name = "some_table")
public class SomeTable {
    @Id
    private String id;
    
    ...
}

@Component
public class SomeDao extends HibernateDaoSupport{
    ...
}



分层必然会使某些代码变得麻烦,但是,我觉得当领域足够复杂的时候,是有必要牺牲的。

我不知道这是不是DDD正统的代码。但从上面的代码来看,分层(包括service,dao还是有的,只不过是换了个概念)。你上面的代码无非就是一个简单的crud,就让人感觉很麻烦!实在看不出来比贫血的应用好在哪里。
当有一些复杂的业务协作时,如各个模块的互相协作,并要保持事务,我想你的app层又该回到过程式的了,不那么oo了。
53 楼 peterwei 2011-04-18  
abiandbel 写道
fireflyc 写道
哎,看了楼主的介绍我觉得,楼主对领域驱动设计认识似乎是有问题的,而且我感觉基本上很多人都是存在这样的一个误区。

认为service和entity和DAO合并在一起,就是领域驱动了,比如之前是
Student,StudentDAO(或者泛型DAO),StudentService现在只有一个Student来干所有的事情。

我觉得持有这种观点的人是对DDD的理解有问题的,DDD中最为突出的5辆马车,Entity,Value Object,Factory,Service,Repository,这里可是也有entity也有vo,也有service的。只此一例来证明楼主对DDD的理解有偏差。


很同意你的观点,貌似现在很多人认为  service和entity和DAO合并在一起,就是领域驱动了


downpour 写道


任何的开发方法和模式,都不能和程序开发中最基本的最佳实践相违背。在Java中,让Domain Object和Service Layer过度膨胀都是错误做法。只有两者结合,才能写出可维护的代码来。程序员在这里需要权衡的,是哪些逻辑应该放在Domain Object里面,哪些逻辑应该放在Service Layer中。


老大说的很到位,非常同意。DDD更加关注职责划分。Domain Object有什么,Service Layer有什么,只有职责都划分清楚了,才能弄的清楚。当然DDD还有其他方面。

1.没有人完全认为service和entity和DAO合并在一起,就是领域驱动了。但是如果你富血也有service,也有entity,dao,逻辑也放在service。那和贫血就没什么太大的区别了。说句不好听的话,就是在玩概念!
2.马P不要乱拍。职责分清肯定是在分析的时候做的,不管是富血还是贫血。
52 楼 peterwei 2011-04-18  
引用
很喜欢这类文章 LZ威武

不过不大明白的是。LZ已经说了。 富血模型对复杂事务的支持不大好, 但是LZ又说贫血模型适合做简单的业务。 这是不是冲突了?

我觉得“复杂的业务”很多时候都是多个领域对像之间的交互,这之间肯定会涉及事务。所以应该是贫血模型适合做复杂原业务。

当把逻辑方法放到domain里时,也就是说不再加一层service层时,各domain方法间的事务一致性是不好控制的。我没有说贫血模型适合做简单的业务(可能你看到的是前人总结的观点),我的观点和你一样是贫血更适合做复杂的业务。
51 楼 peterwei 2011-04-18  
引用
我正在我们公司推进DDD。关于技术实现,我觉得问题不大。想想前人当年没有hibernate, spring等现代化工具时,就能做到,我们现在有更多的选择,不可能做不到的。

觉得问题不大,并不代表就能很顺利,也不代表是最优方案。

引用
一般应用DDD,要对分层,从上到下依次为 用户界面层 -> 应用层 -> 领域层 -> 基础设施层。

你说的service层估计指的是应用程,这一层把用户输入数据转化为领域认识的数据,把领域返回的数据变成用户界面用的数据,这一层知道哪些用户操作执行哪些领域操作。


那你这个应用层和贫血模式就的Service层几乎差不多。

引用
领域层,就是比较纯的面向对象方式组织的代码了,你说的多个领域对象之间协调是很自然的事情,应该不会有问题。为了减少技术框架对实现领域带来的限制,尽管我们使用hibernate, 但是我打算彻底分离实体和数据库表映射的职责。也就是说,一个实体,就是代表了一个领域对象,而不是一张数据库的表,不作为hibernate的表映射类,不打@javax.persistence.Entity。而映射表的类,放到基础结构层。spring的话,对领域的侵入性相对比较小,只是会因为要注入依赖,而给类添加了一些看起来像关联关系的域,不过只要分清楚,还可以接受。我已经在我们的应用的一小部分应用了这一方式,没有什么问题,后面打算大规模推广。

我的理解,你是在entity之上再弄一层,搞一个大的domain对象来面对业务。你并没有回答我的一些问题,如业务逻辑的方法,你们是放在哪里,在你所谓的应用层,还是你的domain里?如果是放在domain里,那么多个domain里面的各种方法互相调用,要保持事务一致性的时候,怎么处理?如果是放在应用层里,那和贫血有什么区别?还不是一样。

引用
我深刻的体会到,领域模型里的实体并不和数据库的表有着简单的一对一映射关系。一个实体,可能会映射到多张表,一张表,也有可能会映射到多个实体,而且表里的字段和实体的属性也不能一一对应。因为,领域实体力求表达领域问题,而数据库的表,更关注于数据的存取便利和性能等。把这两个职责分离后,领域和数据将各自能比较自由的发展。

当domain多时,相当于封装了一个更大的对象。这样就一定比hibernate里的关联关系的映射好吗?而且如果你不映射到相关数据库,你怎么做?手工封装一下domain结构?就算你借助工作吧,那就会到最原始的db table-->javabean(大domain)转换上了。

50 楼 http4j 2011-04-18  
1. SFSB表示鸭梨很大
2. DDD与OO没有绝对关系,分层模式做的好,也有很多可以实践OO的地方。
3. 没有完美的理念,只有残缺的人.
49 楼 Technoboy 2011-04-18  
IcyFenix 写道
peterwei 写道
IcyFenix 写道
downpour 写道
一个没写过超过1000w行代码的人,我认为连说OO的资格都没有。


你是不是多打了一两个零?
这个世界上有多少人能写过超过1000w行代码?

1000w,确实是一个很大的概念。我承认我没有达到。


这不是很大的概念,是一个基本不可能完成的任务。

即使你非常勤奋,每天写300行代码,一年365天不休息,从刚刚出生0岁就开始敲键盘,也要敲到100岁才能写完1000w行代码。

哈哈
48 楼 sulong 2011-04-18  
fireflyc 写道
peterwei 写道
fireflyc 写道
哎,看了楼主的介绍我觉得,楼主对领域驱动设计认识似乎是有问题的,而且我感觉基本上很多人都是存在这样的一个误区。

认为service和entity和DAO合并在一起,就是领域驱动了,比如之前是
Student,StudentDAO(或者泛型DAO),StudentService现在只有一个Student来干所有的事情。

我觉得持有这种观点的人是对DDD的理解有问题的,DDD中最为突出的5辆马车,Entity,Value Object,Factory,Service,Repository,这里可是也有entity也有vo,也有service的。只此一例来证明楼主对DDD的理解有偏差。

我承认我对DDD了解不深。那么,DDD应该是吧数据和逻辑放在一起吧?service层应该不管逻辑的事吧,应该只是浅浅的一层。那么dao,service是可有可无吧,从spring roo可以看到一斑。如果你有DDD实践经验,那么你认为DDD在国内是否已经成熟,是否可以在实际的项目中推广了?推广的难度是什么?为什么大家还在用贫血的模式?



可以先这样理解DDD,它是一种设计方法,与传统的面向数据库设计对立的方法。当面对一个系统的时候不应该想到的是有哪些表,先折腾数据库结构;这种设计方法只能让所有的业务逻辑更加凌乱系统模块各个都紧密的耦合在一起。DDD的做法是先进行系统的分析,识别出一些对象,Entity VO Service Repository之类的,其实就是一个为对象分配职责的过程,注意这个中间不涉及到任何数据库相关的东西,没有表结构的概念。所以重要的问题不在于用的是什么roo还是ruby之类的,重要的是你的设计是否真正的做到了不是以数据库为中心的设计,这个才是DDD真正的意义所在。

赞同,就是这个意思!
47 楼 wolfweis 2011-04-18  
我只说一句:面向真实业务的对象...
其他的神马都是浮云
46 楼 fireflyc 2011-04-18  
peterwei 写道
fireflyc 写道
哎,看了楼主的介绍我觉得,楼主对领域驱动设计认识似乎是有问题的,而且我感觉基本上很多人都是存在这样的一个误区。

认为service和entity和DAO合并在一起,就是领域驱动了,比如之前是
Student,StudentDAO(或者泛型DAO),StudentService现在只有一个Student来干所有的事情。

我觉得持有这种观点的人是对DDD的理解有问题的,DDD中最为突出的5辆马车,Entity,Value Object,Factory,Service,Repository,这里可是也有entity也有vo,也有service的。只此一例来证明楼主对DDD的理解有偏差。

我承认我对DDD了解不深。那么,DDD应该是吧数据和逻辑放在一起吧?service层应该不管逻辑的事吧,应该只是浅浅的一层。那么dao,service是可有可无吧,从spring roo可以看到一斑。如果你有DDD实践经验,那么你认为DDD在国内是否已经成熟,是否可以在实际的项目中推广了?推广的难度是什么?为什么大家还在用贫血的模式?



可以先这样理解DDD,它是一种设计方法,与传统的面向数据库设计对立的方法。当面对一个系统的时候不应该想到的是有哪些表,先折腾数据库结构;这种设计方法只能让所有的业务逻辑更加凌乱系统模块各个都紧密的耦合在一起。DDD的做法是先进行系统的分析,识别出一些对象,Entity VO Service Repository之类的,其实就是一个为对象分配职责的过程,注意这个中间不涉及到任何数据库相关的东西,没有表结构的概念。所以重要的问题不在于用的是什么roo还是ruby之类的,重要的是你的设计是否真正的做到了不是以数据库为中心的设计,这个才是DDD真正的意义所在。
45 楼 ahzzhen2 2011-04-18  
itstarting 写道
引用

3.对于简单的软件,使用领域模型,显得有些杀鸡用牛刀了。


我到觉得简单的软件用领域模型更方便点。

+1
44 楼 sulong 2011-04-18  
idle_sun 写道
很喜欢这类文章 LZ威武

不过不大明白的是。LZ已经说了。 富血模型对复杂事务的支持不大好, 但是LZ又说贫血模型适合做简单的业务。 这是不是冲突了?

我觉得“复杂的业务”很多时候都是多个领域对像之间的交互,这之间肯定会涉及事务。所以应该是贫血模型适合做复杂原业务。


DDD并不会导致对事务支持不好。事务边界的划分本来就不适合于放在领域层,而应该放在上面的应用层。我听着“富血模型”这四个字就觉得很别扭,就好像富血模型和贫血模型就是两种不同的设计风格,都是正确的一样。其实要做DDD就无所谓富血,贫血,因为贫血根本就违背了面向对象和DDD,DDD从来都不贫血。我也写过好多贫血的代码,但是我现在省悟了,我要DDD,寻找领域模型,用模型驱动代码,抛弃贫血模型!
43 楼 sulong 2011-04-18  
下面是一些示例代码,
//以下是UI层代码
@Component
public class SomeAction {
    private SomeApp someApp;
    
    private String id;
    
    public String execute() {
        try {
            this.someApp.someBusinessMethod(this.id);
            return "success";
        } catch (SomeBusinessException e) {
            ...
            return "fail";
        }
        
    }
    
    @Autowired
    public void setSomeApp(SomeApp someApp) {
        this.someApp = someApp;
    }
    
    ...
}

//以下是应用层代码
@Component
public class SomeApp {
    private SomeRepository someRepository;
    
    public void someBusinessMethod(String id) {
        SomeEntity someEntity = this.someRepository.findById(id)
        someEntity.executeSomeBusiness();
    }

    @Autowired
    public void setSomeRepository(SomeRepository someRepository) {
        this.someRepository = someRepository;
    }
}

//以下是领域层代码
public interface SomeEntity {
    public void executeSomeBusiness();
}

public class SomeEntityImpl implements SomeEntity {
    private SomeTable someTable;
    
    private SomeRepository someRepository;
    
    public SomeEntityImpl(SomeTable someTable) {
        this.someTable = someTable;
    }
    
    public void executeSomeBusiness() {
        this.someTable.setSomeAttribute("someValue");
        ...
        
        this.someRepository.update(this);
    }
    
    @Autowired
    public void setSomeRepository(SomeRepository someRepository) {
        this.someRepository = someRepository;
    }
    
    public SomeTable getSomeTable() {
        return this.someTable;
    }
    
}

@Component
public class SomeRepository implements ApplicationContextAware {
    private SomeDao someDao;
    
    private ApplicationContext applicationContext;
    
    public SomeEntity findById(String id) {
        SomeTable someTable = this.someDao.findById(id);
        SomeEntityImpl someEntityImpl = new SomeEntityImpl(someTable);
        this.applicationContext.getAutowireCapableBeanFactory().autowireBean(someEntityImpl);
        return someEntityImpl;
    }
    
    public void update(SomeEntity someEntity) {
        SomeEntityImpl someEntityImpl = (SomeEntityImpl) someEntity;
        this.someDao.update(someEntityImpl.getSomeTable()));
    }
    
    @Autowired
    public void setSomeDao(SomeDao someDao) {
        this.someDao = someDao;
    }
    
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

//以下是基础设施层代码
@Entity
@Table(name = "some_table")
public class SomeTable {
    @Id
    private String id;
    
    ...
}

@Component
public class SomeDao extends HibernateDaoSupport{
    ...
}



分层必然会使某些代码变得麻烦,但是,我觉得当领域足够复杂的时候,是有必要牺牲的。
42 楼 hatedance 2011-04-18  
IcyFenix 写道
downpour 写道
一个没写过超过1000w行代码的人,我认为连说OO的资格都没有。


你是不是多打了一两个零?
这个世界上有多少人能写过超过1000w行代码?

作为一个深度的OO粉丝,我一般用写过多少个类来计算代码量。
而那些用过程化程序员则依然用行数来计算。
一定会有很多过程化程序员来告诉我类的长度是多变的。正如行的长度也是多变的,你是否曾觉得计算字符个数更合理?
一个完美主义者写出来的类的大小一般是体型不大不小的,如果一开始不是,重构以后就是了。
41 楼 abiandbel 2011-04-18  
fireflyc 写道
哎,看了楼主的介绍我觉得,楼主对领域驱动设计认识似乎是有问题的,而且我感觉基本上很多人都是存在这样的一个误区。

认为service和entity和DAO合并在一起,就是领域驱动了,比如之前是
Student,StudentDAO(或者泛型DAO),StudentService现在只有一个Student来干所有的事情。

我觉得持有这种观点的人是对DDD的理解有问题的,DDD中最为突出的5辆马车,Entity,Value Object,Factory,Service,Repository,这里可是也有entity也有vo,也有service的。只此一例来证明楼主对DDD的理解有偏差。


很同意你的观点,貌似现在很多人认为  service和entity和DAO合并在一起,就是领域驱动了


downpour 写道


任何的开发方法和模式,都不能和程序开发中最基本的最佳实践相违背。在Java中,让Domain Object和Service Layer过度膨胀都是错误做法。只有两者结合,才能写出可维护的代码来。程序员在这里需要权衡的,是哪些逻辑应该放在Domain Object里面,哪些逻辑应该放在Service Layer中。


老大说的很到位,非常同意。DDD更加关注职责划分。Domain Object有什么,Service Layer有什么,只有职责都划分清楚了,才能弄的清楚。当然DDD还有其他方面。
40 楼 idle_sun 2011-04-18  
很喜欢这类文章 LZ威武

不过不大明白的是。LZ已经说了。 富血模型对复杂事务的支持不大好, 但是LZ又说贫血模型适合做简单的业务。 这是不是冲突了?

我觉得“复杂的业务”很多时候都是多个领域对像之间的交互,这之间肯定会涉及事务。所以应该是贫血模型适合做复杂原业务。
39 楼 sulong 2011-04-18  
peterwei 写道
sulong 写道
DDD不是一门纯粹的软件设计学问,还是一个软件过程理论。做DDD,前提是要与领域专家充分交互,共同给领域建模,再把领域模型与代码绑定。 DDD也不一定非要用面向对象的方式建模,还可以用建规则模型,数学模型等,只不过面向对象在大部分情况下是最容易建模的。脱离了领域,程序员看到的都是技术问题,是不可能实现DDD的。DDD并不违反面向对象,面向对象要求类要单一职责,所以不应该出现所谓的“充血模型”,一定类只做它该做的事,只会恰到好处,不会血多到要涨。如果一个类太复杂,太大,那说明模型上有些地方要更加的细化了,对应的代码也要更加细化,职责重新划分。DDD也是要迭代中进行的,模型要不断完善,代码也要跟着不断完善。

你说的没错,但是我们现在想知道在java中,DDD如何具体的落地。而不是想听到高谈阔论,大谈理论。如service层这些要不要,在应用中的架构是怎么样的?用到哪些技术,如spring roo.对于复杂的业务,如多个domain的互相调用协调是如何处理的?在应用DDD中,会出现什么样的问题?以后维护会不会方便。。。


我正在我们公司推进DDD。关于技术实现,我觉得问题不大。想想前人当年没有hibernate, spring等现代化工具时,就能做到,我们现在有更多的选择,不可能做不到的。

一般应用DDD,要对分层,从上到下依次为 用户界面层 -> 应用层 -> 领域层 -> 基础设施层。

你说的service层估计指的是应用程,这一层把用户输入数据转化为领域认识的数据,把领域返回的数据变成用户界面用的数据,这一层知道哪些用户操作执行哪些领域操作。

领域层,就是比较纯的面向对象方式组织的代码了,你说的多个领域对象之间协调是很自然的事情,应该不会有问题。为了减少技术框架对实现领域带来的限制,尽管我们使用hibernate, 但是我打算彻底分离实体和数据库表映射的职责。也就是说,一个实体,就是代表了一个领域对象,而不是一张数据库的表,不作为hibernate的表映射类,不打@javax.persistence.Entity。而映射表的类,放到基础结构层。spring的话,对领域的侵入性相对比较小,只是会因为要注入依赖,而给类添加了一些看起来像关联关系的域,不过只要分清楚,还可以接受。我已经在我们的应用的一小部分应用了这一方式,没有什么问题,后面打算大规模推广。

我深刻的体会到,领域模型里的实体并不和数据库的表有着简单的一对一映射关系。一个实体,可能会映射到多张表,一张表,也有可能会映射到多个实体,而且表里的字段和实体的属性也不能一一对应。因为,领域实体力求表达领域问题,而数据库的表,更关注于数据的存取便利和性能等。把这两个职责分离后,领域和数据将各自能比较自由的发展。

相关推荐

    Spring Roo In Action

    Spring Roo还提供了与Spring集成的技术,例如Spring MVC、Spring Security、Spring Batch等,这有助于开发者实现快速开发和部署应用的目的。 标题《Spring Roo In Action》意味着这本书是一本实用指南,旨在向读者...

    spring roo使用文档

    - **现有项目集成**:可以将 Spring Roo 集成到已有项目中,充分利用现有的代码库。 - **数据库集成**:支持多种数据库,如 MySQL、PostgreSQL 等。 #### 九、移除 Spring Roo - **避免绑定**:Spring Roo 生成的...

    Spring Roo 简介,第 4 部分: 用 Spring Roo 和 Cloud Foundry 在云中快速开发应用程序

    **Spring Roo 简介,第 4 部分: 用 Spring Roo 和 Cloud Foundry 在云中快速开发应用程序** 在本篇文章中,我们将深入探讨 Spring Roo 的使用,以及如何结合 Cloud Foundry 进行云端应用开发。Spring Roo 是一个...

    spring roo 1.1.3 学习资料

    例如,它使用贫血模型(Anemic Domain Model)来分隔业务逻辑和服务层,使用约定优于配置(Convention over Configuration)的原则,减少手动配置的工作量。 3. **数据库支持**:Spring Roo支持多种主流数据库,...

    spring roo in action

    Spring Roo是一个用于快速开发Java应用程序的框架,它结合了Spring生态系统的强大功能,尤其是对Spring MVC、Spring Security、Spring Tiles、Spring Web Flow以及Spring测试支持等方面。 Spring Roo利用了一种...

    Spring Roo命令文档

    Spring Roo是Spring框架的一部分,它提供了一种快速开发工具,帮助开发者在Java应用中创建和管理代码。Roo通过自动化过程,简化了常见的开发任务,如设置项目结构、创建实体类、生成数据库表映射以及创建CRUD操作。...

    Spring ROO

    **Spring ROO详解** Spring ROO是Spring框架下的一个快速开发工具,旨在简化Java应用程序的构建过程,尤其针对企业级应用。它通过自动化任务、代码生成以及最佳实践的应用,极大地提高了开发效率。Spring ROO的核心...

    SpringRoo 官方文档-版本 2.0.0.RC1

    **SpringRoo 版本 2.0.0.RC1** 是一个重要的里程碑,标志着 SpringRoo 向更加成熟和强大的方向发展。这一版本在多个方面进行了改进,包括增强的可扩展性、改进的用户体验以及更紧密地与Spring技术栈集成。 #### 二...

    spring-roo-1.1.5.RELEAS

    Spring Roo是Spring框架家族中的一个开发工具,它旨在加速Java应用程序的开发过程,特别是通过提供命令行接口和集成开发环境(IDE)插件来简化常见的编程任务。标题"spring-roo-1.1.5.RELEASE"指的是Spring Roo的一...

    spring-roo-1.3.2.zip

    7. **持续集成**:Spring Roo也考虑到了持续集成,它可以与Hudson、Jenkins等工具集成,自动创建构建脚本,使得自动化测试和部署更加便捷。 8. **社区支持和扩展**:Spring Roo有一个活跃的开发者社区,提供了大量...

    spring-roo-2.0.0.RC1.zip

    Spring Roo是Spring框架家族中的一个开源工具,旨在简化Java应用程序的开发过程,特别是Spring MVC和Spring Data应用。这个"spring-roo-2.0.0.RC1.zip"压缩包包含的是Spring Roo的2.0.0 Release Candidate 1版本,这...

    spring roo 生成数据库表

    在这个场景中,"spring roo 生成数据库表"指的是使用Spring Roo工具来自动化创建与数据库表对应的Java实体类和数据访问层。 首先,Spring Roo支持多种数据库,包括MySQL、Oracle、PostgreSQL等。在开始之前,你需要...

    springroo快速学习

    在本"SpringRoo快速学习"资料中,我们将深入理解SpringRoo的核心概念、安装与设置,以及如何利用它来创建和管理项目。 一、SpringRoo简介 SpringRoo是Spring框架的扩展,它使用命令行界面(CLI)或集成开发环境...

    spring-roo-docs

    ### SpringRoo-ReferenceDocumentation 1.2.5.RELEASE 关键知识点解析 ...以上是对SpringRoo-ReferenceDocumentation 1.2.5.RELEASE的关键知识点总结,希望能帮助读者更好地理解和使用SpringRoo。

    Spring Roo - Reference Documentation

    Spring Roo的教程涵盖了从基础概念到高级应用的各个方面,旨在帮助开发者全面了解框架的功能和最佳实践。无论是希望快速上手的初学者,还是寻求深入优化的专业人士,都能从中找到适合自己的学习路径。 #### 2.2 ...

    spring-roo-1.1.0.M1.zip_54587.m1_M1 ssh_Spring Roo download_spri

    Spring Roo是Spring Framework的一个附加工具,它为Java开发者提供了一个快速开发平台,旨在简化和加速应用程序的构建过程。"spring-roo-1.1.0.M1.zip_54587.m1_M1 ssh_Spring Roo download_spri"这个标题暗示了这是...

    vaadin-springRoo可运行的例子

    这个"vaadin-springRoo可运行的例子"是一个整合了这两个框架的实际项目,提供了完整的war包和源代码,使得开发者可以深入学习和理解如何在实际开发中结合Vaadin和Spring Roo。 Vaadin是一个开源的Java框架,它允许...

    os-springroo2-sample_code

    【os-springroo2-sample_code】项目是一个关于Spring Roo的示例代码库,它展示了如何使用Spring Roo框架来快速开发应用程序。Spring Roo是Spring框架的一部分,它提供了一种简化和加速Java应用开发的方式,通过自动...

    Spring Roo 简介

    为了实现这一目标,Roo 采用了一系列流行、可靠和成熟的库,包括但不限于 Spring 框架、Java 持久化 API、JavaServer Pages (JSP)、Spring Security、Spring Web Flow、Log4J 和 Maven。 #### 二、Spring Roo 的...

Global site tag (gtag.js) - Google Analytics