Rather than copy the documentation here verbatim, you can read it here, but as a quick Tl;Dr:
- All events that come in are normalized, and then go through the
reducer(event handler)-function configured by you. This allows events to modify the state of your system. The reducer is a pure function, which allows for easy reasoning and testing (including property-based tests).
- Around this, you can add a
context_provider, which, can transform your application state to look up that part that is significant for the current event. This allows you to use large systems that have databases, store something in GenServers, are distributed etc. to work with the system as well. (Instead of having a ‘single source of truth’ the ContextProvider sets up a ‘single source of truth for this event’) and after running the reducer, is able to use the results of the reducer-call to e.g. store it back in the database. Because this is separate from the reducer, it can be tested separately!.
- Around this, you can add middleware to do instrumentation, or add things like database-transactions (doing these here mean that the ContextProvider can use a declarative db-interaction-style like e.g.
Ecto.Multi, which again makes things easier to test!).
- You can decide on different configurations for different environments, or a different one per-test, etc: Configuration is done in the final parameter passed to
TeaVent.dispatch, or in the Application.env, falling back to some sensible defaults whenever possible, and raising errors on missing required options, or unknown options. This is based on the discussion on making better configurations in libraries.
The library strives to cater to each of the following needs:
- ‘full’ Event Sourcing where events are always made and persisted, and the state-changes are done asynchroniously (and potentially there are multiple state-representations (i.e. event-handlers) working on the same queue of events).
- a ‘classical’ relational-database setup where this can be treated as single-source-of-truth, and events/state-changes are only persisted when they are ‘valid’.
- a TEA-style application setup, where our business-domain model representation changes in a pure way.
- A distributed-database setup where it is impossible to view your data-model as a ‘single-source-of-truth’ (making database-constraints impossible to work with), but the logical contexts of most events do not overlap, which allows us to handle constraints inside the application logic that is set up in TEA-style.
What is missing from the current version is:
- Better documentation for the current example.
- A proper example (e.g. an example project) of how to use it with e.g. Ecto.
- An even simpler ‘hello-world’ example that could be put in the documentation (/doctests).
- Tests of all the bells and whistles.
- Fixing all typespecs and making sure Dialyzer is happy with them.
And of course I am very eager to hear feedback from you!