我曾经DCI架构是什么?在一文中提到Qi4j框架实现DCI架构比较好,dzone今天就有一篇文章专门谈Implementing DCI in Qi4j。

DCI是一种新的构建面向对象应用的方法途径,DCI: Data数据, Context场景, Interaction交互。该文谈了如何使用DCI构建一个REST API。

Roles

DCI核心思想是:对象不是由单个类如POJO组成的,而是使用Roles角色来组合组装功能,再具体一点,当我们在这里说“对象Object”,是指DDD中的实体,而不是值对象或其他。在带有保存数据的企业架构中,实体将被划分为很多Roles,每个Roles有各自的职责目标,这点可以使用Qi4j的Mixin完成(AOP),或者Scala的Traits,否则很难以实现。

Data

DCI的数据代表实体对象中的数据,数据是进行私有Mixin(Private mixin)混合的,不可以被实体对象以外对象访问,Private mixin是一个重要概念,可以在不使用private语法的情况下,让状态成为对象的私有属性。

Context

Context掌握了在一个交互场景中角色与一个特定对象实例的关系映射。COntext可以使用POJO或Qi4j的TransientComposites 实现,后者功能很强大,体现了面向组合的架构特点。

Interactions

交互是作为场景实现的方法,寻找场景中相关的角色,然后激活调用其领域方法,重要一点是,交互的方法应该对应于用户界面表现层的Action方法,也就是MVC中控制器的方法。如果你改变了你的界面,就改变相应的场景和交互。

该文结合了作者项目,演示了DCI的使用情况,这个项目只有三个简单领域模型: Project, User, 和 Task,Task是一种可分配的角色,而Project和 User 是一种分配者的角色,User处于一种被分配的角色,这样,我们可以有一个简单场景叫InboxContext,有一个方法(或者叫交互):assign(Assignable):分配(可分配)。

Project有一个带有Task的InBox,那是我们要分配给用户的Task。但是它既可以被一个User拥有,也可以被一个特定Project拥有,这样就需要角色职责分配,更多职责发现和切分见:对象的责任与职责

切分的目标是达成这样:"assignment"不和这样场景绑定:"查询一个用户的tasks列表, 他们其中一个能够分配给这个用户",而应该是: "查询可分配者的分配Assignments列表, 选择其中一个分配给分配者"。这两者区别就是,与具体用户解耦,从对象职责行为这个新角度来考虑场景,交互和场景与具体类解耦,只和角色有关系,我们就可以在角色这个层次来思考设计。

这种方式可以让程序员侧重角色这个抽象层面来考虑,然后着重了解职责“分配”是如何工作的,而不必着眼于用户,因为用户有用户名和密码 有个人简历等等很多细节资料;如果我们以后使用Calculation替代Task,使用ClusterNode替代User,这样我们就不必修改这个系统,可以复用它了,唯一修改的就是对象和角色的映射,如果熟悉RBAC基于角色权限设计原理的话,应该对这种从角色和职责角度考虑方式的认同。

该文还使用代码表达了如何实现交互,如下代码:

InteractionContext map = new InteractionContext();RootContext context = assembler.objectBuilderFactory().newObjectBuilder( RootContext.class ).use(stack ).newInstance();InboxContext inboxContext = context.user( userId ).inbox();

为当前场景创造了RootContext,这是所有场景的根场景,从根场景能够创建子场景InboxContext ,其中userId用来寻找特定的User,如果使用REST实现,这个userId就使用URL实现,比如:"/administrator/inbox" .

为了实现前面"查询可分配者的分配Assignments列表, 选择其中一个分配给分配者",代码如下:

class AssignmentsMixin implements Assignments { @This //自动注入Data AssignmentsData data; //选择其中一个分配给分配者 public void assignTo( Assignable assignable, Assignee assignee ) { assignable.assignTo( assignee ); data.assignments().add( assignable ); } //查询可分配者的分配Assignments列表 public Iterable assignments() { return data.assignments(); } }

我们如果实现将任务Task分配给某个用户,就使用如下代码:

inboxContext.assignTo( task );

更多代码可见原文有下载。

最后,该文总结了这样做的优点:

以上代码可能多了些,但是能够完成代码的可读性, 代码的可维护性, 易于改变拓展性,如果你使用普通POJO方法,把所有职责放在一个类中,好像很简单了,一旦这个软件项目发展到一定程度,就难以拓展维护。

此外还有优点是可以重用复用,能够避免贫血模型,在目前所谓主流架构Spring或EJB之中,你为了避免将所有行为方法放入一个大类中,将数据放在实体中,将行为分开放到服务Service中(见请问一下这样分层对不对),这实际破坏了封装,就是MF指责的失血模型,问题摆在那里,但是一直没有得到解决, DCI架构解决了这个问题。

查看原文 >>
相关文章