2013-02-13 48 views
0

在我的工作中,我们使用领域驱动CQRS架构,我们的软件平台,具有事件存储系统,这样我们就可以在域重装事件,在域中的变化的情况下新版本。迄今为止没有特别的。事件商店,域和敏捷,如何处理

我们的一个工作协议是我们尽量避免不惜一切代价变化的事件,因为这打破了旧版本中对生产环境的事件存储的事件。如果我们真的想要改变一个事件,我们必须为旧版本编写转换器,因为这可能会变得乏味,混乱,有时甚至不可能,所以我们尽可能避免这种情况。

但是,我们也做敏捷软件开发,这对我来说意味着(除其他外)“不要在你的代码中提前太多计划,只编写你将在不久的将来使用的代码”。这在大多数情况下对我很有意义。

但是,这成为上述事件的问题。当我创建新的事件时,我必须为它添加几乎所有的相关数据,以便任何处理该事件的系统都拥有它所需的所有数据。但是,从敏捷的角度来看,我宁愿只在当时实际需要的事件中添加数据。

所以我的问题是;处理这种困境的最好方法是什么?我能想到的唯一“解决方案”是放弃无编辑事件规则,只是接受我们将不得不编写事件转换器,或试图为每个事件添加尽可能多的相关数据如果未来的数据仍然不足,将创建新的事件。

另一种可能性是,有简单的是我们的思维方式存在缺陷。你会怎么看待这种方法中最薄弱的环节?有没有更好的方法来处理这个问题?

我希望这个问题是有道理的,我很期待看:)

+0

你的活动有哪些字段?你经常改变哪些领域? – 2013-02-14 09:55:57

+0

我正在投票关闭这个问题作为题外话,因为[项目管理现在在堆栈溢出主题](//meta.stackoverflow.com/questions/343829/is-stack-overflow-an-closed-website -to-ASK-有关项目管理,问题/ 343841#343841)。请在[SoftwareEngineering.SE](// softwareengineering.stackexchange.com/)和[ProjectManagement.SE](// pm.stackexchange.com/)上提出这些问题。 (您也可以通过主持人干预来标记此问题是否已迁移。) – robinCTS 2017-10-28 02:51:41

回答

1

这是不可能的域对象猜测任何用户的需求。域对象的职责只是产生事件来说明发生了什么。通过@MikeSW在此answer

这里是我的策略:

一)发布与查询组件的帮助基础领域的活动。

例如,我们正在一间酒店评论的应用程序。每个评论只有在被管理员批准后才能被其他客户查看。

public class CommentApprovedEvent { 
    private String commentId; 
} 

而事件处理程序更新评论查询数据的状态。到目前为止还不错。有时候以后还会有一些进一步的要求,比如当委员会获得批准时,最新批准的评论内容应该被视为酒店的“推荐”评论。

我们在评论中含有酒店的具体信息,但是这一次,我们选择不将它们添加到事件中。相反,我们使用的查询在事件处理程序检索它:

公共类HotelEventHandler {

public void on(CommentApprovedEvent event) { 
     CommentDetailDto comment = commentDetailQueryService. 
      findBy(event.getCommentId()); 
     comment.getHotelId(); 
     comment.getContent(); 
     //update hotel's query data 
    } 
} 

有时,它甚至不可能将所有相关数据添加到事件。例如,有时候会有新的要求出现:当评论被批准时,评论者应该重新安装一些信用。但是我们在评论中没有评论者的完整个人资料。所以我们再次选择查询。

b)将大事件拆分成更小的事件。 在这种情况下,我们可以添加新事件而不是新属性。考虑DDD样品中交付的情况下,交付是在货物领域至极的重要价值目标显示了一个给定的货物的许多方面:

/** 
* The actual transportation of the cargo, as opposed to 
* the customer requirement (RouteSpecification) and the plan (Itinerary). 
* 
*/ 
public class Delivery {//value object 

    private TransportStatus transportStatus; 
    private Location lastKnownLocation; 
    private Voyage currentVoyage; 
    private boolean misdirected; 
    private Date eta; 
    private HandlingActivity nextExpectedActivity; 
    private boolean isUnloadedAtDestination; 
    private RoutingStatus routingStatus; 
    private Date calculatedAt; 
    private HandlingEvent lastEvent; 
    .....rich behavior omitted 
} 

交付指示货物的当前状态,则重新计算一次新货物处理事件注册或者路由规格变更:

//non-cqrs style of cargo 
public void specifyNewRoute(final RouteSpecification routeSpecification) { 
    this.routeSpecification = routeSpecification; 
    // Handling consistency within the Cargo aggregate synchronously 
    this.delivery = delivery.updateOnRouting(this.routeSpecification, this.itinerary); 
} 

它来到我的脑海,我需要一个CargoDeliveryUpdatedEvent在第一,如:

//cqrs style of cargo 
public void deriveDeliveryProgress(final HandlingHistory handlingHistory) { 
    apply(new CargoDeliveryUpdatedEvent(
     this.trackingId, delivery.derivedFrom(routeSpecification(), 
     itinerary(), handlingHistory); 
} 

class CargoDeliveryUpdatedEvent { 
    private String trackingId; 
    private ..... //same fields in Delivery? 
    private ..... //add more when requirements evolves? 
} 

但最后我发现我可以用更小的事件,它可能会暴露意图更好,如:

//cqrs style of cargo 
public void deriveDeliveryProgress(final HandlingHistory handlingHistory) { 
    final Delivery delivery = Delivery.derivedFrom(
     routeSpecification(), itinerary(), handlingHistory); 
    apply(new CargoRoutingStatusRecalculatedEvent(this.trackingId, 
     delivery.routingStatus()); 
    apply(new CargoTransportStatusRecalculatedEvent(this.trackingId, 
     delivery.routingStatus()); 
    ....sends events telling other aspects of the cargo 
} 

class CargoRoutingStatusRecalculatedEvent{ 
    private String trackingId; 
    private String routingStatus; 
} 

class CargoTransportStatusRecalculatedEvent{ 
    private String trackingId; 
    private String transportStatus; 
} 

希望这些帮助。干杯。

0

多久你存储事件其他点?您的问题只是在系统升级期间的“混合模式”操作的短时间内,还是您有某种长期存储模式需要支持多个历史版本的事件?

+0

我们谨慎地保存每一个事件。这个想法是,如果领域逻辑发生变化,我们可以完全重建一个域。 – Dennisch 2013-02-13 16:20:50

+0

在这种情况下,您遇到了一个问题:您的域的语义会随着时间而改变,并且事件的每个版本都必须与事件生成时域中存在的特定语义耦合。这与API版本相关的经典问题非常相似。 – Addys 2013-02-13 16:44:11

+0

换句话说,根据您的域的N版本重放事件流可能不会导致相同的最终状态,因为处理这些事件的业务逻辑可能已经发生变化,因为它不会与针对版本N + 1重放相同的流。 – Addys 2013-02-13 16:53:58

0

“变更”必须被接受,而不是被阻止。您的系统应该有一个内置解决方案来接受更改,作为正常软件生命周期的一部分。与此相反,可能会“花费”更多的东西而不是制定一个解决方案来接受它作为“功能”。

我会去比阿迪同一个方向,如果我理解正确了。您需要为您的事件模型进行版本化。你应该建立一个类似的版本化事件/软件系统,以允许任何新的软件版本与旧的事件模型兼容。我不认为这是一个不可逾越的问题,但你可能不得不回到你的建筑图纸。