Web Engineering 109: Event Driven Design
If a thing happens but no-one is told about it, did it really happen?
Events have quickly become a cornerstone of modern web development. A decade ago when the web was mostly using a basic MVC model (Model/View/Controller) monoliths rain supreme. Trying to communicate from one module to another was a pain and often involved tight coupling of classes and overall lead to fragile systems that were filled with bugs. As web development matured we adopted patterns from more traditional fields, one of them was Event Driven Programming/Design.
What is it?
Event Driven Design is the concept of encapsulating state changes as an object. Some examples; UserWasCreated, UserWasDeleted, UserPasswordWasUpdated… etc. Each of these events describe a state change, in this case limited to the concept of a User. More broadly Events can describe data in a raw from as well. Perhaps you need to parse a CSV file; you can represent a row as an event RecordWasParsed. These objects hold state, and they are immutable (Cannot change).
But Why?
There’s quite a few reasons. Primarily Events are messages that other parts of your system might care about. For instance let us go back and take a look at UserWasCreated. We might want to do the following when this event happens:
Send a welcome email
Bill a credit card
Generate an API key
… etc
Remembering back to Single Responsibility Principle and Service Oriented Architecture these functions should be separate classes or systems. Then thinking about testing, we would need to create an integration test for this User Registration Flow. Events allow separate systems/services to be very loosely coupled.
How does an event make it to all of these systems?
The answer to this is well, the Event Bus. The event bus is a class that is responsible for the delivery of events. Classes that care about events register with the Event Bus, and then receive an event instance when an event in the system is generated. Here’s what a sample of an API could look like;
interface EventBus {
public function dispatch(Event $event): void;
public function subscribe(string $eventId, callable $subscriber);
}//Someplace else …
$bus->subscribe(‘eventA’, function (Event $event) {
// do something with the event
});
The unit test for the Event Bus itself is rather trivial. The key is to write good integration tests for whatever components use the Event Bus. The overall testing strategy is to assert that a system is producing an event, that it is handed off to the event bus and that the receiving system is receiving that event.