2010-11-4
在考虑项目立项的草稿问题时,发现不同的用户获取的ProjectInfo中包含的立项草稿应该是不同的,所以,这里可能不应该只用一个字段savedProjectInitByCurrentUser来表示保存的草稿,而应该是一种其他的方式。
比如,可以采用ProjectInfo中调用草稿Service的方式,用来读取指定用户创建的草稿。
public ProjectInit getSavedProjectInit(String username, BizObjectSandboxMgrService sandboxService) {
if (savedProjectInitByCurrentUser != null) {
return (ProjectInit) savedProjectInitByCurrentUser.retriveBizObject();
}
return null;
}
同时,对于ProjectInfo处于不同状态时,getSavedProjectInit()应该是不同的实现。对于新建的项目,那么创建的草稿就是内存中的数据,如果是已经存在的项目,那么立项草稿就根据用户的信息来获取。这也提示我们采用状态模式来处理。比如上面一段代码就是projectinfo处于新建时的获取草稿的代码,下面是projectinfo处于已经保存状态时的获取草稿的代码。
public ProjectInit getSavedProjectInit(String username, BizObjectSandboxMgrService sandboxService) {
if(savedProjectInitByCurrentUser == null && !hasLoadedSavedProjectInit) {
savedProjectInitByCurrentUser = sandboxService.getBizObjectByOwnerAndBizObjectInfo(username,
ProjectInfo.class.toString(), parent.getId());
hasLoadedSavedProjectInit = true;
}
if(savedProjectInitByCurrentUser == null) {
return null;
}
else {
return (ProjectInit) savedProjectInitByCurrentUser.retriveBizObject();
}
}
2010-11-5
关于项目立项的历史版本保存问题。
当调用ProjectInfo.acceptProjectInit()时,内部应该更新当前最新的项目立项信息,同时保存上一个项目立项为历史记录。由于我们是采用的通用的历史机制,也就是ProjectInfo中即使具有项目立项的历史版本集合,那么也是无法通过级联操作更新历史记录信息的!这是因为通用的历史机制是基于元数据驱动的,ProjectInfo表与历史信息表并没有外键关系,而是通过元数据来关联的。
最开始的一个思考是在ProjectInfo中没有立项信息的历史信息,当ProjectInfo.acceptProjectInit()时,内部直接调用历史服务进行持久化操作。这个方案的问题在于历史版本的持久化早于ProjectInfo及其相关对象的持久化,可能出现数据同步的问题(当然通过事务可以解决)。另外一个问题是如果将ProjectInfo移动到了客户端,那么在进行ProjectInfo.acceptProjectInit()的时候,就会与服务器端进行通信进行数据持久化,这个数据通信其实是可以与项目信息的持久化一起的。这样分析之后得到的一个结果是,领域实体(值)对象的方法不要直接进行持久化操作!持久化的操作应该由服务调用Repository来驱动持久化,目前这个原则还是比较正确的。
因此,目前采用的模型是,ProjectInfo包含一个立项历史版本的集合,这个集合是transient的。当需要获取立项历史版本的时候,通过历史服务来获取。同时,保存新的历史版本时,也是存入到这个集合,然后最后调用updateProjectInfo的时候来显式的进行持久化操作,而不是隐式的!持久化的代码可能如下:
updateProjectInfo(projectInfo);
updateProjectInitHistory(projectInfo.getProjectInitHistory());
当ProjectInfo.acceptProjectInit()的时候,执行代码仅仅是在立项历史版本集合中添加一条记录。
2010-11-12
关于页面数据绑定是采用领域实体还是DTO的问题
一直以来都是采用实体与DTO进行转换后,然后将DTO绑定到页面对象。但是,EL表达式提供了足够丰富的表达方式,所以直接采用实体来进行页面展示和数据获取的绑定是可行的。比如要绑定项目立项的类型,传到jsp的对象可以是ProjectInfo对象,其中包含savedProjectInitialByCurrentUser项目立项对象,那么可以通过ProjectInfo对象来导航所有的savedProjectInitialByCurrentUser的属性。
<SELECT NAME="projectType" SIZE="1">
<c:forEach var="type" items="${PROJECT_TYPE_LIST}">
<c:choose>
<c:when test="${PROJECT_INFO_OBJECT.savedProjectInitialByCurrentUser.projectType.id == type.id}">
<OPTION VALUE="${type.id}" selected><c:out value="${type.metaItemName}"></c:out></OPTION>
</c:when>
<c:otherwise>
<OPTION VALUE="${type.id}"><c:out value="${type.metaItemName}"></c:out></OPTION>
</c:otherwise>
</c:choose>
</c:forEach>
</SELECT>
以上这种方法对于实体只是数据载体的用法,理论上是可行的,而且也是简便的。如果ProjectInfo的数据量过大,可以通过类似于构建DTO的方法,将ProjectInfo中不需要的数据去掉。
但是这种方法对于数据的充血模型来说,也就是DDD的思想,是不行的。因为在DDD中的数据实体不仅仅是数据的载体,而是封装了业务规则和逻辑的对象,所以其接口的设计不能是每个属性都有标准set和get方法,如果是这样,就无法将领域对象传到jsp中实现属性的提取。所以,对于DDD来说,需要设置DTO来专门进行前段的数据展示与接收。