← Back

About events and component orchestration

Breaking architecture into components and using events to orchestrate high-level functionality helps build large, maintainable, testable, and scalable software systems.

Software Architecture

Those who have read my latest book, “El Arte del Emprendedor Digital”, will have seen why it is a good strategy to break down the architecture of a software project into components, especially if the project is medium or large and is going to evolve a lot. In this way, and without going into too much detail, the system’s functionality is organized into independent responsibilities that are easy to locate and modify.

The key is that each component (call it a module if you want) is small, handles something very concrete and specific, and that when a broader functionality must be addressed, it can be distributed across several components that talk to each other. How? Depending on the technology and the work framework used to implement this scheme.

Hub de Libros currently has more than eighty components, while a simple project I have worked on recently already has around thirty (many of them reused from other projects).

It may seem like things get a bit messy when we talk about dozens of independent modules, although perhaps only those who, like me, have suffered from a monolithic solution with a lot of functionality (hundreds of thousands of lines of code) can appreciate the benefits of this architecture. In another different project I have led at Solid Stack, it already has more than a hundred components and is growing.

If you are facing a project with hundreds of KLOC (1 KLOC = one thousand lines of code) and there is no trace of components or independent modules, that is a bad sign.

However, in any large system there is always high-level functionality that affects or uses a set of lower-level functionalities. We are always tempted to implement it directly and separately, and even if it is encapsulated in its own component, in the end what we have is again a large, monolithic component, harder to change, test, and evolve—exactly what we want to avoid.

There is another approach to implementing that higher-level functionality based on events: that is, when something happens in the system, a component does something specific and simple, simply emitting the corresponding event in case another part of the system needs to know that the event has occurred. Managing that event does not even have to happen in the same application; there can be real software infrastructures for managing events across independent applications. Redis, for example, allows a publish/subscribe model that facilitates this communication between processes in cases where hundreds or thousands of events per second need to be handled.

For example, if when a user logs in to the system we need to save certain information to the activity log, launch a report for their custom dashboard, or do anything else, none of that should be implemented in the same component that handles user security—that component simply emits an event like “users.userloggedin” (it is just a descriptive name), and something somewhere else in the system will take care of doing what needs to be done when that happens.

In this way, there are base components that implement very canonical functionalities, but there are also, as part of a well-structured architecture in responsibilities, other components tasked with implementing higher-level functionality that affects groups of components. How? By managing events and orchestrating the functionality of several components of the first type.

It is rare to find high-level functionality that cannot be implemented this way. Sometimes all that kind of functionality is included in what is called the integration layer, and it is even implemented under the concept of workflow or business process.

Managing those events does not have to be synchronous at all (possibly delaying the user interaction with the system); it can be done in the background.

With this strategy, everything remains encapsulated in simple components, while those responsible for integrating more complex functionality simply orchestrate the first ones, keeping the implementation simple: when event X occurs, I do this and then that.

Without a doubt, event management and orchestration are fundamental tools for implementing large software systems.