-
-
Notifications
You must be signed in to change notification settings - Fork 16
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
Add support for "Dynamic Consistency Boundary" (DCB) #166
Comments
Also note that if we're to generate a consistencyTag in the db, it should be based on "id+source". Also, how well does the consistencyTag calculation do for large streams? |
We must also consider backward compatibility with old versions that uses "stream version" |
After talking to chatgpt (🙇♂️), we came up with this approach:
A nice thing with |
Solves unique email constraint: |
Idea: If we have a globalposition, we could return the global poisition of the last written event as the "consistency marker" (see this ES impl). We can then use a query like this: { $and : [ {"globalposition" : {$gt : <consistencyMarker> }}, {$not : { $match: <criteria>}}] } To determine if we can write the events within a transaction. |
The goal of this issue is to support the approach discussed in various talks such as this.
First of all, WriteCondition should be renamed and a "condition" method should be added:
Actual Example
A "User" has a name and an address. Atm, there are no invariants between name and address, so we decide to create two deciders/aggregates/functions for the User, one that deals with "name changes" (i.e. one that is interested in "name" related events), and another that deals with "address changes" (i.e. one that is interested in "address" related events).
Now let's say we have a stream for this User at stream1 that contains the following events:
If the user wants to change his/her name again, this is how it can be done without taking the address events into account:
The
EventStream
returned byread
looks something like this today:This should be changed into something like this:
Now, in the ES impl we can no longer just check that the streamVersion is equal to the stream version in the event store when writing new events. Instead, before returning the
EventStream
we need to calculate theconsistencyTag
(mongodb example):This will generate a unique
consistencyTag
per write. Note that we don't store the consistency tag in the database, I don't think that is required, it's an in-memory thing and it's calculated on the fly. So, instead of reading the "stream version" and comparing it to the supplied stream version of theWriteCondition
, we can just check if the result of the "consistencyTag" matches. This means that we're also backward compatible, because the default streamVersionEq condition will just yield "streamId+streamVersion" consistency condition.Now, if the business rule changes, and you cannot change the name to "John Doe" if the address is "Address street", we can change the "consistency condition" to include more events (or the entire stream like before).
Note 1
While the above (hopefully) solves the case where you have smaller "logical aggregates" than the number of different event types you have in the stream, it doesn't solve the reverse problem. For example, if you represent the "User" in two different streams, one for name and one for address:
To solve this case, if the new business rule is applied, the consistencyCondition must span multiple streams so the
EventStream
model above needs to be changed. Maybe to this:where
StreamInfo
contains "streamId" and "version" for each involved stream. Then we can we can perform the use case like this:Note the subtle difference on the "read" method here. We don't pass a "streamId" explicitly, but only a
ConsistencyCondition
. This means thatread(<streamId>, <consistencyCondition>)
will just do:Another easier and arguably better way (if only these two streams exist for a User), would be to change the consistency condition to make use of a "subject" filter:
Note 2
A cool thing if we implement what's implied by "Note 1" is that we can have consistency conditions that selectively pick explicit events from different streams like this:
Note 3
We should NOT replace "version" with the consistency tag. This won't be good because then projections have no way to compare etags "in time" (i.e. now you can check if a version is greater than another version).
Note 4
Consistency conditions will only be performant if correct indexes are added.
Note 5
Maybe it would be a good idea to include some sort of "global time" when a consistency tag is generated:
Then the
EventStream
could contain aConsistencyTag
object:Idea: Could this replace the "stream version" altogether? For example, instead of doing:
you could do:
The "consistencyTag" here is constructed from the mongo aggregation above, with an (implicitly created) "consistency condition" of "streamId".
Maybe it would be a good idea to write the
consistencyTag
, including thetimestamp
instead of the version. This way you would have a global position as well!? (it'll fail as soon as the time is changed on the server though which is bad).Note 6
This is a variant of "Note 5", but we instead combine the md5 checksum + timestamp into the "consistency tag":
Then in the code we could Base64 encode the
rawConsistencyTag
so that it's possible to easily just send it in HTTP query params etc (this cannot be done in MongoDB). TheConsistencyTag
object inEventStream
may look like this:The cool thing with this is that projections (or whoever else) that need to check if one
ConsistencyTag
is before or after another can easily do so (and it works globally).The text was updated successfully, but these errors were encountered: