Universal Architecture

In this post, I’d like to introduce you to the Universal Architecture idea described by J.B. Rainsberger in the podcast Unit Testability and the Universal Architecture.

Everyone wants to deliver software that works and delivers value to our customers. That’s a fact! Everyone wants our software to be easy to change and maintain. That’s obvious! Unfortunately, we all know this is very hard to achieve. We are all aware of the pain and costs of impenetrable code. Over the past few years, after spending my time inside very difficult code bases, I’ve kept asking myself the following question:

How can we prevent code becoming an intricate mess over time?


… or, framed more positively …

How can we write working software that is easy to change and maintain over a long period of time?


Unfortunately, I don’t have a definitive answer to this question. The Agile development process was born as an answer and radically changed our industry. However, we’re still bad at writing working and maintainable software. We still deliver software that is full of bugs. Developers still write software that is to hard to understand and maintain.

There are many ideas and techniques that can significantly reduce the risk of ending up in a mess, most of which come from Extreme Programming and, more recently, the Software Craftsmanship community.

The Universal Architecture call to action is simple yet extremely powerful:

Maximize the amount of code you can fully run in memory,
make it simple and unit test it aggressively

If you do it, you will end up in a three zones architecture:

The Happy Zone
This zone is where the vast majority of your code should live. It includes your domain and application logic. Everything is good here. The code is simple, it does not depend on external frameworks and libraries. It runs super fast and fully in memory.

Unit tests run quickly and provide 100% code coverage. They can provide an immediate feedback loop and safety net during development. They help to find mistakes sooner rather then later.

Developers can be extremely productive in this zone.

In this zone, you define clean and narrow interfaces to access services from the external world. The interfaces are closer to your business. They are designed the way you want them and you can change them. You never mock external libraries directly, but you create mocks of these interfaces to use in the unit tests (guideline: Don’t mock types you don’t own).

You have total design freedom to make this code the simplest and cleanest possible code that works.

The Demilitarized Zone

The Demilitarized Zone contains all the code that integrates with the outside world. It offers services to the Happy Zone via well designed interfaces.

The code can run slowly based on how it interacts with the external world.

Here is the only place where you should write integration tests. Watch Integration Tests are a Scam.

These tests have nothing to do with your application! Effectively, they are characterization tests (also called learning tests). You create these tests to describe the behavior of third party libraries you rely on. They do not represent any of your application use cases. They exist to document the behavior of third party libraries. They provide a way to validate your assumptions on how the external world works and more importantly they help you to catch regressions when the external libraries get updated.

These tests don’t need to run all the time in your build server but you definitely want to run them when you upgrade a library or you need to start using it in a new way.

The code in the Demilitarized Zone can potentially be moved into libraries shared across products.

The Outside World

Here is where all the external frameworks and libraries live. It includes the file system, the registry, the database, the network and so on.

The rules of the game

There are a few important rules to follow:

  • Arrows never go from the Happy Zone out The Happy Zone does not depend on the external world directly. If you feel you need to do it, this is a sign that there is a missing abstraction.
  • Code should flow from the Demilitarized Zone into the Happy Zone  The Demilitarized Zone should not attract logic. All the logic should flow into the Happy Zone. The Demilitarized Zone contains the minimum amount of code required to invoke the external libraries; everything else can run in memory and should live in the Happy Zone.
  • Follow the Four Rules of Simple Design (by Kent Beck) J.B. summarizes the rules in remove duplication + improve names. Following these two simple rules can significantly improve your design and make your code simpler and easier to understand.

Summary

The underlying idea of Universal Architecture is pretty powerful and offers a very clear and simple guideline for developers to follow. If more code can run in memory, more code can be easily tested. The feedback loop is short and developers can write high quality code productively. If the majority of the code is in the Happy Zone, we have a greater ability to change and maintain the software in the long term.

This post was originally published on Andrea Angella’s Software is My Passion blog, where you can read more of his thoughts on productivity, teamwork, craftsmanship, C#, and .NET.