How to model temporal attributes in CQRS?

166 views
Skip to first unread message

Frank

unread,
Sep 19, 2016, 1:05:14 PM9/19/16
to DDD/CQRS
Hi there,

today I came to a problem with CQRS and temporal attributes, that got me really into thinking and I couldn't make my mind. So here is the simplified problem:

Think of a time scheduler that can switch a light "On" and "Off" on basis of a daily time program. So a time program consists of time ranges where the light is either "On" or "Off". Now consider a command that can add a new time range to an already defined time program. The AddTimeRangeCommand with the attributes of the affected time program (id), the start and end time and the status of the light will be used for it.

public class AddTimeRangeCommand {
   
public final UUID timeProgramId;
   
public final LocalTime startTime;
   
public final LocalTime endTime;
   
public final String value;

   
public AddTimeRangeCommand(UUID timeProgramId, LocalTime startTime, LocalTime endTime, String value) {
       
this.timeProgramId = timeProgramId;
       
this.startTime = startTime;
       
this.endTime = endTime;
       
this.value = value;
   
}
}

Given is the following time program:

00:00                            Off                                 23:59
|----------------------------------------------------------------------|

Then a new time range for 08:00 to 12:00 with "On" is added resulting in the new time program:

00:00    Off       08:00   On   12:00             Off                23:59
 
|------------------|------------|------------------------------------|

So the internal model was having one range [00:00-23:59]. After adding the new range the resulting model has three ranges [0:00- 08:00], [08:00-12:00] and [12:00-23:59].

Now comes the question that bothers me. What would be the domain events resulting from this operation? My guess is that it would be one TimeRangeChangedEvent (to shorten the first interval) and two TimeRangeAddedEvent. One for the added one [08:00-12:00] and one for the remainder of the first one [12:00-23:59].

So basically this is a question on how to model temporal attributes with CQRS. Any thoughts or hints on this?

Thanks in advance

Danil Suits

unread,
Sep 19, 2016, 1:34:43 PM9/19/16
to DDD/CQRS

Herman Peeren

unread,
Sep 20, 2016, 5:22:37 AM9/20/16
to DDD/CQRS
In what you describe, you use TimeRanges in two different ways:
  • the TimeRange that is added to the previous stream of TimeRanges.
  • the TimeRanges in your "internal model" that describe the current state of your time program.

To simplify this I'd only use the first, temporal way; this is Event Sourcing. The current state of the time program is then caused by the original issued AddTimeRangeCommands, resulting one-on-one in a stream of TimeRangeAddedEvents. The current state in your example is then a result of applying two TimeRangeAddedEvents: [0:00-23:59]-off and [8:00-12:00]-on. Applying this eventstream reconstitutes your state. You can make a projection of this stream to easily query the current state. No other events are necessary to get the current state.


In short: state is the result of events. The stream of events completely constitutes the (current) state. The current state is not something extra besides the stream of events.

Frank

unread,
Sep 21, 2016, 5:12:45 AM9/21/16
to DDD/CQRS
Thanks Danil,

this is definitely another and maybe the better way of thinking. I will try to understand what this does mean for my time program problem.

In the meantime I've found another, very good presentation of Mathias Verraes about temporal business processes. This seems to go even one step further or at least adds another view to the problem.

Frank

unread,
Sep 21, 2016, 5:23:02 AM9/21/16
to DDD/CQRS
In what you describe, you use TimeRanges in two different ways:
  • the TimeRange that is added to the previous stream of TimeRanges.
  • the TimeRanges in your "internal model" that describe the current state of your time program.
Ok, got it.
 

To simplify this I'd only use the first, temporal way; this is Event Sourcing. The current state of the time program is then caused by the original issued AddTimeRangeCommands, resulting one-on-one in a stream of TimeRangeAddedEvents. The current state in your example is then a result of applying two TimeRangeAddedEvents: [0:00-23:59]-off and [8:00-12:00]-on. Applying this eventstream reconstitutes your state. You can make a projection of this stream to easily query the current state. No other events are necessary to get the current state.


Also makes sense.
 

In short: state is the result of events. The stream of events completely constitutes the (current) state. The current state is not something extra besides the stream of events.


Ok, here is what I understood from your hints. An Aggregate can have a model that manifests through the stored (and replayed) events. To build the model, functions can be used that sometimes can be complex. Like this could be in in my representation of the time ranges. This state is kind of a projection from the events. As already some persons within this forum pointed out you can call this the internal (read) model. This is solely used to validate incoming commands and ensure the business rules (invariants).

Herman Peeren

unread,
Sep 21, 2016, 9:23:23 AM9/21/16
to DDD/CQRS
On Wednesday, 21 September 2016 11:23:02 UTC+2, Frank wrote:
Ok, here is what I understood from your hints. An Aggregate can have a model that manifests through the stored (and replayed) events. To build the model, functions can be used that sometimes can be complex. Like this could be in in my representation of the time ranges. This state is kind of a projection from the events. As already some persons within this forum pointed out you can call this the internal (read) model. This is solely used to validate incoming commands and ensure the business rules (invariants).

Aggregate state might be used to validate incoming commands and ensure the business rules (invariants). But with your commands for adding the TimeRanges, what are the invariants? And why would you need any state of an aggregate to maintain those invariants? I'd say, given the format of the commands is valid (for instance startTime <=  endTime) there will hardly be any invariant. The write-side will just append those events to the event stream.

On the read-side (your time program) you don't have aggregates, invariants etc. Just a collection of TimeRanges (+ on/off), that are the result of the event stream. The only responsibility that he read-side has, is to query for the actual TimeRange, probably resulting in an on or off. Nothing more or less.

A main advantage of splitting the read-side and write-side is to make both simpler. I guess you see some complexity by unnecessary mixing them.
Reply all
Reply to author
Forward
0 new messages