clonalman
2012-08-31 07:57
鸟儿有了翅膀能够飞翔,但不能为了飞翔让鸟儿全身长满翅膀

banq
2012-08-31 08:04
2012-08-30 19:47 "@clonalman"的内容
在领域层添加DCI里的Context、CQRS里的DomainEvent,如果这些元素是我们领域建模想要的、所期待的,就是正影响, ...

基本同意你的观点,可以重新审视一下业务和架构的关系。

这里有一个隐式前提,DDD业务和现有架构脱节,也就是说,现有架构不能很好支撑DDD,为什么这么说呢,因为在这样架构下,DDD实体一直是被操作,作为方法参数传来传去,见下面伪代码演示:

public void myMethod(Entity entity){

.....

}

然后我们会有出现另外一个方法类似上面,只是其中方法代码有稍微不同:

public void myMethod2(Entity entity){

.....

}

public void myMethod3(Entity entity){

.....

}

问题来了,由于这三个方法中有一部分是共同的,当我们修改一个方法时,另外两个都要修改,万一忘记修改,就出现BUG,这时我们很容易想把三个方法中共同部分抽象成一个方法,这里有两个方向:一个是首先用继承模板,这是坏的设计,为什么坏这里不多说。

第二个是:之前我们总是从功能行为角度考虑实现,换个不同角度,从实体角度考虑,这共同的部分是不是可以写入实体内部呢? 也许从业务上讲,属于实体的行为,属于实体的职责,实体应该自己干的事情,应该有责任去做的事情,当然这其中也区分为基本职责和业务职责,后者我们通过DCI来实现。

下面问题来了,实体代码变为:

public class Entity{

public void myMethodCommon(){

......

}

}

如果myMethodCommon方法需要调用存储数据库,或调用服务或调用其他实体交互怎么办?

那么一般就是把这些资源注入到实体中,实体代码为:

public class Entity{

//数据库资源

public MyRepository myRepository;

//其他服务....

public MyService myService;

public void myMethodCommon(){

......

}

}

很明显,架构技术因素侵入搞脏了实体,怎么解决这个问题?

联想到Command命令模式,既然用户通过界面可以向服务以命令模式发出各种调用(如Struts等框架),实体作为用户行中模型的代表,代表用户需求思想,为什么不可以以命令对技术架构发出各种调用呢?通过命令模式,实体将各种调用打包成请求,而我们这时需要做的只是在实体中提供一个传递命令管道:

public class Entity{

public SubPublisherRoleIF subPublisherRole;

public void myMethodCommon(){

......

subPublisherRole.send...

}

}

这样,基本杜绝了实体和技术架构如各种仓储或服务的依赖,当然,更好的方式是,连命令管道都没有,直接在运行是注入各种实体需要的资源,这种方式虽然很干净,好像一点副作用都没有,但是带来另外问题,如变魔术,代码本来是让大家读懂的脚本,这时反而变得不容易理解,因为看到的代码和实际运行是完全不同的。

总是,正如如Clonalman所说,架构对领域总是有各种副作用,关键是我们能够结束哪种,可能完美是不存在的。

clonalman
2012-08-31 09:55
一个很困惑难以理解的想象,人类的认识总是想寻求一个一劳永逸的解决办法,

比如是“ET”或“神仙姐姐”干的,逻辑思维很合理和完美,

但问题“ET”与“神仙姐姐”符合现实吗?

把仓储或服务注入实体,这是DDD明确反对的,领域上也是不可理解的。

这么干的原因往往是偷懒或着对领域理解不够深刻造成的。

持久化:

持久化自身:不应放在(myMethodCommon中)

EntityRepository repository = new EntityRepository();

repository.save(entity);

(如果在方法中实现自身持久化,你是使用ActiveRecord模式)

持久化其他(Entity2):意味着Entity与Entity2存在某种业务联系,Entity2的生命周期是否为Entity所决定

public class Entity{

public Entity2Collection Entity2s = new Entity2Collection();

public void myMethodCommon(){

//持久化

Entity2 entity2 = new Entity2();

....

Entity2s.add(Entity2); <-- Entity2Collection.add 如何保存持久化,这是发挥架构的威力的地方

}

}

