What Exactly Is Software Architecture?

That thing many talk about but only a few actually do

Caio Andrade
The Startup
6 min readJul 10, 2020

--

From time to time someone mentions this term. In the most diverse contexts. It’s a term that has been used to express many different things, and when a word can mean anything, it ends up meaning nothing. In this article I explain my own vision, after a lot of study, about what software architecture actually is, in the most precise way I could until now.

A library’s architecture doesn’t define how the books are going to be organized

Also available in Portuguese

As software crafters, we write code to the best of our ability, according to the knowledge we currently have about programming and also about the application’s business domain.

As time goes by, we learn more and more, not only about programming techniques but also about the peculiarities and characteristics of the business domain.

Trends change, new programming techniques are created and some of them get more traction in the market. The business domain also evolves and changes as more features are added to the software.

So, the code we have written seems to slowly decay, because we wrote it without the new knowledge we now have. Every time we look at the older code, more certain we become that it doesn’t reflect anymore the best modeling for the problem it should solve.

Code decaying over time

This is normal. It happens since the beginning of a project and it will keep happening until the end. Software is soft because it was made to be changed. Refactored. Experimented. Adjusted. Corrected. Incremented.

But changing this code isn’t something simple, because systems are, most of the time, complex. The impact of a change isn’t always clear. We get struck by that fear of breaking something. And that’s why we write automated testes. To be able to fearlessly change software. To be able to rewrite, adjust, experiment, fix.

Automated tests to measure the impact of change

It’s not only to know if the software works. If we would never change that system, a well-done manual test would be pretty good at making sure a version works. Perhaps it would even be better then automated tests, because manual tests are forcefully end-to-end and also catch unforeseen issues in UX and business domain.

It turns out that writing automated tests isn’t enough to make our software easy to change. If the code gets too coupled to implementation details like UI, database and communication with other systems, any change in code is subject to the intrinsic modeling that these external factors impose on our code.

Any change in the way business rules are modeled imposes changes in multiple automated tests and in the implementation details. To improve the name of a variable inside a business rule is to impose that it changes also in the database, or the screen, or in other places. Bigger changes, structural ones, chill down our spine.

When one change causes others

That’s why architectures were created. Architecture isn’t a recipe to follow when modeling a system. It isn’t a nomenclature. It isn’t a way to organize business rules into classes or methods. Architecture isn’t DDD[1].

Architectures are ways to isolate our code from external factors, so we have the freedom to model and remodel the solution to a problem in the way we understand to be the best at the moment. And then remodel it again. And again.

One of the most famous architectures today is the Clean Architecture:

In the article, Uncle Bob seems to provide really the recipe, the nomenclature and the way to organize business rules into classes and methods. But if you read carefully, you’ll notice that the objective — of all architectures — is to achieve the so called independence, the so called freedom.

Freedom to learn and remodel the system without breaking everything. Freedom to clean the code.

That’s why there are Usecases (Interactors). They represent the actions users can do in the system. They are the communication bridge between the UI and the application. And that’s also why there are Presenters, which make the way back from the application to the UI when there’s some extra processing involved to present that information. They are part of the shell, they can be named differently and have other forms, as long as they fulfill their role of isolating.

From there inwards, rigid rules shouldn’t be imposed. That’s the point. Freedom from the imposed rules. All the tests we have written, all the Usecases and Presenters, exist so we have greater freedom to define how our Entities are and how they’re going to behave.

Once the library’s architecture is ready, we can organize the books as we please

So we ourselves can define if they should be functions, classes, how many method they ought to have. If they will be instantiated in the Gateways (Repositories), if they’ll receive data in the constructor or in the methods. And any other way of modeling.

Each domain has its peculiarities. The rules don’t fit, principles do. Each principle is going to be applied in a given way in each domain. It all depends. But to advance at this point we need to get used to thinking again. Maybe go back and do a few katas or dojos.

Forget the rules, forget the patterns… What’s the simplest way to solve a problem?

Forget the screen, forget the database… What’s the simplest algorithm to solve that college challenge given by the professor?

Forget the Usecase, forget the architecture… Write a test, see it failing, write the minimum code possible to make it pass, and then refactor.

Forget the class, forget the interface… What is more readable? What is simpler to understand to the rookie that just started at the company?

Forget the pattern, forget the inheritance… Is the package where I put this code coherent? Is it easy to anyone using this rule to find it?

Of course we’ll be making use of the UI, the database, of Usecases and Patterns, of classes, interfaces and inheritance. But those things are tools made to help us model the simplest code possible to solve the problem.

Each part of the domain has a different problem. Even if they all look like the same CRUD. One part of the system is going to need an Entity with data in the constructor, another part is going to need an Entity that is born inside a Gateway. Another part is going to need a Gateway with lots of rules in it. Another is going to have a Usecase that’s only used by other Usecases, and another is going to have them all inline.

But the question is more important: is this code the simplest possible for now? So be it, because in a little while we’ll learn more and change this code to something better.

[1] The book Domain Driven Design addresses some concepts that help to isolate an application from external factors, but the objective is to model the business rules in a consistent way to the business domain — which is very important indeed, and a topic for another article — and doesn’t focus so much on the independence and freedom.

--

--