Dyego Maas - Blog

Generative AI Consultant and Software Architect

Screaming Architecture

Screaming Architecture

Ever heard of Screaming Architecture? In this article we explore what makes an application's architecture scream, and how that can benefit a software project.

9 min read

One of my favorite chapters of Robert C. Martin’s (Uncle Bob’s) book Clean Architecture is the one called Screaming Architecture.

The concept is very simple, but it certainly makes us think about how we structure our applications, and how the structure we choose gives an application its face.

The theme of the application should be evident in its structure.

At first glance this may sound obvious, but let’s look at a few examples to understand the nuances of the problem.

Mute Architecture

Uncle Bob didn’t name this one, but the name helps illustrate the problem.

When we come across a directory and file structure like the one below, what can we infer about the application?

Mute Architecture structure
|-Entities
|---CarrinhoCompras
|---Clientes
|---Enderecos
|---Pedidos
|---Produtos
|-Repositories
|---CarrinhoComprasRepository
|---ClientesRepository
|---EnderecosRepository
|---PedidosRepository
|---ProdutosRepository
|-Services
|---CarrinhoComprasService
|---ClientesService
|---EnderecosService
|---PedidosService
|---ProdutosService
|-Controllers
|---ClientesController
|---PedidosController
|---ProdutosController

We can deduce that the application deals with some kind of order (pedido), which includes certain products (produtos) and is placed by certain customers (clientes), who even have an address (endereço). But what does this application actually do? Does it receive ready-made orders from another application? Does it actually record the orders? Or does it just generate reports?

To answer these questions we need to dig into the source, look at the controllers and services to start getting an idea of what it’s about. And depending on the application, even that can be hard.

The application is, in a way, mute. It doesn’t say much about itself or what it does, because it values the wrong things.

Mass graves (or, little maintenance hells)

Maybe you’ve run into a service like ClientesService, with twenty-something public methods. Several of them are even pretty similar.

Then you navigate to ClientesRepository and that’s when you get a fright: of the thirty methods you find there, about twelve are overloads with slightly different arguments. Some of these methods even try to share bits of queries, filters, orderings.

A repository method originally designed for a very specific situation is now being used for another situation that has nothing to do with it. Almost by accident, everything is coupled. The whole thing is a stew that’s hard to swallow.

This kind of code organization is bad because it tends to emphasize the kinds of things we find in the application. But why would it be useful to emphasize the kinds of things in the application?

Why create a service that will end up as a mass grave? A plot of land where any rubble that looks related to that theme gets dumped?

There’s a better way.

Screaming Architecture

In the book Clean Architecture, Uncle Bob illustrates what a screaming architecture is using architectural floor plans of houses and apartments:

Illustrated floor plan of a house

The floor plan above needs no legend. We easily understand what the rooms are, and we get a good sense of the proportions and the spaces.

The architecture is screaming “BEDROOM!”, “LIVING ROOM!”, “KITCHEN!”, “POOL!”.

This other one, even without the colors and illustrations, still manages to make all of that clear:

Floor plan of a house, technical version

So how can we make our applications scream?

Making the application scream

In the book Object Oriented Software Engineering: A Use Case Driven Approach, I. Jacobson reflects on which elements should be emphasized. He calls them Critical Business Rules.

Business rules are the fuel of a product: they define how users interact with the system, and how the system interacts with other systems, all to generate value for the organization.

So it’s the business rules that should be emphasized, rather than almost arbitrary structural elements like controllers, services and repositories.

One of the most useful tools for outlining business rules is the UML use case diagram, like this one:

Use case diagram of the store, an alternative to the example at the start

These diagrams are very explicit in outlining how users interact with a system. Those interactions are deeply tied to the business rules that generate value for the user and for the organization, and they’re what should be put front and center.

Many architectures, like Onion, Hexagonal, DDD, Clean and others, all try to emphasize use cases in one way or another.

Highlighting the Use Cases

If we do a quick exercise of restructuring the example application from the start of the article, this time focusing on highlighting the use cases, the picture changes quite a bit:

