I tend to get caught up in rabbit holes. A long time ago, I said: “I need a scalable framework for hosting WordPress, so that I don’t have to worry about what happens if my blog goes viral. [Which it did last summer]” Then I said, “I want to bring my prank calling software back to life, which means I need to create a new language.”
The first part of creating a new language is discovering if you even need one. So, I wrote a framework for nodejs based on the core concepts. I used it to build a proof-of-concept for the prank calling app. It worked. But I also learned some things I want to share with you about event based programming.
What is event based programming? It’s similar to low-level programming found in the OS, where you have system calls, interrupts, etc. But, it’s also not quite the same. Event based programming offers no “function calls” in the regular sense. It offers nothing but events that just “happen” and must be reacted to … or not. Writing event based applications requires a huge change in thinking.
Time Ceases to Exist
When your code reacts to an event, you won’t know if it’s been 1 microsecond or 20 minutes since the event fired. Sure, it will be in the metadata of the event, but choosing not to react because your infrastructure started lagging isn’t a good design. So, you can’t rely on time. When I was building my proof-of-concept, I ran into this problem some. I wanted to score points based on time, and restrict calls to 10 minutes.
I decided to build in a “heartbeat” concept. Where an “origin” event would fire off a heartbeat. A heartbeat would fire another heartbeat after 10 seconds, ad infinitum. This worked, after solving another problem (events firing events with recursion). It also required distributed locks to prevent more than one “origin” event to ever fire. It was a hack, for sure. I’m curious if anyone has a better solution they can think of?
Events Firing Events
When an event fires an event, you eventually end up with a Rube Goldberg machine. I found it akin to the singleton pattern. The Singleton is a good concept, when implemented correctly. When it’s implemented poorly, you end up with a mess.
I found that listening for events to be much cleaner. For example, my payment system listened for the “attempted_purchase” event, which fired a “completed_purchase” when the payment completed. The app then listened for a “completed_purchase” event to give credits to the user. In a finished app, I’d probably have a receipt service listening to the “completed_purchase” event to send a receipt.
I still had events firing events, but it wasn’t too bad.
Recursion / Infinite Loops
Recursion and infinite loops in event based architecture can really make you realize that the “Halting Problem” is real and you can kill your app with just one of these running away. Unlike traditional programming where the stack will eventually overflow, there’s nothing stopping that from occurring in event land.
In fact, events that call back to themselves are the most dangerous things you can do here. Just stay away, and you’ll be ok.
This is something I still haven’t figured out. Who owns the event? Who listens to what event? This is a real thing that I don’t know the correct answer to.
I decided in my framework that multiple aggregate objects can share the same instance. This would be like having two completely different classes in C++ sharing the same memory space (or two structs in C, maybe even a C union type). I decided on this because it offered the most flexibility.