This post is part of The Software Architecture Chronicles, a series of posts about Software Architecture. In them, I write about what I’ve learned on Software Architecture, how I think of it, and how I use that knowledge. The contents of this post might make more sense if you read the previous posts in this series.

In this post, I establish the very first concepts about Software Architecture, that will be needed in order to better understand the following posts.

No Silver Bullets!

No matter how you understand what I talk about in The Software Architecture Chronicles, above all please understand that there are no silver bullets, there is no “one boot fits all” solution. Learn as much as possible about different approaches, understand the pros and cons of each approach, what specific technical problem they solve.

Then, when taking on a new challenge, start by understanding the business and end-user requirements. Only after having those requirements clearly understood you can reason about what architectural styles and patterns should be used to better address the problem at hand.

Finally, make your own choices, maybe implement one of the known solutions, maybe create your own design that fits your particular problem.

Some architectural styles are often portrayed as ‘silver bullet’ solutions for all forms of software. However, a good designer should select a style that matches the needs of the particular problem being solved. Roy Fielding, 2000 [1]

Terminology

In the Software Development world there is plenty of ambiguity in the terminology used and, as such, before continuing, it is important to clarify the meaning I want to give to a few terms I use.

Functional

It’s any piece of code, method, class, group of classes, that has a purely technical role in the application. It is not related to the domain, it simply represents a technical capability in the application. Examples:

Layers

Factories

Repositories

Value Objects

Views

ViewModels

Conceptual

It’s any piece of code, method, class, group of classes, that reflects a domain concept in the application. It is directly related to the domain, it represents a business capability in the application. Examples:

User

Product

Stock Management

Product Variants

Checkout

Upsells

This separation doesn’t mean that a code unit can not be referred to in both ways (Functional and Conceptual). For example, a “Money” class can represent a domain concept and be designed as a Value Object. If I refer to it as a concept, I mean the domain Money concept, but if I am referring to the Functional aspect of the class, then I mean the technical characteristics of a ValueObject (no ID, maybe immutable, etc).

Package

Any set of classes grouped together, ideally following some set of rules

Module

I use the definition given in Software Architecture in Practice[7], which says that a module is a functional package, it reflects a technical capability in the application. It is decoupled and can be swapped by another implementation. For me, modules exist at a low granularity level, for example, a “Security Module” or the “ORM”, but also at a higher granularity level like Client and Server blocks of the application. Modules provide for Functional cohesion.

Component

I also use the definition given in Software Architecture in Practice[7], where the authors define a component as a conceptual package reflecting a business capability. It is also, ideally, decoupled from other components and modules. Examples can be “User”, “Product” or “Checkout”.

The most important thing to remember, though, is that, ideally, it reflects a bounded context. Components provide for Conceptual cohesion.

Application

I see an application as the user-facing code, the UI, which is built on top of the Components. For example, we can have a set of components on top of which we build a web shop. This web shop, however, has a UI which is used by the consumers browsing and buying products (storefront) and it has another independent UI which is used by the shop administrators to manage products, stocks, payment providers, etc (admin). Those are two independent applications built on top of the same business components.

System

I think of the system as a set of applications that somehow work together to provide functionality to a wide range of enterprise necessities, forming an enterprise-wide system, an Enterprise Application. These applications may or may not be built on top of the same Components. In the previous example of a web shop, the system is the web shop as a whole, including the two applications built on top of the same business components (the storefront and the admin) but also including other 3rd party applications like payment providers or shipment providers.

Architecture

There are many simple definitions of Software Architecture, and I think it is good to have them, but I think it’s easier to understand what it is, and even more important, defining the deliverables of the Architecture, what it should deliver to the project.

Software Architecture […] is the set of structures needed to reason about the system, which comprises software elements, relations among them, and properties of both. Clements et al, 2010 [6]

This is how I reason about Architecture:

All technical decisions that are cross-cutting to all feature development , ie. frameworks, coding standards, documentation, processes, …;

that are , ie. frameworks, coding standards, documentation, processes, …; It’s the set of technical decisions that are difficult to change later in the project [3] ;

that are in the project ; It’s the big picture view of the system [5]:pp.2 , the broad strokes, the structure , the components and their relations [4] [6] ;

, the broad strokes, the , the ; It prepares the project for change [5]:pp.30 , often simply by delaying decisions to the last acceptable moment [5]:pp.32 ;

, often simply by delaying decisions to the last acceptable moment ; It prepares the project for reuse of components and modules [7]:pp.29–35 ;

of components and modules ; It establishes standards for consistency of results and lightweight of processes , like coding standards, the development stages, continuous delivery and deployment;

, like coding standards, the development stages, continuous delivery and deployment; It’s not the responsibility of only one person, but of a guild of experienced developers that belong to different feature teams in the project.

If you are not familiar with the concept of guilds, take a look at the Spotify Engineering Culture videos:

Architect

He is the guardian and promoter of the Architecture, which is discussed and decided as a guild. He is one of the most experienced developers in the team/department, which happens to have the extra responsibility of analysing high-level problems and solutions. He also benefits from a “quality vote” when making an architectural decision.

It is important to note, though, that all developers end up being architects to some extent, as they all need to understand the Architecture, they all contribute to the architecture in some way, and in the end, they all have the responsibility of maintaining the Architecture in place.

Ivory Tower Architect

The all mighty Ivory Tower Architect is the anti-pattern of the Architect, one who takes upon him to decide over all issues related to the Architecture. He castrates the other stakeholder’s contributions to the architecture, by not being open nor easily available to receive those contributions.

Smells of a bad Architecture (and bad code) [8]

Rigidity

Software that is rigid is software that is difficult to change because one change will trigger the need for more and more changes. It becomes a rabbit hole: when we think we are almost finished we find more code that needs to be changed, dragging us into a loop with no end in sight.

Fragility

When changed, the fragile software will break in unexpected, unrelated and unpredictable places.

Immobility

A design is immobile when it contains parts that could be useful in other systems, but the effort and risk of separating them from the original system are too great.

Viscosity

In a viscous system, it’s much easier to do the wrong thing than to do the right thing. It implies that it is much easier to implement a change by making a hack than to develop it properly.

System-wide viscosity happens if running unit tests and/or compilation takes a lot of time, making it likely for a developer to bypass procedures and implement a hack without running all automated tests.

Needless repetition

This happens when necessary abstractions have not been made, either because of lack of time or lack of experience. The code may not have been literally copy-pasted but the same business rules are defined in more than one place.

Opacity

The code has been written in an opaque and confusing way, and we need to dive into the methods implementations to understand what they do.

Needless complexity

In a passionate attempt to avoid the other 6 smells, developers introduce all sorts of abstractions and preparations for potential future changes. Good software design is lightweight, flexible, easy to read and understand and above all easy to change so you don’t have to try to predict all potential changes in the future.

Sources

[1] 2000 – Roy Fielding – Architectural Styles and the Design of Network-based Software Architectures

[2] 2000 – Robert C. Martin – Design Principles and Design Patterns

[3] 2006 – Booch, in [5 pg.2]

[4] 2007 – IEEE1471 in [5 pg.2]

[5] 2010 – James Coplien, Gertrud Bjornvig – Lean Architecture

[6] 2010 – Paul Clements, Felix Bachmann, Len Bass – Documenting Software Architectures

[7] 2012 – Len Bass, Paul Clements, Rick Kazman – Software Architecture in Practice

[8] 2014 – M. H. Jongerius – THE SEVEN DESIGN SMELLS OF ROTTING SOFTWARE

[9] 2017* – Wikipedia – Software Architecture

*seen in

52.381613 4.871174