5
votes

I would like to implement CQRS and ES using Axon framework

I've got a pretty complex HTML form which represents recruitment process with six steps.
ES would be helpful to generate historical statistics for selected dates and track changes in form.

Admin can always perform several operations:

  • assign person responsible for each step
  • provide notes for each step
  • accept or reject candidate on every step
  • turn on/off SMS or email notifications
  • assign tags

Form update (difference only) is sent from UI application to backend.

Assuming I want to make changes only for servers side application, question is what should be a Command and what should be an Event, I consider three options:


  1. Form patch is a Command which generates Form Update Event

    • Drawback of this solution is that each event handler needs to check if changes in form refers to this handler ex. if email about rejection should be sent

  1. Form patch is a Command which generates several Events ex:. Interviewer Assigned, Notifications Turned Off, Rejected on technical interview

    • Drawback of this solution is that some events could be generated and other will not because of breaking constraints ex: Notifications Turned Off will succeed but Interviewer Assigned will fail due to assigning unauthorized user. Maybe I should check all constraints before commands generation ?

  1. Form patch is converted to several Commands ex: Assign Interviewer, Turn Off Notifications and each command generates event ex: Interviewer Assigned, Notifications Turned Off

    • Drawback of this solution is that some commands can fail ex: Assign Interviewer can fail due to assigning unauthorized user. This will end up with inconsistent state because some events would be stored in repository, some will not. Maybe I should check all constraints before commands generation ?
3

3 Answers

4
votes

The question I would call your attention to: are you creating an authority for the information you store, or are you just tracking information from the outside world?

Udi Dahan wrote Race Conditions Don't Exist; raising this interesting point

A microsecond difference in timing shouldn’t make a difference to core business behaviors.

If you have an unauthorized user in your system, is it really critical to the business that they be authorized before they are assigned responsibility for a particular step? Can the system really tell that the "fault" is that the responsibility was assigned to the wrong user, rather than that the user is wrongly not authorized?

Greg Young talks about exception reports in warehouse systems, noting that the responsibility of the model in that case is not to prevent data changes, but to report when a data change has produced an inconsistent state.

What's the cost to the business if you update the data anyway?

If the semantics of the message is that a Decision Has Been Made, or that Something In The Real World Has Changed, then your model shouldn't be trying to block that information from being recorded.

FormUpdated isn't a particularly satisfactory event, for the reason you mention; you have to do a bunch of extra work to cast it in domain specific terms. Given a choice, you'd prefer to do that once. It's reasonable to think in terms of translating events from domain agnostic forms to domain specific forms as you go along.

HttpRequestReceived ->
FormSubmitted ->
InterviewerAssigned

where the intermediate representations are short lived.

3
votes

I can see one big drawback of the first option. One of the biggest advantage of CQRS/ES with Axon is scalability. We can add new features without worring about regression bugs. Adding new feature is the result of defining new commands, event and handlers for both of them. None of them should not iterfere with ones existing in our system.

FormUpdate as a command require adding extra logic in one of the handler. Adding new attribute to patch and in consequence to command will cause changes in current logic. Scalability is no longer advantage in that case.

1
votes

VoiceOfUnreason is giving a very good explanation what you should think about when starting with such a system, so definitely take a look at his answer.

The only thing I'd like to add, is that I'd suggest you take the third option. With the examples you gave, the more generic commands/events don't tell that much about what's happening in your domain. The more granular events far better explain what exactly has happened, as the event message its name already points it out.

Pulling Axon Framework in to the loop, I can also add a couple of pointers. From a command message perspective, it's safe to just take a route and not over think it to much. The framework quite easily allows you to adjust the command structure later on. In Axon Framework trainings it is typically suggested to let a command message take the form of a specific action you're performing. So 'assigning a person to a step would typically be a AssignPersonToStepCommand, as that is the exact action you'd like the system to perform.

From events it's typically a bit nastier to decide later on that you want fine grained or generic events. This follows from doing Event Sourcing. Since the events are your source of truth, you'll thus be required to deal with all forms of events you've got in your system.

Due to this I'd argue that the weight of your decision should lie with how fine grained your events become. To loop back to your question: in the example you give, I'd say option 3 would fit best.