锁定老帖子 主题:再次小结领域模型的种种观点
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2005-12-16
其实说起来,我赞同robbin的意见,目前domain obj和manager or service的这个划分做法是合适的,不同的是我们使用的是失血模型,因为在所处的行业里,各种概念都还处于待沉淀,待精化的阶段,这也意味着数据的多变和业务的多变,而为了适应两者分别的变化,我们采用失血模型和Service两层来区分处理两者的变化。
|
|
返回顶楼 | |
发表时间:2005-12-16
wolfsquare 写道 其实说起来,我赞同robbin的意见,目前domain obj和manager or service的这个划分做法是合适的,不同的是我们使用的是失血模型,因为在所处的行业里,各种概念都还处于待沉淀,待精化的阶段,这也意味着数据的多变和业务的多变,而为了适应两者分别的变化,我们采用失血模型和Service两层来区分处理两者的变化。
毫无疑问,目前如果项目拿来就做,这样的做法是最合适,最成熟。 但是我们现在的讨论是在理论的高度,本身开这个帖子很大一个原因是我们看到一些新的似乎更理想的oo的编程,我们的框架、技术等都在朝这样的方向前进,我们的想法为什么还要停留在原地? robbin对充血模型的缺点总结也总结得很好,但问题摆在那边是需要解决的,乐观得看,我觉得这些问题都是会被解决的。 |
|
返回顶楼 | |
发表时间:2005-12-16
nkoffee 写道 robbin 写道 引用 这样的情况
Accounts.getAverageBalance() 比 AcountManager.getAverageBalance() 更好些, AccountManager更像是关于Account这个modul的业务脚本, 而Accounts是建立在这些业务脚本上更高一层次的OO抽象, 两个干的事差不多,但是概念不同, 当工具足够强大后,我们的domain modul就无须为事务的特点所妥协(会自动合并,设置事务边界), 事务应该散布在整个domain modul里。 经过wolfsquare的启发,我明白account的这些无状态的被动操作其实都是bank领域对象的主动业务逻辑方法。如果外沿无限扩大的话,我们总可以消除所有的无状态被动操作,让系统只剩下单一的领域对象和DAO接口。 叫Accounts确实比AccountManager来得舒服那么一点点,但是概念没有什么不同,都是用来分离bank的职责的。 现在spring的声明式事务已经强大到自动合并,设置事务边界了,事务确实可以散落在domain model里面,但是就算这样,你还是少不了Accounts。 我们应该可以看到我们的工具正在朝这样的方面努力,所以我们也需要更开阔得看这些问题,我们的domain modul也可以应该也能够丰富到很丰富,我觉得我们今天讨论还是得到原先的结论,是有些保守了。 Accounts是必须的,因为他本身就是这个modul里的一部分,在整个modul里每个modul都各司其职,但是不应该以事务来再区分modul里的这部分和那部分(xxx and xxxManager)。这是我们原来的一种妥协,我们现在就是置疑这种妥协的必要性。 按照Accounts和Account两个Class的二元结构来表达Account领域模型,我认为这个充血模型是比较合理的。 我先前提出把包含持久化逻辑的操作都划分到AccountManager(或者叫做Accounts)里面,确实如你所说,是基于事务的划分。单纯从OO的角度来考虑,不够完备,是贫血的。 但是从目前阶段来看,对于domain object的post-initiation技术还不完备,Spring2.0和dynamic AspectJ还没有推出(nuts弄出来好多Factory,引入injector让我感觉也很别扭),充血模型从技术上来说,还无法优雅的实现。 如果Spring2.0和dynamic aspectJ普及了,我很愿意尝试这种充血模型,但是现在技术上实现不了,我不得不妥协。 |
|
返回顶楼 | |
发表时间:2005-12-16
使用AOP静态配置的cutpoint也能完成这样的充血模型实现,不太清楚为什么要引入动态的概念。
而且,即使使用了这种模型,业务方法domain object和service还是要重复声明,麻烦。 再者,怎么控制web层不能随便调用业务方法? |
|
返回顶楼 | |
发表时间:2005-12-16
我正在做一个Nuts和aspectj结合的post instantiation。等做好了贴出来。
|
|
返回顶楼 | |
发表时间:2005-12-16
robbin 写道 二、贫血模型 贫血模型请看 http://forum.iteye.com/viewtopic.php?t=11712 中列举的第二种模型,简单来说,就是domain ojbect包含了不依赖于持久化的领域逻辑,而那些依赖持久化的领域逻辑被分离到Service层. Service(业务逻辑,事务封装) --> DAO ---> domain object 这种模型的优点: 1、各层单向依赖,结构清楚,易于实现和维护 2、设计简单易行,底层模型非常稳定 这种模型的缺点: 1、domain object的部分比较紧密依赖的持久化domain logic被分离到Service层,显得不够OO 2、Service层过于厚重 我以为自己对贫血模型理解错了,马上又把老马的文章拿来看一遍http://martinfowler.com/bliki/AnemicDomainModel.html 我觉得自己理解应该没错,贫血模型应该Domain Object本身只是数据的容积,不包含或者包含很少的业务逻辑, 而采用只提供静态方法的Manager实现业务逻辑(TransactionScript).这种方式没办法将OOAD和实现相对应,无法使用继承多态不利于系统的扩展,逻辑难于重用。 当然,很多系统都算不上企业级,很多企业级的系统也都被切分成一个一个的小系统,Rich Domain Model在这些小系统中是很难发挥作用的,从小系统的实用的角度来说,我并不排斥贫血模型。但是技术人员总归是得有些理想,理想我们可以把这些小的系统组合起来,或者整个企业有完整统一的领域层,所以我们坚持在这些小系统里面尝试Rich Domain Model。在我们的小系统里面,有些业务边界对象使用了Manager,边界对象在另外一个系统中可能有一个复杂的模型,但是在我的系统里面,可能只用到少量的静态逻辑。 |
|
返回顶楼 | |
发表时间:2005-12-16
Readonly 写道 robbin 写道 好吧,那么我是web程序员,我从页面传一个id过来,要先load一下该Account,显示account的信息。那么请问:你提供给我什么API?是
account.getAccountById(id); 还是 accountService.getAccountById(id); 还是 accountDao.getAccountById(id); ? new Account(id); 我以前就是用的这种方法,但是你的代码还是要给别人看的,我不能确保每个看我代码的家伙都能来javaeye看到这么好的帖子^_^ 所以对于我来说,在很长的一段时间内,domain object 和Service都会在系统中存在 举个例子: robbin说得WEB上提供id可以使用new Account(id);,如果是用户登陆呢? 难道是 new User(name,password) throws XXXException,YYYException 么? User user=userService().getUserByName(name); user.validatePass(pass); user.logon(); 这种代码,就是我所理解的service和user完美组合。 |
|
返回顶楼 | |
发表时间:2005-12-16
User user=userService();.getUserByName(name);; user.validatePass(pass);; user.logon();; 完美嘛?你如何处理用户不存在的情况?还不是要写恶心的if? |
|
返回顶楼 | |
发表时间:2005-12-16
我是比较同意robbin的观点的,举个登录的例子:
EmployeeService的实现: /* (non-Javadoc); * @see com.bearingpoint.gdc.service.EmployeeService#doLogin(java.lang.String, java.lang.String); */ public Employee doLogin(String name, String password); throws ObjectNotFoundException, PasswordNotMatchException { Employee employee = null; List result = employeeDAO.getEmployeeByName(name);; if(result.size(); == 0); throw new ObjectNotFoundException();; else { employee = (Employee);result.get(0);; if(!employee.getPassword();.equals(password);); throw new PasswordNotMatchException();; } return employee; } DAO的实现: /* (non-Javadoc); * @see com.bearingpoint.gdc.dao.EmployeeDAO#getEmployeeByName(java.lang.String); */ public List getEmployeeByName(String name); { String querySentence = "FROM Employee employee WHERE employee.name = :name"; Query query = getSession();.createQuery(querySentence);; query.setParameter("name", name);; return query.list();; } Action更简单: /* (non-Javadoc); * @see com.opensymphony.xwork.Action#execute(); */ public String execute(); throws Exception { employee = employeeService.doLogin(employee.getName();, employee.getPassword(););; //put the employee into session ServletActionContext.getContext();.getSession();.put("currentEmployee", employee);; return SUCCESS; } 结构层次很清晰,DAO就负责数据库操作,Service层处理逻辑。万一你的业务逻辑发生扩展和变化,也只要修改和扩展你的Service实现,与其他层次无关。同时,Service层总是接受事务的控制。 按照firebody的观点,莫非我的doLogin的逻辑要写在我的Employee类里面?然后new Employee(String name, String password) throw xxxException?这太恶心了。 |
|
返回顶楼 | |
发表时间:2005-12-16
downpour 写道 User user=userService();.getUserByName(name);; user.validatePass(pass);; user.logon();; 完美嘛?你如何处理用户不存在的情况?还不是要写恶心的if? UserNotFoundException 目前系统里最多的就是Action和Exception,的确烦恼 完美只是我个人感觉,因为从最开始在jsp中用userUtil开始到现在这种,这个是最能被人接受的 |
|
返回顶楼 | |