4
votes

We're building a microservices system with Axon Framework 4.1. In our domain, we have a label concept where we can attach labels to other entities. While labels are normally created and managed by the user, some of these labels are "special" and need to be hard-coded, but they need to be present in the event stream as well.

We have a bunch of aggregates that represent entities that can be labeled with these labels. Some of these aggregates will be used frequently, while others might be used infrequently or are even abandoned by the user.

Sometimes we come up with new special labels. We add them to the code, and then we also need to add them to the event stream. What is a good way to do that?

We can create a special command that we need to send when the updated service is started for the first time. It goes through all the labels and adds the ones that aren't in the event stream yet. This has two disadvantages. First, we need to actually send that command, which either requires us to not forget it, or to add some infrastructure for it outside of the code (e.g., in our build pipeline). Also, other services could have booted up faster with the new labels and started sending commands before we fired our special command. The other disadvantage is that this command will target all aggregates, including the abandoned ones, which could be wasteful of resources and be confusing to end users who might see activity in a document they thought was abandoned.

Ideally, we would like to be able to send the command when Axon has just loaded the aggregate. That way we would be certain that the labels are only introduced in aggregates that are actually used. Also, we could wire this up in code and it wouldn't require us to add infrastructure outside of the application and/or remember to do it manually.

Unfortunately, this feature doesn't seem to exist in Axon (yet) ????.

Are there other (better) ways to achieve this?

1

1 Answers

2
votes

I've got an idea which might help you out on this.

If I understand the use case correctly, the "Label" in your system, which user can introduce themselves but for which also a couple of hard-coded versions exist, is an Aggregate. Based on that assumption, I suggest to be smart with the Aggregate Identifier you are using.

The sole thing that Axon expects from you, is that the Aggregate Identifier is (or can be made in to) a String. Typically a UUID is used for the Aggregate Identifiers, which is a reasonable first start. You can however wrap this UUID in a typed-id object. Taking your "Label" Aggregate, that would opt for a LabelId.

That said, let's first go back to verifying whether a given "Label" Aggregate exists within the Event Stream. The concern you have is rather valid I think; reading the entire Event Stream to figure out whether a given Aggregate instance exists is to big of a hassle.

However, the EventStore can be queried through two mechanism:

  1. The Event Stream from a given point in time (e.g. what the TrackingToken mechanism does).
  2. The Event Stream for a given Aggregate instance, based on the Aggregate Identifier.

It's the second option which is far more ideal in your scenario. Just query the EventStore for a given "Label" Aggregate's Identifier. If you receive a non-empty Event Stream, you know it already exists. Vice versa, if no Events are found, you are certain it's a new "Label" that needs to be introduced.

The crux here is in knowing the "Label's" Aggregate Identifier up front, which circles back to the String storage approach for the Aggregate Identifiers using a typed LabelId. What you could do, is deviate in the LabelId object between a custom "Label" (I'd opt for a UUID here) and a hard-coded "Label". For the latter, you could for example have the label-name, plus a UUID/counter if desired.

Doing so will ensure that all the Events published from a hard-coded "Label" will have an Aggregate Identifier you can anticipate on during start-up.

Hope this is clear and all, if not, please comment on my response below.