服务调用:当我们想在一个方法中调用服务来,

意味领域上往往Service内对象(Entity3)并不想Entity知道它是谁、是怎么工作?(也许Entity也并不想知道)

如果觉得实体里需要调用Service的地方,其方法myMethodCommon本身就是领域服务EntityService的一个方法。

public class EntityPubService <--因为banq没给具体场景,这个“pub”是举例用的,实际应该领域里的一个动作

{

EntityBusService bus; <- 这个可能是一个设施服务层的服务,是架构发挥作用的地方。。。。

public void process(Entity entity) <-参数不一定直接使用Entity,可以是其他与Entity相关的

{

bus.send(entity);

}

}

public class EntitySubService <--因为banq没给具体场景,这个“sub”是举例用的,实际应该领域里的一个动作

{

public void process(Entity entity)

{

Entity3Repository repository = new Entity3Repository();

Entity3 entity3 = repository.find(1); ///(随便写的1)

entity3.status = 1;

entity.reference = entity3.id

.......

}

}

myMethod、myMethod2、myMethod3的公共部分如果用到了EntityService的方法,

说明myMethod、myMethod2、myMethod3很可能是一个领域服务的方法,

Entity4Service.myMethod

Entity5Service.myMethod2

Entity6Service.myMethod3

否则可以是一个实体的方法如

Entity4.myMethod

Entity5.myMethod2

Entity6.myMethod3

是否属于领域方法还是实体方法,可能是动态,是业务本身决定的,

如果哪天Entity4.myMethod需要的添加一个服务来配合,就需要重构

实体:

public class Entity4

{

public void myMethod(Entity entity){

entity.myMethodCommon();

}

}

public class Entity5

{

public void myMethod2(Entity entity){

entity.myMethodCommon();

}

}

public class Entity6

{

public void myMethod3(Entity entity){

entity.myMethodCommon();

}

}

public class Entity4Serivce

{

EntityPubService entityPubService; <- 发挥架构威力(Ioc注入)

public void myMethod(Entity entity){

entityPubService.process(entity);

entity.myMethodCommon();

}

}

public class Entity5Service

{

EntityPubService entityPubService; <- 发挥架构威力(Ioc注入)

public void myMethod2(Entity entity){

entityPubService.process(entity);

entity.myMethodCommon();

}

}

public class Entity6Service

{

EntityPubService entityPubService; <- 发挥架构威力(Ioc注入)

public void myMethod3(Entity entity){

entityPubService.process(entity);

entity.myMethodCommon();

}

}

如果Entity是一个Domain Event,EntityBusService可能就会选择CQRS来实现,

你选择SOA或EJB服务总线也可以,只是“火车”或“飞机”的区别而已。(哪天我可以介绍具体场景的可能有助进一步理解)

(服务的注入我都舍不得用标签,而是根据类名称,比如一Service结尾就认为是领域服务,即符合DDD规范,又干干净净)

(架构最好能做到“随风潜入夜,润物细无声”)

[该贴被clonalman于2012-08-31 10:22修改过]

clonalman
2012-08-31 10:02
当然,引入一个Role也没问题,但它为解决问题能是我们想象出来的“ET外星人”或“神仙姐姐”,并强加给领域。。。

把一部“记录片”拍成“科幻片”或“神话片”

以上我自己的理解,个人观点。。。

banq
2012-08-31 11:21
2012-08-31 10:02 "@clonalman"的内容
当然,引入一个Role也没问题,但它为解决问题能是我们想象出来的“ET外星人”或“神仙姐姐”,并强加给领域 ...

说得很有趣,我们现在要解决的问题是让实体模型直接指挥架构,看来只有Scala 的trait或ruby的Mixin符合你的润物无声的要求。我也比较认同,但是担心问题还是两面的,因为润物无声是隐式的,无法显式,那么是否类似魔术Magic,对于代码结构阅读理解是不是有些障碍?

猜你喜欢
9Go 上一页 1 2 3 4 5 ... 9 下一页