Hacker News new | ask | show | jobs
by taeric 3551 days ago
As someone that has fallen for the "event sourcing" promise before, the article does a decent job explaining the promise. Not sure if it will be the next article, but the actual task of delivering on this work is where things break. Hard.

The vast majority of the things you will ever program are pretty much guaranteed from one statement to the next. Hard boundaries, where things can fail, are often decently understood and actually quite visible in the code.

Moving everything to be an event completely throws this out the window. You can take a naive view, where you pretend from one event to the next is safe to happen. However, to start building up the system to cope when this is not the case starts to build a complicated system. In areas that are decidedly not related to your business domain. (Well, for most of us.)

Maybe some day there will be a system that helps with this. Until then, my main advice is to make sure you have solved your system with a naive solution before you move on.

5 comments

Agree with the potential for complexity. Here's how we've dealt with this (on a so far / so good basis): Didn't seem necessary to go asynchronous and beef up on heavy infrastructure, so we went with simple in-thread, in memory message buses to start with. I think a lot of the perceived complexity of building event sourced systems comes because people start with the heavy plumbing instead of going YAGNI in order to get the domain model implemented. Easier to beef things up as needed once everything works.

I became disillusioned with doing the naive solution, for two reasons:

Found it to be impossible (from a time/project mgmt perspective) to ever replace it with the "non-naive" one, so it turns into the usual mess because CRUD doesn't work well as you load more functionality on it over time.

Secondly ... it's thinking machines. From a business perspective, does it still make sense to hand code glorified rolodexes without behaviour? Maybe Excel does the trick. Seeing it as a red flag if someone asks me to build dumb data entry forms in 2016.

Therefore, I always start eventsourced theses days. YMMV.

This seems solid advice. I guess my main questions are:

* If you are in-memory and in-process, why bother with the events in the first place? (Simpler put, why not go with the simpler process based solutions?)

* If you are not testing distributed, how do you know you will be able to distribute?

In particular, there is a very large chance that you will have the same difficulty in replacing an in-memory/process solution that you would have had with a naive one.

And don't underestimate the amount of manpower you can get with success. Nor the amount of features that will not help get success.

I don't really do eventsourcing for technical reasons. To me it's about creating an environment for project delivery where teams can succeed because they own more of their stack, from an org chart & organizational perspective.

As far as code is concerned, I see that as coming down to managing coupling & cohesion in such a way that the various pieces can be designed, built, supported, deployed and enhanced by the same team, with minimal need to wait for, coordinate with or be on the same page with any other team.

I think event sourcing & CQRS are great for enabling that because they result in the lowest form of coupling: Commands are fire and forget. You don't care who subscribes to your events. You don't care who publishes events you subscribe to. You never query anything you don't own in order to get information you need for your business logic. While the system is still small, we seem to get these advantages just fine when we do in-memory and in-process. Once package management, performance, units of deployment, etc. become an issue, we build out to add infrastructure, as needed. Because the pieces are pretty standalone within the code, it's not all that complicated to do, compared to "traditional" CRUD systems.

Not testing distributed: It's possible to deduce a lot with testing locally if it's simple to understand what's coupled to what and if good shared contracts for commands and events are in place, but ultimately the proof is of course in the pudding. When the time comes, there is a big advantage: The system is already fully operational and testing comes down to "does it still work?" If one were to do the plumbing before the business functionality and tests that in isolation ... does that really amount to knowing that it'll still be OK once it actually runs what it needs running ...? For starters, there is a danger of over-engineering because the required performance characteristics of the end products are more difficult to asses if there is no end product yet.

I think the problem is best seen by considering the statement "You don't care who subscribes to your events." This is great when you are literally doing something only on one end of the event creation barrier. This makes sense when creating something takes a lot of effort.

However, for many many shops, this is an abstraction they will never get to. They care intimately about who will subscribe to the events they publish, because they are planning on doing something as the primary subscriber.

> Moving everything to be an event completely throws this out the window.

This is either an antipattern or unrelated to event-sourcing depending on how you read it. "Event sourced" means that state within a transaction boundary is built ONLY through events which are regarded as persistent and immutable; evented is the term I'll use for state which is built when events happen, when those events may or may not be recorded, and may happen before or after state is changed and are independent of the state change.

