Codementor Events

Application Architecture and the Design Fractal

Published Aug 29, 2019

So, when we talk about “architecture” in the IT world, we can be talking about any of a number of things, it pays to be clear about which one you’re discussing. For instance, if I go into an Enterprise Architecture discussion talking about CQRS with Event Sourcing there’s a good chance I’m wasting everyone’s time.

CQRS with Event Sourcing is an application architecture – it informs how I build out a specific business application/service. Certainly my Enterprise can adopt CQRS, but if it does so, it does so as an across-the-board Application Architecture.

When we talk about Application Architecture- whatever type we use – it is wise to keep in mind the design fractal. Let’s take CQRS with Event Sourcing (something I’m currently using, so I can talk about it readily). The Design Fractal says I care about my SOLID principles as low as the method level and as high as the Enterprise level. But how does that apply to CQRS?

First, a quick primer on CQRS with Event Sourcing, because not everyone knows about these.

CQRS is “Command Query Responsibility Segregation.” It means that I use two different channels when accessing my domain objects (yes, it is most often used in the paradigm of Domain Driven Design). One of those is a command channel which handles my CRUD operations and business logic. The other is a query channel which supplies read-only copies of the domain objects, mostly for use outside of the domain.

Event Sourcing is a separate, but highly compatible idea, that my domain objects are actually just the sum of the events which went into creating their current state. So rather than necessarily storing my Shopping Cart as such, I store Cart Events with some aggregation ID. Then, when I’m ready to do something to the shopping cart – say, turn it into an actual order and pay for it – I process the events to determine the end-state of the cart object.

There are myriad sources on these topics, both separately and as a unit, and I highly recommend you do some more research on them. They are a quite powerful combination.

So, with that out of the way, how can we apply SOLID to CQRS with Event Sourcing? Let’s take a look.

S – Single Responsibility
This comes kind of baked into CQRS: the command channel has only the responsibility to process commands. The event processor (which lives inside the command channel, most likely) has only the responsibility to process events to evaluate object state. The query channel has only the responsibility to service read-only queries.

But it gets deeper – and more generic. Presumably I have multiple things that can happen to my object. Take a loan application, for example. I can update it with information, I can modify information, I can evaluate it at any point for completeness, I can submit it for underwriting, and so forth. Do each of these run through the same component? Certainly not. Instead, I can have several small components which are each responsible for handling a specific command or specific set of highly related commands. I can have components that compose other components. Really quickly I’m looking like an Actor system and/or microservices – and those both become options at high enough levels of complexity – but I don’t necessarily have to go quite that far.

O – Open / Closed
This also comes baked into the CQRS cake, somewhat. By providing read-only copies via the query channel, consumers can write whatever methods they need on those read-only copies – extending them as needed – while my domain object and logic remain completely within my control.

Other architectures might approach this a different way. Some will do it simply by code governance or code security – only code within the app domain can access certain members. Some might not address this directly in any meaningful fashion, which just makes it more imperative that we consider it in our design.

L – Liskov Substitution
This one is a little more tricky, but it goes back to the components idea I mentioned previously. If I have lots of components that handle specific commands, those should at least share some basic interface. The commands themselves should share an interface. Because of this, I can compose more quickly, and I can handle broad groups of commands, events, or components in multiple ways if I need to do so. Further, if I have a certain component for *most* of my objects, but I know certain specific objects will need to substitute their own behavior, I can have multiple components which implement the same interface (leading us to the D in SOLID), and still pass around those components as though they are the base implementation when necessary.

This is not particular to CQRS at all, but CQRS enables it quite easily once we add the idea of component-based processing.

I – Interface Segregation
Again, this one is not particular to CQRS. By using the component approach, I can have as many specific interfaces providing specific functionality as I need.

D – Dependency Inversion
As mentioned under Liskov Substitution, our component-based approach to the actual command/event processing allows us to handle most functionality via interface. This means a given coordinating component does not have to be directly aware of any specialized components so long as those components share an interface with more generic versions.

Now, CQRS with Event Sourcing itself is no guarantee of any of this, and may or may not be implemented in a way that comports with the Design Fractal. Indeed, when we talked about the LID of SOLID, none of those directly relate to CQRS – you can implement them or not and still be within the pattern. Contrariwise, the Design Fractal can inform any Application Architecture model or pattern. When we talk about application architectures, we need to keep the Design Fractal in mind. If we find that we’re deviating from those principles, then we may need to consider changes.

Discover and read more posts from Allen Goodner
get started
post commentsBe the first to share your opinion
Show more replies