Hi everyone,
If you have a Java Spring Boot application with a PostgreSQL database, you can implement Event Sourcing without introducing new specialized databases or frameworks.
If you have an application dealing with an entity called Order, you should adopt Event Sourcing to keep track of all changes, and know how the Order got into the current state.
Event Sourcing gives you:
1. the true history of the system (audit and traceability),
2. the ability to put the system in any prior state (debugging),
3. the ability to create read projections from events as needed to respond to new demands.
There are several well-known specialized frameworks and databases for Event Sourcing: EventStoreDB, Marten, Eventuate, to name a few.
But adopting a new framework or database you are not familiar with may stop you from trying the Event Sourcing pattern in your project. But you can actually implement Event Sourcing with a few classes and use PostgreSQL as an event store.
The "postgresql-event-sourcing" project is a reference implementation of an event-sourced system that uses PostgreSQL as an event store built with Spring Boot. Fork the repository and use it as a template for your projects. Or clone the repository and run end-to-end tests to see how everything works together.
The project describes in detail:
- database model for storing events,
- synchronous and asynchronous event handlers,
- CQRS,
- Transactional Outbox pattern,
- Polling Publisher pattern,
- optimized publisher that uses PostgreSQL LISTEN/NOTIFY capabilities,
- and more.
This project can be easily extended to comply with your domain model.
The source code is available on GitHub <https://github.com/eugene-khyst/postgresql-event-sourcing>.
You gain temporal querying with a history/audit table that mirrors the main one (or not -- if you prefer not to, and instead want to cram everything into a jsonb column).
Combine it with TSTZRANGE and you can better express the bounds of when the data was last amended and valid. A "period" column has a range `[start, null)` indicating it's current; `[start, end)` indicating an older record. Your main table is always current, with the history table recording every change made. The benefit of this approach is that you can use a GiST index and postgres' datetime ranges to find rows that intersect a point or range in time. If it gets too big, then think of ways you can roll up or prune old records, as needed.
And you can have all of this without compromising on your table structure. Using an ORM and you can have it mirror your audit tables as needed, or of course you can use any number of other methods, such as, ugh, table inheritance or a bit of plpgsql + create table as magic.
Audit tables are useful, and they can approximate a lot of this event system stuff but without the downsides.