If you meant "evented" then I would say that there are lots of message-based systems that aren't failing hard, and a lot that are, which says to me that there are patterns some of those systems are using to manage the nature of async, evented development that others aren't.

If you mean "event sourcing" then the application of event-sourced data is neither an application architecture nor appropriate for all areas of your application[1,2]. If you were trying to apply event-sourcing in this way its not surprising you ran into problems with it.

> Until then, my main advice is to make sure you have solved your system with a naive solution before you move on.

It is important to really know and understand the problem domain you are applying ES to. Having a strategy to upgrade streams to new versions of your domain model is a good idea if you're applying ES to a business without a well-understood domain. However, it is very, very difficult to back into an ES implementation from a "naive" solution, which I'm reading as "CRUD".

[1] https://www.youtube.com/watch?v=LDW0QWie21s [2] https://www.infoq.com/news/2016/04/event-sourcing-anti-patte...

Unfortunately, you lost me in that first paragraph. I think you left off a set of quotes on the first use of "evented."

Regardless, I meant the idea of moving everything to a communication of events between subsystems. To be fair, the sibling read me correctly and stated that if you just ignore the distributed nature of events, then things aren't that hard.

However, it is easy to follow the lure of "I'll go ahead and make this work for the distributed case" from the beginning. This is for two reasons. First, why not? :) Second, it is seen as something that would be very hard to add in later.

So, I can't disagree that it is an anti-pattern or unrelated. However... I would be surprised if the next version of this article didn't go over the distributed nature. Indeed, it already covers how this more correctly mirrors the distributed nature of the organization.

> I think you left off a set of quotes on the first use of "evented."

Indeed, sorry I lost you there.

> Regardless, I meant the idea of moving everything to a communication of events between subsystems.

Unless the persisted events are the primary representation of data, this describes "evented" (or message-based) rather than ES.

> To be fair, the sibling read me correctly and stated that if you just ignore the distributed nature of events, then things aren't that hard.

> However, it is easy to follow the lure of "I'll go ahead and make this work for the distributed case" from the beginning

So it seems like what we're actually saying here is "Distributed systems are hard to build reliably"—which wouldn't seem to be a surprising result on HN, and certainly a sentiment I'd agree with.

I've moved to using Elixir and Erlang primarily because processes/actors have such a natural fit for this kind of data that I get really grumpy when I have to work in something else now. There is also a long history of distributed systems within the BEAM ecosystem so there is a legacy of design meant to deal with the inherently unreliable nature of building distributed.

Do you have specific examples of where things break hard?

We are currently using ES end to end for a distributed application including a wearable device, iOS app and scala backend, and up to now, the things that broke hard in the system are the naive/non ES parts.

For information, on the server-side, we are currently experimenting with GetEventStore (https://geteventstore.com/), which seems to be working well for us.

I'm curious what has broken that was not directly related to the hard split between the wearable and the backend. This is a particular case where you are by definition doing a distributed system, so any attempts to hide that will be problematic.

My main thoughts are anywhere you are trying to hide that distributed nature, things will go awry. Add to that, anywhere you have introduced the potential for things to be distributed.

The sibling post about keeping it simple as you build up your eventing system is pretty accurate. Remember you are trying to solve an actual customer problem. Keep pointed on that and do not get distracted by any neat engineering problems that come along the way. (This is not to say you will not have to solve some... but if you are solving a neat problem that was not needed for the customer's problem, you are going to have trouble.)

I always hoped that materialized views would rise to the occasion and we could have the best of both worlds.

Events for writes and current data for most reads.

Just don't do eventual consistency if the domain doesn't allow for it and if performance is OK. From a functional perspective there is no need for it in CQRS/ES systems.

However ... if going with eventual consistency, it's essential that the write side is consistent. The read models - not so much. This is because every aggregate (... if Domain Driven Design terminology is your thing) represents a transactional boundary within which state needs to be consistent in order for the business rules to work correctly. Read models are more forgiving.

I'd suggest people start with CQRS+Events before going to CQRS+EventSourcing.

In the first case, you keep however you were loading/saving your entities, and modify them to emit events as they mutate. Then you can play with sending those events to external systems, or using them to drive read-models for queries, etc.

In the second case, you go further, and "dogfood" those events so that they are the authoritative record of what the entity is at a given point in time.