Screaming Architecture structure
|-Entidades
|---Clientes
|---CarrinhoCompras
|---Enderecos
|---Pedidos
|---Produtos
|
|-CarrinhoCompras
|---AdicionarProduto
|------...
|---RemoverProduto
|------...
|-Clientes
|---RealizarCadastroNaLoja
|------...
|-Pedidos
|---RealizarPedido
|------...
|-Produtos
|---PesquisarProdutos
|------...
|
|-Controllers
|---ClientesController
|---PedidosController
|---ProdutosController

Now, just by glancing at the directory structure, we can identify the features the application implements: signing up customers to the store, searching for products, the shopping cart and placing orders.

This approach demands a certain amount of change from the way many of us are used to organizing our applications’ code. And this organization brings several benefits with it.

No more code shared by accident

One of the first differences we notice is the absence of ClientesService and ClientesRepository, which used to be mass graves for dumping any and all code related to a customer.

In their place, each use case directory holds code that is extremely specialized in serving that specific use case, and no other.

Reusing business logic code isn’t just discouraged; to some extent, it’s forbidden.

But isn’t reusing code a good thing? Sometimes it isn’t.

Code reuse is good when that reuse won’t cause problems down the road. One kind of problem that can happen when reuse is misplaced is breaking a business rule while updating another rule that isn’t necessarily related.

In this case, misplaced reuse creates misplaced coupling. The problem is that these mass graves don’t provide the clear boundaries (bounded contexts) needed to guide the development of new features without the danger of falling into these traps.

Tolerating a bit of duplication

Sometimes it can be tempting to share common code between two different use cases. Especially if some bits are the same, or very much alike. But in most cases this similarity is accidental.

To understand why it’s accidental, we can revisit one of the SOLID principles, the Single Responsibility Principle (or SRP), which says that any module should be responsible for only one part of a feature. In Uncle Bob’s words, “a class should have only one reason to change”.

In the book Clean Architecture, Robert C. Martin explains in more detail what having only one reason to change means. To get there, we need to ask ourselves:

What does it mean to have only one reason to change? What is a class’s or module’s reason to change?

According to Uncle Bob, the user is the reason for a class or module to change. It’s when a user’s need changes that certain business rules have to change along with it.

That’s not how most people understand it when we discuss SOLID. I myself had a quite different understanding of SRP, more about a simple, clear contract for the class, with few public functions, or preferably just one; a class that does one thing only, and does it well.

So, if we understand that the user is a class’s reason to exist and to change, and that a class’s single responsibility is to perform a function serving a specific user, then the scope for reusing that class naturally shrinks.

Following that line of reasoning, whenever a class serves two distinct use cases (and possibly two users), it can fall victim to a conflict of interest, and so it may be violating the single responsibility principle (SRP).

Use case diagram with two hours-worked reports, one for Finance and another for the DHO (HR)

For example, suppose a system has two reports of hours worked, one for the Human and Organizational Development department (DHO, roughly HR), and another for Finance. The two reports are quite similar, so they share certain calculations. If one report’s requirements change because of the Finance department’s interests and projects, and that change touches the shared code, there’s a good chance it will unintentionally affect the DHO report.

This reinforces that generic use cases are discouraged when that sharing serves different users, and consequently different interests. One example is the “Generate hours report” use case.

We can also look at two of the Component Cohesion Principles:

  • CCP: Common Closure Principle
  • CRP: Common Reuse Principle

According to the Common Closure Principle, a component should not have more than one reason to change.

And according to the Common Reuse Principle, classes that tend to change together, at the same time and for the same reasons, should stay together. That’s one way of deciding how to group the source files.

Well then, if a component shouldn’t have more than one reason to change, and the reason to change is the user and their needs, what better way to organize the application than by the use cases themselves, which already lay out the system according to its users and their interactions?

Plus: System Design

The concept of Screaming Architecture doesn’t apply only at the application level, but also at the system design level.

You can find some examples of this in the The System Design Primer repository by GitHub user donnemartin.

The idea here is that a system’s architecture should look like what it sets out to do.

For example, the high-level architecture of a search engine for a key-value store should look like this:

Diagram of a search engine for a key-value store

While the data structure of a social network could look like this:

Diagram of a data structure design for a social network

If you liked the post, or would like to contribute, leave a comment!