It's impossible for the domain object to guess the needs of whatever subscriber. The domain object's duty is only to generate the event saying what happened. by @MikeSW in this answer
Here is my strategies:
a) Publish events with basic fields with help of query components.
For example, we're working on a hotel comment application. Each comment chould only be viewed by other customers after being approved by admin.
public class CommentApprovedEvent {
private String commentId;
}
And the event handler updates the status of the comment query data.So far so good. Sometimes later, some further requirements follows, such as when the commment is approved, the latest approved comment's content should be viewed as a "recommended" comment of the hotel.
We do have hotelId and content in the comment. But this time, we choose not to add them to the event. Instead, we use the query to retrieve it in the event handler:
public class HotelEventHandler {
public void on(CommentApprovedEvent event) {
CommentDetailDto comment = commentDetailQueryService.
findBy(event.getCommentId());
comment.getHotelId();
comment.getContent();
//update hotel's query data
}
}
Sometimes, it's even impossible to add all relevant data to the event. For example, sometimes later, a new requirement comes:the commentor should be rewareded with some credits when the comment is approved. But we don't have commentor's full profile in the comment. So we choose query again.
b) Split big event into smaller ones. In the case, we could add new events instead of new attributes. Consider the delivery case in DDD sample, Delivery is an important value object in the cargo domain wich shows many aspects of a given cargo:
/**
* 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
}
The delivery indicates the current states of the cargo, it is recalculated once a new handling event of the cargo is registered or the route specification is changed:
//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);
}
It came to my mind that I need a CargoDeliveryUpdatedEvent at first, like:
//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?
}
But finally I found out that I could use smaller events which could reveal the intention better, like:
//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;
}
Hope these helps. Cheers.