Description
Event Sourcing and CQRS building blocks.
Goals
- incentivize the usage of Event Sourcing and CQRS as good choice for domains that can leverage the main benefits of this design pattern;
- offer the essential building blocks for using Event Sourcing in your system with proper contracts, but allowing specific needs to leverage what Elixir already brings to the table, for example, concurrency;
- leverage functions and reducers for executing commands and applying events, facilitating stateless tests;
- allow customization for fine-grained needs without compromising the principles;
Incident alternatives and similar packages
Based on the "Eventhandling" category.
Alternatively, view Incident alternatives based on common mentions on social networks and blogs.
-
event_bus
Simple event bus implementation with topic filtering and built-in event store and event watcher. -
reaxive
Reaxive is a reactive event handling library, inspired by Elm and Reactive Extensions. -
goldrush
Small, Fast event processing and monitoring for Erlang/OTP applications. -
Seven Otters
Seven Otters is a set of facilities (macros, functions, modules, etc.) developed to create CQRS/ES solutions in Elixir on BEAM virtual machine. -
wait_for_it
Provides convenient and easy-to-use facilities for synchronizing concurrent activities. -
cizen
Build highly concurrent, monitorable, and extensible applications with a collection of sagas.
Scout APM - Leading-edge performance monitoring starting at $39/month
Do you think we are missing an alternative of Incident or a related project?
README
Incident
Event Sourcing and CQRS building blocks.
Special thanks to my friend Paulo Gonzalez for the name suggestion for this library.
This library is in constant development phase and evaluation, that means core changes still can be made. While I am already using Incident in production, please be advised about the risks, once the library reaches its maturity, a release 1.x.x will be created.
Goals
- incentivize the usage of Event Sourcing and CQRS as good choice for domains that can leverage the main benefits of this design pattern;
- offer the essential building blocks for using Event Sourcing in your system with proper contracts, but allowing specific needs to leverage what Elixir already brings to the table, for example, concurrency;
- leverage functions and reducers for executing commands and applying events, facilitating stateless tests;
- allow customization for fine-grained needs without compromising the principles;
Getting Started
Example Application
There is an example application that implements a Bank application for reference with great documentation and including all the details and usage in IEx as well.
It also contains projections specific to the application domain with migration and schemas defined.
This example application will give you all the details in how to use Incident, including integration
tests for both InMemory
and Postgres
adapters for both Event Store and Projection Store.
Blog Posts
Event Sourcing and CQRS
In a nutshell, Event Sourcing ensures that all changes to application state are stored as a sequence of events. But if you are new to Event Sourcing and CQRS I highly recommend watch Greg Young's presentation at Code on the Beach 2014 before moving forward.
Main Components
Command
It is a data structure used to define the command attributes with some basic validation.
Command Handler
It is the entry point of a command in the write side. It performs basic command validation and executes the command through the Aggregate logic.
Aggregate
Defines how a specific entity (User, for example) in your domain will execute each of its commands and apply each of its events. The aggregate itself only defines the logic but not its state.
Aggregate State
Defines the initial state of an Aggregate and it is able to calculate the new state by replaying all the events through the aggregate logic.
Event
It is a data structure that defines the event data attributes.
Event Handler
Receives a persisted event and perform actions in the read side such as to update the projection for an individual aggregate. Another usage is to build a new command and send to the Command Hanlder when specific events should trigger a new cycle.
Projection
It represents the current state of an individual aggregate (User ID: 123456, for example) after replaying all events. Your application reads/queries data from the projection, that is similar to a persisted cache.
Event Store
All events from all aggregates are stored in the Event Store. The Event Store uses the Port/Adapter
design pattern so through configuration you can define which adapter your application will use to store
the events. Currently, Incident comes with two adapters, an InMemory
to be used as playground and a
Postgres
one.
Projection Store
Very similar to the Event Store, the Projection Store uses the Port/Adapter design pattern so through
configuration you can define which adapter your application will use to store the aggregate projections.
Currently, Incident comes with two adapters, an InMemory
to be used as playground and a Postgres
one.
Installation
If available in Hex, the package can be installed
by adding incident
to your list of dependencies in mix.exs
:
def deps do
[
{:incident, "~> 0.5.1"}
]
end
Configuration
Event Store and Projection Store Setup
Configure incident
Event Store and Projection Store adapters and some options. The options will
be passed in during the adapter initialization.
In Memory Adapter
The goal of using the In Memory Adapter is to provide a quick way to store events and projections, as a playground tool. This adapter is not suppose to be used in a real application.
config :incident, :event_store, adapter: Incident.EventStore.InMemoryAdapter,
options: [
initial_state: []
]
config :incident, :projection_store, adapter: Incident.ProjectionStore.InMemoryAdapter,
options: [
initial_state: %{}
]
Postgres Adapter
The Postgres adapter uses Ecto
behind the scenes so a lot of its configuration it is simply
how you should configure a Postgres database for any application using Ecto.
There will be two Ecto Repos, one for the events and another one for the projections.
Optionally, you can even define two different databases, so the events database will contain only one table (events) and the projections database will contain one table fore each projection type.
Add the Ecto Repo modules for both databases:
defmodule AppName.EventStoreRepo do
use Ecto.Repo,
otp_app: :app_name,
adapter: Ecto.Adapters.Postgres
end
defmodule AppName.ProjectionStoreRepo do
use Ecto.Repo,
otp_app: :app_name,
adapter: Ecto.Adapters.Postgres
end
In your application config.exs
specify the repositories:
config :app_name, ecto_repos: [AppName.EventStoreRepo, AppName.ProjectionStoreRepo]
In your application dev|test|prod.exs
(the example below defines two databases but it could be only one):
config :app_name, AppName.EventStoreRepo,
username: "postgres",
password: "postgres",
hostname: "localhost",
database: "app_name_event_store_dev"
config :app_name, AppName.ProjectionStoreRepo,
username: "postgres",
password: "postgres",
hostname: "localhost",
database: "app_name_projection_store_dev"
config :incident, :event_store, adapter: Incident.EventStore.PostgresAdapter,
options: [
repo: AppName.EventStoreRepo
]
config :incident, :projection_store, adapter: Incident.ProjectionStore.PostgresAdapter,
options: [
repo: AppName.ProjectionStoreRepo
]
Create the application databases running the Ecto mix task:
mix ecto.create
Use the Incident Mix Task below to generate the events table migration, after that, run the migration task:
mix incident.postgres.init -r AppName.EventStoreRepo
mix ecto.migrate
The migrations and schemas for the projections will depend on your application domains and it follows the same process for any Ecto Migration.
Planned Next Steps
The list below is the upcoming enhacements or fixes, it will grow as the library is being developed.
- [ ] allow Incident to be used by more than one application within the umbrella, if needed;
- [ ] add Telemetry module and trigger telemetry events;
- [ ] standardize and document approach for dealing with stale commands and/or race conditions;
- [ ] run migrations when using
mix incident.postgres.init
forPostgres
adapter; - [ ] allow custom error modules to be used and incorporate as part of the contract in some components;
Contributing
We appreciate any contribution to Incident. Please see the Code of Conduct and Contributing guides.
Documentation
The full documentation can be found at https://hexdocs.pm/incident.