During my series on event sourcing, I got a question a couple of times: “Why don’t you use an event sourcing library? Why have you built your own kind of library?” So part seven is about the trade-offs of using out-of-the-box libraries versus building your own – even today.
When we started
When we started developing our time-tracking system back in 2015, we made the architectural decision to use event sourcing. Event sourcing aligns well with what a time-tracking application needs to do. Back then, there were simply no event sourcing libraries in the .NET space – at least we didn’t know any. So, we started building our own little “library” one use case at a time. Soon, we realised that the majority of the most important event streams in our system are bi-temporal. They have two timestamps attached: one for when they entered the system, and one for when they are valid or take effect. E.g. I entered today that I am going on vacation next month. Therefore, we optimised our code to support this use case in a simple way.
When we changed the bases of our architecture
In 2020, we made a fundamental change in the direction of our architecture. We introduced F# – alongside C# – and shifted the style of our code from OOP-focused to FP-focused. We already wrote a lot of functional-style code in C#, but F# boosted the adoption of FP concepts.
Back then, we were faced with the decision of either porting our event-sourcing code to F# or introducing an event-sourcing library. Still, the event sourcing library landscape was small. Adopting a out-of-the-box library would have ment to at least have one of these downsides:
- Switching databases (from SQL Server to PostgreSQL)*
- Losing bi-temporal even sourcing capabilities**
- Re-modelling existing events to match the needs of the library
So, porting the existing code to F# was the bettter solution. This may sound easier than it actually was. The code doing the projections has to be very fast. So porting code from C# to F# isn’t straightforward. The F# code had to be performance optimised on its own. Still, it was the most cost-efficient decision – even when considering long-term maintenance costs.
Looking back, it was the right decision because we encountered some bi-temporal event sourcing needs that current libraries don’t support well or not at all. (you know, more on that in a later post)
* Not that PostgreSQL would be a bad choice, but switching wasn’t an option because of the high investment: changing a lot of database access code, migrating all the data, learning a new tool (especially how to operate it).
** Or bi-temporal event sourcing would at least become harder to implement.
What if we started today?
I’m actually not sure how we would decide, but here are some thoughts that would shape the discussion:
- Normal and bi-temporal projection setups are a bit different in our current solution anyway, so it would probably be okay to use different solutions for normal and bi-temporal event sourcing.
- What database do we want the events to be persisted in? Consider that we also have non-event-sourced data, and the backup strategy should include all relevant data.
- The risk of lock-in and the risk of the library dying? How easy is it to leave the chosen library (code and data)?
- How well does the library support F#? Even if it’s .NET, ergonomics can vary.
- Risk of supply chain attacks – sadly, a very hot topic nowadays.
Summary
In the beginning, there were no libraries available, so we built our own. We stick with our own approach when we made a major architectural change in our system because of the effort involved. And today, we are still happy with the approach chosen. Bi-temporal event sourcing is still a niche, and I’m only aware of a single library that supports it in the .NET space.
So, this was an architectural choice well made (as if we had a choice back then 😅). Maybe, someday, I’ll find time to blog about our not-so-great architectural decisions. Let me know if you would like to read about our failures.
That’s it for today – it is a short week due to a holiday, and there was a lot of mountain biking due to the sunny weather.
Is this open source? Where is the repo?
No, the code isn’t open source. This series is about the concepts, not about this exact implementation, which is tailor-made for our needs.