0

I'm solving a problem that might seem simple, but in event storming I'm not sure how the implementation should be done.

I have an application that works like dropbox, the user get to upload files and on top of those files the user can run a series of operations that will modified the content of it. There is also the possibility to change the name of that file or move it to another location.

I want to track the LastUpdatedTimestamp of the file and the reason of it, and all these updates are done via a FileUpdated event. - for example:

  • A user uploads a file, the FileUpdated wevent at the end of the process would look like {"fileId": "1234556", "LastUpdatedTimestamp": "2022-06-08 10:50:00", "reason": "FileUploaded"}.

  • A user writes something to a file, the FileUpdated event at the end of the process would look like {"fileId": "1234556", "LastUpdatedTimestamp": "2022-06-08 11:00:00", "reason": "FileContentModified"}.

and so on.

I'm not really sure how to keep this update history in event storming for my File object. I don't have an aggregate in this example yet, and I thought that a FileStatus could provide this update history but I'm not fully convinced.

Cesar Flores
  • 762
  • 7
  • 16
  • 2
    Are you sure that you're talking about event storming (a domain exploration technique) rather than event sourcing (a particular implementation technique)? – Levi Ramsey Jun 08 '22 at 19:05
  • Right @LeviRamsey, I'm referring to the implementation technique, and changing the title of the question. Thanks for that – Cesar Flores Jun 09 '22 at 23:26

1 Answers1

0

I find it's usually helpful in event storming to record what facts are in the event: it makes it easier to trace the flows of information through the system. I personally prefer a methodology similar to event sourcing called event modeling, where that's a core part of the technique.

So if we notate that the FileUpdated event contains an updatedAt timestamp and also notes which file was updated, we can formulate a sort of behavior-driven spec even if we haven't mapped out what the aggregates are, perhaps "if we issue a command which results in a FileUpdated(file = "foo", updatedAt = "2022-06-09 20:00"), a command to get the last time file "foo" was updated should result in a date no earlier than "2022-06-09 20:00"".

Note that this can be formulated without a hint of the aggregates, though it does imply that the command resulting in a FileUpdated(file = "foo", ...) event and the command to get the last time file "foo" was updated need to be against the same aggregate, because that's the only way without assuming more of the infrastructure than is necessary to do event sourcing to get that consistency guarantee. That aggregate could be a file, it could even be a file over a day or it the aggregate could be every file (for instance, you decide that you want a command to get all files modified since some given file was modified to be strongly consistent).

Once you have the aggregates, how the state is represented within the aggregate is an implementation detail and arguably just an optimization to save you from replaying every event.

So for the "get the last time "foo" was updated" command, a state that is just a timestamp is sufficient: iterate through the events and when you find a FileUpdated(file = "foo", ...) take the max of the updatedAt timestamp and the timestamp you're accumulating (this even has the advantage of being a CRDT: the event iteration can happen in any order). Of course, as you add events and associated behavior specifications, the obvious optimization is to combine event-handlers that are keyed on one type of event so you iterate through the events once instead of several times as the behavior being exercised changes.

Levi Ramsey
  • 18,884
  • 1
  • 16
  • 30