This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Detecting whether an aggregate exists #1589
Comments
Hi! Indeed, it looks like boilerplate, but that not quite right. Imagine an aggregate:
We need to define exists automatically under the hood. But here some inconsistencies:
So, my opinion, its more looks like a pattern, or somewhat, but not a boilerplate that can be moved to internals. |
First, I would approach this without changing reSolve - for instance, with some kind of high order function.
If this is something that developers would often forget to do, then we can think of kind of middleware that once registered, will be called before each command handler and projection function Also, I would not enforce such things by default, I can imagine an aggregate that can have no create* command at all - if system doesnt care if it created or just updated, then it can leave without this command. |
I'm not quite sure that my request has been understood correctly. So let me try to clarify two points:
Instead, I want a way to access the information reSolve already holds: is the aggregate passed to the command function a new one (i.e. it has just been created from the initial value and no projections have ever been applied to it) or is it an old one (i.e. has had projections applied)? I believe that reSolve can tell the difference - am I correct? If so, I could imagine a simple flag passed to the function: myCommand: (state, { aggregateId, isNew, payload: ... }) => {
// check isNew to find out whether the aggregate is new
} I'm not sure whether the suggested placement of this flag makes most sense, there might be better ways of passing it. But I hope this serves to clarify what I'm asking. |
Internally aggregate has a version - number of events applied to it before command. |
Yes! That's what I said in my initial post:
I suddenly forgot about it myself, but I believe this detail would be useful - I would leave it up to the developer to do with what they want, and I also agree with you, Roman, that a collection of higher order functions would be the best approach to make "standard" functionality/patterns available over time. |
It seems to be undocumented, but current aggregate version is passing within command context (third argument): doSomething: (state, command, { aggregateVersion }) => {
...
} |
Wow :) |
I tested it to be sure and this approach works exactly like it should: {
createThing: (aggregate, { payload: { ... } }, {aggregateVersion}) => {
if (aggregateVersion > 0) throw new Error('Thing exists');
...
},
updateThing: (aggregate, { payload: { ... } }, {aggregateVersion}) => {
if (aggregateVersion === 0) throw new Error('Thing does not exist');
...
},
} Unless this API changes, or there are reasons why it shouldn't be done this way, this solves my problem. I'll close this issue. |
Turns out that a command in aggregate A can be called with an aggregate ID that belongs to an aggregate instance of type B. In such a case, the aggregate version that is passed to the command handler will be that of the B-type instance, but the aggregate instance itself is a new (created from |
To be clear. Let's say I send a command to create an aggregate A:
Now I send a command to create an aggregate B. I use the same ID as before:
I will now find myself in the function In this situation, the |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
I think it would be useful if there was a built-in mechanism to detect whether an aggregate exists (in a command handler function) - reSolve knows this, since it has just attempted to retrieve the aggregate before the function is called. Unfortunately, the information is not passed on to the command handler and I am left to my own devices to figure it out. Since many aggregates require this logic to avoid duplicates, there is room for improvement here.
Currently, the only "good" option is to handle a special aggregate field which denotes specifically whether the aggregate exists or not. (Note - I'm aware of several other approaches that are worse than this - please let me know in case you need me to elaborate.)
So I'd have this in
thing.commands.js
:And then this in
thing.projection.js
:Obviously this approach works - but I don't think it should be necessary. It's boilerplate, required for almost any aggregate, and all just to detect a piece of information that is already known to reSolve!
I suggest we use one of these two approaches:
The text was updated successfully, but these errors were encountered: