In software development, event-driven architecture (EDA) is becoming more and more popular due to its ability to facilitate real-time communication, scalability and loosely coupled systems. At its core — and as the name indicates — communication is done through events. One of the key debates among those who use event-driven architecture is what those events should be like: should we use thick events or thin ones?
There’s no obvious answer; both have benefits in particular contexts. However, in my experience thin events are usually better for making software work smoothly and flexibly. In this blog post I’ll explain why.
Thin vs. thick events: Choosing the right messenger
To better evaluate the differences and value of using either thick or thin events in an event-driven architecture, let’s consider how it works in an example scenario. So, let’s consider it in terms of user registration.
Thick event: In user registration, a thick event would be detailed and comprehensive in terms of the data that is sent in a given message. It would likely include everything from name and address to preferences and profile picture. The data is rich — but do you need all of it right away?
Thin event: In the same scenario this would just be something like a simple notification; maybe only the user ID. It conveys the core message efficiently — if more detail is needed, that can be grabbed later.
Another way to think about thin events — sometimes called "event notifications" — is to view them as being like telegrams. They deliver the essential message with minimal data. Downstream systems can then retrieve specific details as needed.
Thick events, meanwhile — sometimes called "fat events" or "event carried state transfer" — are more like detailed reports. They carry a lot of data upfront, potentially including everything you might need. Think of it as a comprehensive report on the user's registration.
Beyond delivery: Events harmonizing systems
In event-driven architecture, events do more than pass messages; they initiate actions across the system. By enabling asynchronous communication, they allow for independent operations and dynamic responses with loose coupling. Loose coupling essentially means that different parts of the system can both operate and be changed independently without relying on each other's inner workings. s Asynchronous communication allows different parts of a system to interact with one another without waiting for an immediate response. The benefit is that it enables independent operations and dynamic responses, allowing systems to respond to events as they happen without being held up by other processes.
Their main value is in showing when actions occur and helping a system remain flexible, scalable and strong. They let other parts of the system know when something changes, which then starts other processes. Imagine an "item bought" event; that will set off inventory updates, order confirmations and shipping notifications. In EDA, these actions work together in harmony.
Thin events: A flexible core for EDA
In EDA, thin events are useful because of their simplicity, modularity and adaptability.
Let's explore why:
Clear boundaries: Imagine thin events as the stagehands backstage, ensuring smooth transitions during a performance. They announce actions — such as "user commented" — but leave the data gathering to the experts: APIs. By establishing clear boundaries between tasks, thin events promote modularity. This means systems can grow independently without their overall performance being negatively impacted. If a notification system receives a "user commented" event, that’s all it needs to initiate the notification process; it doesn’t need, say,the user's name or profile picture. With clear boundaries and communication, the system is able to operate harmoniously.
Agnostic yet adaptable: Thin events are versatile; they can adapt to different needs. This allows for smooth scaling even as the number of consumers increases or the purpose of event consumption evolves over time. For instance, a "user updated profile" event doesn't care if a new system needs that data for marketing – it simply transmits the core message. This adaptability ensures the event remains relevant and efficient regardless of the ever-changing consumer landscape.
The balancing act: Thick events often struggle to strike a balance between data granularity and efficiency. Deciding what to include is a constant act of weighing information against payload size. Thin events, on the other hand, avoid this issue because they prioritize streamlined communication. This means they can steer clear of bloated payloads and complex and ever-evolving schemas.
Thick events: Use strategically for specific problems
Despite the benefits of thin events in EDA,thick events can still be valuable in specific scenarios — particularly where including data upfront is important.
Here are a few instances where thick events may be useful:
Integration events: Imagine you are trying to integrate a payment system into your platform. Shared context might be limited, thus making seamless communication crucial. Here, a well-crafted thick event which contains both order details and payment information can bridge the gap and ensure data is exchanged smoothly.
Point-in-time state: In dynamic domains with frequent data changes, consumers often need the exact data at a specific moment, such as if you are trying to capture payment information or notification content. By including all essential data in the event, you can better ensure accurate representation later. For example, a "payment processed" event might need to be thick if it has to include the transaction amount, balance and currency for accurate processing.
A caveat: While not ideal for large-scale applications, thick events can be considered in smaller solutions where event consumers are positioned close to event producers. In such cases, the producer should be well aware of the consumers’ data needs and have confidence that exposing too much data won't create security risks. However, it's crucial to revisit these assumptions regularly as the solution evolves.
DDD and thick events: Walking the tightrope
In EDA, a dilemma arises when it comes to deciding what data to include in thick events: should we add data for the whole aggregate or should it be for an entity within that aggregate? This is what is sometimes referred to as the entity vs. aggregate data inclusion dilemma. In domain-driven design (DDD), entities and aggregates are two fundamental and related concepts. Entities are individual objects with unique identities and well-defined behaviors; aggregates are consistent groupings of entities. Aggregates typically represent a complete unit of business behavior.
Embedding entity-level data involves including granular details specific to a single entity within the aggregate. While this approach provides detailed information, it can lead to increased numbers of events, particularly if multiple entities are referenced within the aggregate. This complexity makes managing schema changes difficult and may potentially violate DDD principles of encapsulation.
On the other hand, embedding aggregate-level data entails including data representing the entire aggregate's state or relevant entities within it. While this approach prevents bloating the number of events, it risks overexposing details that not all consumers require, potentially leading to tight coupling between systems. For example, if a consuming system receives aggregate-level data that includes more details than it actually needs, it might start relying on those extra details. This creates a dependency where the consuming system becomes tightly coupled to the producing system's implementation details.
Moreover, there's also potential concern around data privacy; data could be exposed to consumers who shouldn't have access to it. However, APIs should be well-equipped to handle such scenarios, providing a layer of control and security over data access and ensuring compliance with privacy regulations.
Making informed choices
When it comes to choosing whether to use thick or thin events in event-driven architecture, there are a number of things worth considering.
Favor minimal data: Start with the absolute minimum information required to trigger downstream actions. Don't overload the event with unnecessary details.
Leverage APIs for detailed data access: Allow consumers to retrieve specific data they need through well-defined APIs, adhering to the separation of concerns principle.
Evaluate thick events carefully, outlining specific needs and weighing complexity against benefits. Document the reasoning behind your choice for future reference.
Contextualize data inclusion: If embedding data is unavoidable, carefully consider the specific use.
Conclusion
While thick events have their place, the simplicity and agility of thin events make them the preferred option in most EDA designs. Thin events, with their focus on simplicity, modularity and clearly defined responsibilities, enable systems to grow smoothly and adjust to evolving needs without compromising on strength or efficiency.
However, it’s still important to remember that there's no one-size-fits-all answer. Evaluate your specific context, data needs and communication requirements to choose wisely. Try out, gain knowledge and embrace the potential of EDA to construct robust and flexible systems.
Disclaimer: The statements and opinions expressed in this article are those of the author(s) and do not necessarily reflect the positions of Thoughtworks.