Factor #2 is to use an event system to manage the state of the app. Instead of writing long procedures which manipulate state in-memory and persist them in the database at the end, perform simple operations (for e.g. CRUD on a resource) that initiate events and persists them to an event log. The backend should listen (or be triggered) from this immutable event log. The event system should have the following properties:
- Atomic: Mutations to the application state should atomically create event(s).
- Reliable: Events once emitted should be delivered (to any consumer) atleast once.
Traditional | 3factor (Factor #2) |
---|---|
On an API call: fetch relevant resources, perform business logic in a transaction and finally commit the transaction | On an API call: Produce and persist event(s) which represents the intent |
Avoid using async functionalities in a procedure because it is hard to rollback on failure | Fundamentally async |
Implement error recovery logic in case of crashes | No error recovery logic required as events are atomic |
There are numerous benefits of architecting apps based on an immutable event log:
-
The biggest advantage in writing and reading from an event log is how it seamlessly handles error recovery. Whereas in a traditional workflow there will be some additional logic to recover from intermittent crashes because of partial side-effects, there is no such logic required if your application is emitting atomic events. The application proceeds (crash or not) based on the current event log.
-
With the application only producing and persisting events, there is a detailed audit trail of each action in the application. Hence, we get logging and observability built-in with this type of system.
-
In case you need to replicate your entire app in a different environment, it is easy to do so by just reprocessing each event in the event log.
A reliable event system can be hard to implement but it can be done in few ways:
-
Change data capture patterns can be used to atomically generate events on database changes. There are tools like Debezium which provide reliable streaming of change events to your application.
-
One common change data capture pattern is using triggers on tables to write to an event log. A implementation of such a system is Hasura Event Triggers.
-
Event sourcing is another pattern which can be used to set up an event system: https://martinfowler.com/eaaDev/EventSourcing.html