Eventuality
This is simply a POC and my way of writing down my thoughts about how I would want to implement a CQRS system. The focus here is on components/tools and functions that can be interchanged rather than a full framework.
components
-
Domain Model (Aggregate)
This is the object that represents the state of our model. Only it can change its own state as a result of Commands. Each state change results in an Event.
-
EventStore
A storage facility for events/history/facts. This can be in memory or backed by a database. What I have here is in memory.
-
EventBus
The medium through which facts of state change are shared to interested observers.
-
EventListeners
These are the observers of facts of state changes. They are functions to be invoked when the domain model has changed its state.
-
Repository
A component through which we create and access the domain model.
-
Commands
Objects representing an intent of state change by the user on the domain model.
-
CommandHandlers
Functions with the purpose of communicating the intended state change to the domain model.
-
Events
Objects representing a fact of state change in the domain model.
Based on these definitions of the components, the system should work like this:
CommandHandlers are effectively
CH(Command) -> [...Event]
Actions the domain model can execute are
A() -> [...Event]
This means every intention to change the state of the domain model results in n events (where n = 0 is a failure and n > 0 is success).
Examples of usage
Of course, I'll use a Todo application because that's the app any system can build.
Eventuality = require 'eventuality'
Todo = Eventuality.defineAggregate
name: 'Todo'
state:
description: null
completed: false
methods:
complete: ->
@state.completed = true
Eventuality.Event(aggregateId: @id, name: 'TodoCompletedEvent', payload: {completed: true}, state: @state)
TodoCommands =
CreateTodo: ({id, description }) -> name: 'CreateTodo', message: {id, description}
MarkAsCompleted: ({ id }) -> name: 'MarkAsCompleted', message: {id}
TodoEventStore = Eventuality.EventStore()
TodoRepository = Eventuality.Repository 'Todo', Todo, TodoEventStore
TodoCommandHandlers =
CreateTodo: (attrs) -> TodoRepository.add attrs
MarkAsCompleted: ({ id }) ->
TodoRepository.load(id).then (todo) ->
todo.complete()
TodoEventBus = Eventuality.EventBus()
TodoCreatedEventListenerToUpdateDB = (event) -> # Update database...
TodoCreatedEventListenerToLogStuff = (event) -> # Do some logging...
TodoEventBus.registerListeners
TodoCreatedEvent: [
TodoCreatedEventListenerToUpdateDB
TodoCreatedEventListenerToLogStuff
]
TodoCompletedEvent: [
(event) ->
# More stuff to be done
]
It can(doesn't have to) all come together like so...
TodoFlow = Eventuality.Flow
eventStore: TodoEventStore
eventBus: TodoEventBus
commandHandlers: TodoCommandHandlers
## Later when the user wants to do things...
TodoFlow.dispatch TodoCommands.CreateTodo id: 'todo1', description: 'Create a todo'
TodoFlow.dispatch TodoCommands.MarkAsCompleted id: 'todo1'