关于Domain Model的讨论已经非常多了,炒炒冷饭,这里是自己的一些做法。 以Workitem(工作流里的工作项)作为例子 最开始的做法: 一个实体类叫做Workitem,指的是一个工作项或者称为任务项 一个DAO类叫做WorkitemDao 一个业务逻辑类叫做WorkitemManager(或者叫做WorkitemService) 主要看看WorkitemManager,因为主要逻辑集中在这里
public
class
WorkitemManager {
private
WorkItemDAO workItemDAO;
public
void
setWorkItemDAO(WorkItemDAO workItemDAO) {
this
.workItemDAO
=
workItemDAO; }
/**
* 提交工作项 *
@param
workitemId 工作项ID
*/
public
void
commitWorkitem(String workitemId){ WorkItem workitem
=
workItemDAO.getWorkItem(workitemId);
//
当前工作项结束
workitem.complete();
int
sID
=
workitem.getSequenceId();
//
找到所对应的节点
InstActivity instActivity
=
workitem.getInstActivity();
//
查找是否存在下一工作项
WorkItem sequenceWorkitem
=
workItemDAO.findSequenceWorkItem(instActivity.getId(), sID
+
1
);
//
如果不存在则触发节点流转
if
(sequenceWorkitem
==
null
) { instActivity.signal(); }
//
否则把下一工作项激活
else
{ sequenceWorkitem.setExecutive(); } } }
Workitem类里有一些状态转换的逻辑,这样避免直接调用get/set属性方法
public
class
Workitem{
private
int
state
=
WorkitemInfo.PREPARE;
/**
* 委派工作项
*/
public
void
commission() {
if
(state
!=
WorkitemInfo.EXECUTE
&&
state
!=
WorkitemInfo.SIGNINED
&&
state
!=
WorkitemInfo.TOREAD
&&
state
!=
WorkitemInfo.SUSPEND)
throw
new
WorkflowException(Messages.CANNOT_ALTER_WORKITEM_STATE); setState(WorkitemInfo.COMMISSIONED); setCommitted(
new
Timestamp(System.currentTimeMillis())); }
/**
* 完成工作项
*/
public
void
complete() {
if
(state
!=
WorkitemInfo.SIGNINED)
throw
new
WorkflowException(Messages.CANNOT_ALTER_WORKITEM_STATE); setState(WorkitemInfo.COMPLETE); setCompleted(
new
Timestamp(System.currentTimeMillis())); } }
接下来的做法: 三个类不变,将WorkitemManager打平,将逻辑移动到Workitem
public
class
WorkitemManager {
private
WorkItemDAO workItemDAO;
public
void
setWorkItemDAO(WorkItemDAO workItemDAO) {
this
.workItemDAO
=
workItemDAO; }
/**
* 提交工作项 *
@param
workitemId 工作项ID
*/
public
void
commitWorkitem(String workitemId){ WorkItem workitem
=
workItemDAO.getWorkItem(workitemId);
//
当前工作项提交
workitem.commit(); } }
实际上此时WorkitemManager的功能非常有限,仅仅是事务边界和获取workitem对象,甚至在一些情况下可以省略。 通过一个Container类将spring的applicationContext进行封装,然后通过getBean()的静态方法即可访问被spring所管理的bean。实际是将workItemDAO隐式注入了Workitem。
public
class
Workitem{
/**
* 提交工作项
*/
public
void
commit() {
if
(state
!=
WorkitemInfo.EXECUTE
&&
state
!=
WorkitemInfo.SIGNINED
&&
state
!=
WorkitemInfo.TOREAD
&&
state
!=
WorkitemInfo.SUSPEND)
throw
new
WorkflowException(Messages.CANNOT_ALTER_WORKITEM_STATE); setState(WorkitemInfo.COMMISSIONED); setCommitted(
new
Timestamp(System.currentTimeMillis()));
int
sID
=
workitem.getSequenceId(); WorkItemDAO workItemDAO
=
(WorkItemDAO)Container.getBean(
"
workItemDAO
"
);
//
查找是否存在下一工作项
WorkItem sequenceWorkitem
=
workItemDAO.findSequenceWorkItem(instActivity.getId(), sID
+
1
);
//
如果不存在则触发节点流转
if
(sequenceWorkitem
==
null
) { instActivity.signal(); }
//
否则把下一工作项激活
else
{ sequenceWorkitem.setExecutive(); } } }
这样带来的好处是业务逻辑全部被封装到Domain Model,Domain Model之间的交互变得非常的简单,没有频繁的set/get,直接调用有业务语义的Domain Model的方法即可。问题在于单元测试时脱离不了spring的容器,workItemDAO需要stub。我觉得这个问题不大,问题是Domain Model开始变得臃肿,在业务逻辑复杂时代码行急剧膨胀。 现在的做法 以上三个类保持不变,增加一个类WorkitemExecutor,将业务逻辑移步。
public
class
Workitem{
/**
* 提交工作项
*/
public
void
commit() {
if
(state
!=
WorkitemInfo.EXECUTE
&&
state
!=
WorkitemInfo.SIGNINED
&&
state
!=
WorkitemInfo.TOREAD
&&
state
!=
WorkitemInfo.SUSPEND)
throw
new
WorkflowException(Messages.CANNOT_ALTER_WORKITEM_STATE); setState(WorkitemInfo.COMMISSIONED); setCommitted(
new
Timestamp(System.currentTimeMillis())); WorkitemExecutor workitemExecutor
=
(WorkitemExecutor)Container.getBean(
"
workitemExecutor
"
); workitemExecutor.commitWorkitem(
this
); } }
public
class
WorkitemExecutor {
private
WorkItemDAO workItemDAO;
public
void
setWorkItemDAO(WorkItemDAO workItemDAO) {
this
.workItemDAO
=
workItemDAO; }
/**
* 提交工作项 *
@param
workitemId 工作项ID
*/
public
void
commitWorkitem(Workitem workitem){
int
sID
=
workitem.getSequenceId();
//
找到所对应的节点
InstActivity instActivity
=
workitem.getInstActivity();
//
查找是否存在下一工作项
WorkItem sequenceWorkitem
=
workItemDAO.findSequenceWorkItem(instActivity.getId(), sID
+
1
);
//
如果不存在则触发节点流转
if
(sequenceWorkitem
==
null
) { instActivity.signal(); }
//
否则把下一工作项激活
else
{ sequenceWorkitem.setExecutive(); } } }
将业务逻辑拆分成两部分,一部分在Workitem,另一部分委托给WorkitemExecutor。实际上是Domain Model将复杂逻辑的情况重新外包出去。调用的时候,面向的接口还是Domain Model的方法。注意到WorkitemExecutor和WorkitemManager的API是非常相似的。实际可以这样认为,传统的方式 Client->(Business Facade)->service(Business Logic 部分依赖Domain Model)->Data Access(DAO)。 现在的方式 Client->(Business Facade)->Domain Model->service->Data Access(DAO)。 另外,在返回client端的查询的时候还是倾向于直接调用DAO,而不是通过Domain Model。 http://www.blogjava.net/ronghao 荣浩原创,转载请注明出处:)