Conditional `create` commands within a CQRS architecture
I will simplify my problem:
My LightsState API can receive 2 type of inputs: lightOn {lightId: ##}
and lightOff {lightId: ##}
. (AMQP input but irelevant here)
These inputs translate well into 2 Commands: TurnLightOnCmd
and TurnLightOffCmd
.
These commands will create 2 Events: LightTurnedOnEvent
and LightTurnedOffEvent
.
These events will be applied to the Light Aggregate
and the persisted projection will be the state of the light
.
All good until here.
But because there is no input: create light
, i can not make a CreateLightCmd
from that. I can only invoke a CreateLightCmd
when I receive a lightOn
input with a NEW lightId
to create a Light Aggregate
and then also apply TurnLightOnCmd
on it.
I am not sure how to handle this and also follow good CQRS practices.
Is it ok to call the Query side from Command side to check if light exists by id
and then invoke CreateLightCmd
first if needed?
Or should I make a db query from within the Command side and keep the Command and Query sides decoupled?
Or are there any other solutions to this?
Thanks
java domain-driven-design cqrs eventsource axon
add a comment |
I will simplify my problem:
My LightsState API can receive 2 type of inputs: lightOn {lightId: ##}
and lightOff {lightId: ##}
. (AMQP input but irelevant here)
These inputs translate well into 2 Commands: TurnLightOnCmd
and TurnLightOffCmd
.
These commands will create 2 Events: LightTurnedOnEvent
and LightTurnedOffEvent
.
These events will be applied to the Light Aggregate
and the persisted projection will be the state of the light
.
All good until here.
But because there is no input: create light
, i can not make a CreateLightCmd
from that. I can only invoke a CreateLightCmd
when I receive a lightOn
input with a NEW lightId
to create a Light Aggregate
and then also apply TurnLightOnCmd
on it.
I am not sure how to handle this and also follow good CQRS practices.
Is it ok to call the Query side from Command side to check if light exists by id
and then invoke CreateLightCmd
first if needed?
Or should I make a db query from within the Command side and keep the Command and Query sides decoupled?
Or are there any other solutions to this?
Thanks
java domain-driven-design cqrs eventsource axon
I can only invoke a CreateLightCmd when I receive a lightOn input with a NEW lightId to create a Light Aggregate and then also apply TurnLightOnCmd on it - is that a constraint imposed by your context? It seems awfully complicated.
– guillaume31
Nov 29 '18 at 10:28
It is not a constraint but the creation of the aggregate should be done by an explicit command, that is why I suggestedCreateLightCmd
.
– Razor
Nov 29 '18 at 17:06
I am experimenting now with calling the event store directly to check uniqueness oflightId
. This way at least the Command and Query sides are separated.
– Razor
Nov 29 '18 at 17:07
I mean, does the client absolutely have to know the ID ahead of time when creating a Light, and why correlateTurnLightOn
withCreateLight
in the first place?
– guillaume31
Nov 30 '18 at 8:18
add a comment |
I will simplify my problem:
My LightsState API can receive 2 type of inputs: lightOn {lightId: ##}
and lightOff {lightId: ##}
. (AMQP input but irelevant here)
These inputs translate well into 2 Commands: TurnLightOnCmd
and TurnLightOffCmd
.
These commands will create 2 Events: LightTurnedOnEvent
and LightTurnedOffEvent
.
These events will be applied to the Light Aggregate
and the persisted projection will be the state of the light
.
All good until here.
But because there is no input: create light
, i can not make a CreateLightCmd
from that. I can only invoke a CreateLightCmd
when I receive a lightOn
input with a NEW lightId
to create a Light Aggregate
and then also apply TurnLightOnCmd
on it.
I am not sure how to handle this and also follow good CQRS practices.
Is it ok to call the Query side from Command side to check if light exists by id
and then invoke CreateLightCmd
first if needed?
Or should I make a db query from within the Command side and keep the Command and Query sides decoupled?
Or are there any other solutions to this?
Thanks
java domain-driven-design cqrs eventsource axon
I will simplify my problem:
My LightsState API can receive 2 type of inputs: lightOn {lightId: ##}
and lightOff {lightId: ##}
. (AMQP input but irelevant here)
These inputs translate well into 2 Commands: TurnLightOnCmd
and TurnLightOffCmd
.
These commands will create 2 Events: LightTurnedOnEvent
and LightTurnedOffEvent
.
These events will be applied to the Light Aggregate
and the persisted projection will be the state of the light
.
All good until here.
But because there is no input: create light
, i can not make a CreateLightCmd
from that. I can only invoke a CreateLightCmd
when I receive a lightOn
input with a NEW lightId
to create a Light Aggregate
and then also apply TurnLightOnCmd
on it.
I am not sure how to handle this and also follow good CQRS practices.
Is it ok to call the Query side from Command side to check if light exists by id
and then invoke CreateLightCmd
first if needed?
Or should I make a db query from within the Command side and keep the Command and Query sides decoupled?
Or are there any other solutions to this?
Thanks
java domain-driven-design cqrs eventsource axon
java domain-driven-design cqrs eventsource axon
asked Nov 28 '18 at 22:13
RazorRazor
477615
477615
I can only invoke a CreateLightCmd when I receive a lightOn input with a NEW lightId to create a Light Aggregate and then also apply TurnLightOnCmd on it - is that a constraint imposed by your context? It seems awfully complicated.
– guillaume31
Nov 29 '18 at 10:28
It is not a constraint but the creation of the aggregate should be done by an explicit command, that is why I suggestedCreateLightCmd
.
– Razor
Nov 29 '18 at 17:06
I am experimenting now with calling the event store directly to check uniqueness oflightId
. This way at least the Command and Query sides are separated.
– Razor
Nov 29 '18 at 17:07
I mean, does the client absolutely have to know the ID ahead of time when creating a Light, and why correlateTurnLightOn
withCreateLight
in the first place?
– guillaume31
Nov 30 '18 at 8:18
add a comment |
I can only invoke a CreateLightCmd when I receive a lightOn input with a NEW lightId to create a Light Aggregate and then also apply TurnLightOnCmd on it - is that a constraint imposed by your context? It seems awfully complicated.
– guillaume31
Nov 29 '18 at 10:28
It is not a constraint but the creation of the aggregate should be done by an explicit command, that is why I suggestedCreateLightCmd
.
– Razor
Nov 29 '18 at 17:06
I am experimenting now with calling the event store directly to check uniqueness oflightId
. This way at least the Command and Query sides are separated.
– Razor
Nov 29 '18 at 17:07
I mean, does the client absolutely have to know the ID ahead of time when creating a Light, and why correlateTurnLightOn
withCreateLight
in the first place?
– guillaume31
Nov 30 '18 at 8:18
I can only invoke a CreateLightCmd when I receive a lightOn input with a NEW lightId to create a Light Aggregate and then also apply TurnLightOnCmd on it - is that a constraint imposed by your context? It seems awfully complicated.
– guillaume31
Nov 29 '18 at 10:28
I can only invoke a CreateLightCmd when I receive a lightOn input with a NEW lightId to create a Light Aggregate and then also apply TurnLightOnCmd on it - is that a constraint imposed by your context? It seems awfully complicated.
– guillaume31
Nov 29 '18 at 10:28
It is not a constraint but the creation of the aggregate should be done by an explicit command, that is why I suggested
CreateLightCmd
.– Razor
Nov 29 '18 at 17:06
It is not a constraint but the creation of the aggregate should be done by an explicit command, that is why I suggested
CreateLightCmd
.– Razor
Nov 29 '18 at 17:06
I am experimenting now with calling the event store directly to check uniqueness of
lightId
. This way at least the Command and Query sides are separated.– Razor
Nov 29 '18 at 17:07
I am experimenting now with calling the event store directly to check uniqueness of
lightId
. This way at least the Command and Query sides are separated.– Razor
Nov 29 '18 at 17:07
I mean, does the client absolutely have to know the ID ahead of time when creating a Light, and why correlate
TurnLightOn
with CreateLight
in the first place?– guillaume31
Nov 30 '18 at 8:18
I mean, does the client absolutely have to know the ID ahead of time when creating a Light, and why correlate
TurnLightOn
with CreateLight
in the first place?– guillaume31
Nov 30 '18 at 8:18
add a comment |
2 Answers
2
active
oldest
votes
Is it ok to call the Query side from Command side to check if light exists by id and then invoke CreateLightCmd first if needed?
Not really - that introduces race conditions, the consequences of which may not make you happy.
Review: DDD+CQRS+ES is very similar architecturally to DDD alone. The basic notion was that we persist information into our storage appliance (aka "the database"). The model runs in a process that loads the current state from the database, uses the command to compute a new state, and then stores that new state in the database.
The same pattern holds when we are doing event sourcing - we read the history from the database we use for writes, compute new events, and append those events to the history.
But: creation patterns are weird.
When we try to query for the history of an identifier that we haven't seen before, then we are going to get a null, or a None
, or a history with no events in it, or something like that.
The surprise is: that's fine.
For your use case, you wouldn't necessarily want a CreateLightCmd
-- instead, you want to produce a new LightCreatedEvent
when you get one of the other commands that should implicitly create the light.
In pseudo code:
TurnLightOn (cmd) {
history = getHistory(cmd.lightId)
if (history.isEmpty) {
history.append(LightCreatedEvent.from(cmd))
}
history.append(LightTurnedOnEvent.from(cmd))
save(cmd.lightId, history)
}
I ended up implementing something similar you suggested. Where I check in the EventStore if any event with specifiedlightId
exists and then call CreateLightCmd or TurnLightOnCmd.
– Razor
Nov 30 '18 at 12:31
add a comment |
Take a look at Udi Dahan's seminal post, "Don't create aggregate roots" [0]. The key points are:
- Don’t Create Aggregate Roots
- Always Get An Entity
What that means is that when you issue a command, you should always have an aggregate with an actual aggregate root entity initialized to it's default state. In your case, the default state is "light off". You execute the command on this light and now it is in state "light on". Now you save it to the database and it is created. Unless your domain cares about LightCreatedEvent, you don't need to model it.
[0] http://udidahan.com/2009/06/29/dont-create-aggregate-roots/
add a comment |
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53528912%2fconditional-create-commands-within-a-cqrs-architecture%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
Is it ok to call the Query side from Command side to check if light exists by id and then invoke CreateLightCmd first if needed?
Not really - that introduces race conditions, the consequences of which may not make you happy.
Review: DDD+CQRS+ES is very similar architecturally to DDD alone. The basic notion was that we persist information into our storage appliance (aka "the database"). The model runs in a process that loads the current state from the database, uses the command to compute a new state, and then stores that new state in the database.
The same pattern holds when we are doing event sourcing - we read the history from the database we use for writes, compute new events, and append those events to the history.
But: creation patterns are weird.
When we try to query for the history of an identifier that we haven't seen before, then we are going to get a null, or a None
, or a history with no events in it, or something like that.
The surprise is: that's fine.
For your use case, you wouldn't necessarily want a CreateLightCmd
-- instead, you want to produce a new LightCreatedEvent
when you get one of the other commands that should implicitly create the light.
In pseudo code:
TurnLightOn (cmd) {
history = getHistory(cmd.lightId)
if (history.isEmpty) {
history.append(LightCreatedEvent.from(cmd))
}
history.append(LightTurnedOnEvent.from(cmd))
save(cmd.lightId, history)
}
I ended up implementing something similar you suggested. Where I check in the EventStore if any event with specifiedlightId
exists and then call CreateLightCmd or TurnLightOnCmd.
– Razor
Nov 30 '18 at 12:31
add a comment |
Is it ok to call the Query side from Command side to check if light exists by id and then invoke CreateLightCmd first if needed?
Not really - that introduces race conditions, the consequences of which may not make you happy.
Review: DDD+CQRS+ES is very similar architecturally to DDD alone. The basic notion was that we persist information into our storage appliance (aka "the database"). The model runs in a process that loads the current state from the database, uses the command to compute a new state, and then stores that new state in the database.
The same pattern holds when we are doing event sourcing - we read the history from the database we use for writes, compute new events, and append those events to the history.
But: creation patterns are weird.
When we try to query for the history of an identifier that we haven't seen before, then we are going to get a null, or a None
, or a history with no events in it, or something like that.
The surprise is: that's fine.
For your use case, you wouldn't necessarily want a CreateLightCmd
-- instead, you want to produce a new LightCreatedEvent
when you get one of the other commands that should implicitly create the light.
In pseudo code:
TurnLightOn (cmd) {
history = getHistory(cmd.lightId)
if (history.isEmpty) {
history.append(LightCreatedEvent.from(cmd))
}
history.append(LightTurnedOnEvent.from(cmd))
save(cmd.lightId, history)
}
I ended up implementing something similar you suggested. Where I check in the EventStore if any event with specifiedlightId
exists and then call CreateLightCmd or TurnLightOnCmd.
– Razor
Nov 30 '18 at 12:31
add a comment |
Is it ok to call the Query side from Command side to check if light exists by id and then invoke CreateLightCmd first if needed?
Not really - that introduces race conditions, the consequences of which may not make you happy.
Review: DDD+CQRS+ES is very similar architecturally to DDD alone. The basic notion was that we persist information into our storage appliance (aka "the database"). The model runs in a process that loads the current state from the database, uses the command to compute a new state, and then stores that new state in the database.
The same pattern holds when we are doing event sourcing - we read the history from the database we use for writes, compute new events, and append those events to the history.
But: creation patterns are weird.
When we try to query for the history of an identifier that we haven't seen before, then we are going to get a null, or a None
, or a history with no events in it, or something like that.
The surprise is: that's fine.
For your use case, you wouldn't necessarily want a CreateLightCmd
-- instead, you want to produce a new LightCreatedEvent
when you get one of the other commands that should implicitly create the light.
In pseudo code:
TurnLightOn (cmd) {
history = getHistory(cmd.lightId)
if (history.isEmpty) {
history.append(LightCreatedEvent.from(cmd))
}
history.append(LightTurnedOnEvent.from(cmd))
save(cmd.lightId, history)
}
Is it ok to call the Query side from Command side to check if light exists by id and then invoke CreateLightCmd first if needed?
Not really - that introduces race conditions, the consequences of which may not make you happy.
Review: DDD+CQRS+ES is very similar architecturally to DDD alone. The basic notion was that we persist information into our storage appliance (aka "the database"). The model runs in a process that loads the current state from the database, uses the command to compute a new state, and then stores that new state in the database.
The same pattern holds when we are doing event sourcing - we read the history from the database we use for writes, compute new events, and append those events to the history.
But: creation patterns are weird.
When we try to query for the history of an identifier that we haven't seen before, then we are going to get a null, or a None
, or a history with no events in it, or something like that.
The surprise is: that's fine.
For your use case, you wouldn't necessarily want a CreateLightCmd
-- instead, you want to produce a new LightCreatedEvent
when you get one of the other commands that should implicitly create the light.
In pseudo code:
TurnLightOn (cmd) {
history = getHistory(cmd.lightId)
if (history.isEmpty) {
history.append(LightCreatedEvent.from(cmd))
}
history.append(LightTurnedOnEvent.from(cmd))
save(cmd.lightId, history)
}
answered Nov 29 '18 at 1:41
VoiceOfUnreasonVoiceOfUnreason
21.6k22151
21.6k22151
I ended up implementing something similar you suggested. Where I check in the EventStore if any event with specifiedlightId
exists and then call CreateLightCmd or TurnLightOnCmd.
– Razor
Nov 30 '18 at 12:31
add a comment |
I ended up implementing something similar you suggested. Where I check in the EventStore if any event with specifiedlightId
exists and then call CreateLightCmd or TurnLightOnCmd.
– Razor
Nov 30 '18 at 12:31
I ended up implementing something similar you suggested. Where I check in the EventStore if any event with specified
lightId
exists and then call CreateLightCmd or TurnLightOnCmd.– Razor
Nov 30 '18 at 12:31
I ended up implementing something similar you suggested. Where I check in the EventStore if any event with specified
lightId
exists and then call CreateLightCmd or TurnLightOnCmd.– Razor
Nov 30 '18 at 12:31
add a comment |
Take a look at Udi Dahan's seminal post, "Don't create aggregate roots" [0]. The key points are:
- Don’t Create Aggregate Roots
- Always Get An Entity
What that means is that when you issue a command, you should always have an aggregate with an actual aggregate root entity initialized to it's default state. In your case, the default state is "light off". You execute the command on this light and now it is in state "light on". Now you save it to the database and it is created. Unless your domain cares about LightCreatedEvent, you don't need to model it.
[0] http://udidahan.com/2009/06/29/dont-create-aggregate-roots/
add a comment |
Take a look at Udi Dahan's seminal post, "Don't create aggregate roots" [0]. The key points are:
- Don’t Create Aggregate Roots
- Always Get An Entity
What that means is that when you issue a command, you should always have an aggregate with an actual aggregate root entity initialized to it's default state. In your case, the default state is "light off". You execute the command on this light and now it is in state "light on". Now you save it to the database and it is created. Unless your domain cares about LightCreatedEvent, you don't need to model it.
[0] http://udidahan.com/2009/06/29/dont-create-aggregate-roots/
add a comment |
Take a look at Udi Dahan's seminal post, "Don't create aggregate roots" [0]. The key points are:
- Don’t Create Aggregate Roots
- Always Get An Entity
What that means is that when you issue a command, you should always have an aggregate with an actual aggregate root entity initialized to it's default state. In your case, the default state is "light off". You execute the command on this light and now it is in state "light on". Now you save it to the database and it is created. Unless your domain cares about LightCreatedEvent, you don't need to model it.
[0] http://udidahan.com/2009/06/29/dont-create-aggregate-roots/
Take a look at Udi Dahan's seminal post, "Don't create aggregate roots" [0]. The key points are:
- Don’t Create Aggregate Roots
- Always Get An Entity
What that means is that when you issue a command, you should always have an aggregate with an actual aggregate root entity initialized to it's default state. In your case, the default state is "light off". You execute the command on this light and now it is in state "light on". Now you save it to the database and it is created. Unless your domain cares about LightCreatedEvent, you don't need to model it.
[0] http://udidahan.com/2009/06/29/dont-create-aggregate-roots/
answered Nov 28 '18 at 22:34
CPersonCPerson
3367
3367
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53528912%2fconditional-create-commands-within-a-cqrs-architecture%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
I can only invoke a CreateLightCmd when I receive a lightOn input with a NEW lightId to create a Light Aggregate and then also apply TurnLightOnCmd on it - is that a constraint imposed by your context? It seems awfully complicated.
– guillaume31
Nov 29 '18 at 10:28
It is not a constraint but the creation of the aggregate should be done by an explicit command, that is why I suggested
CreateLightCmd
.– Razor
Nov 29 '18 at 17:06
I am experimenting now with calling the event store directly to check uniqueness of
lightId
. This way at least the Command and Query sides are separated.– Razor
Nov 29 '18 at 17:07
I mean, does the client absolutely have to know the ID ahead of time when creating a Light, and why correlate
TurnLightOn
withCreateLight
in the first place?– guillaume31
Nov 30 '18 at 8:18