Skip to content
This repository was archived by the owner on May 4, 2026. It is now read-only.

Commands and Events

Tijl Leenders edited this page Feb 1, 2020 · 2 revisions

Commands

A Command encapsulates the desire for domain changes that absolutely have to happen in the same unit of work.

Consequences:

  • A Command contains all information needed to attempt a domain change/action, including relevant receivers
  • A Command can be executed immediately or at a later time via a queue (so it can be sent asynchronously and not create UI delays)
  • A Command can fail (returns a Result object with possible error messages and parameters info)
  • A Command persists to the Domain Repository when it succeeds, or - if relevant for the domain - also when it fails
  • Even though a Command can be persisted to a queue, it does not persist itself to the Domain Repository (the keeper of 'the truth'), only the relevant domain Event(s) it has caused

Why can't you persist a Command to the Domain Repository?

  • A Domain Repository stores domain Events. Events are state changes that have already happened. When attempting ChangeEntryTitleCommand, nothing has happened/changed in the domain yet. Only persist EntryTitleChangedEvent when the Command is successful, and it is only definitively successful the moment the Event is persisted.

But what if it is relevant for the Domain to know if a Command happened? Like sending a Command to another system? Or for 'undo' purposes?

Events store all the information necessary to issue an additional corrective/undo Command. When it is relevant for the domain to know if a Command happened, like when sending the Command outside the scope of influence (ie a request to somebody else to change the title of their Entry) it is still not necessary to persist the ChangeEntryTitleCommand, rather you issue a different Command like RequestTitleChangeCommand which results in TitleChangeRequestedEvent - regardless of the eventual results (TitleChangeRequestDelivered/Denied/Lost).

The point is: the request that happens does not need to happen in the same unit of work as

  • the sending of the request to the receiver
  • the decision by the receiver (if (s)he ever get the request) to accept or deny

A simple Command example is found here: https://en.wikipedia.org/wiki/Command_pattern#Java (although Freelist currently does not register commands to objects like in the example; whatever UI piece needs a command just constructs it on the fly.

Commands are currently constructed in the UI layer and should ideally get moved to domain layer (https://github.com/tijlleenders/Freelist-android/issues/31).

Events

An Event expresses a state changes to a single entity that has already happened.

Consequences:

  • Because events have already happened they are final => you can only make corrections with new Commands causing new Events, just like in accounting
  • If multiple events logically occur together they need to be persisted together - or not happen at all (design decision: persistence is the proof of existence)
  • An event contains all the information necessary to be applied to an Entity/Aggregate and recreate the state changes. This starts with the identifier of the Entity/Aggregate it 'belongs' to.
  • Reversing state changes expressed by the Event is only possible if the Event stores the before and after state (design decision: store only the after state in an event to avoid complexity and redundancy; for a consistent undo experience the previous state has to be retrieved by searching for the previous event affecting the state element)

Commands and corresponding Events

  • CreateResourceCommand => ResourceCreatedEvent
  • CreateEntryCommand => EntryCreatedEvent
  • ChangeEntryDescriptionCommand => EntryDescriptionChangedEvent
  • ChangeEntryDurationCommand => EntryDurationChangedEvent
  • ChangeEntryParentCommand => EntryParentChangedEvent
  • ChangeEntryTitleCommand => EntryTitleChangedEvent
  • ScheduleEntryCommand => EntryScheduledEvent (Entry state change) + CalendarEntryAddedEvent(s) + CalendarEntryRemovedEvent(s) + CalendarEntryMovedEvent(s) (Calendar state changes)

NB:(Multiple events caused together by the same Command are persisted together or not at all)

Clone this wiki locally