This is a review for the Clean Architecture book written by Uncle Bob (for the first three parts). I read it on my holidays, and I must confess is not what I can name a 'holidays book'. This, because it is a very serious book about programming written by someone with more than 50 years in this field - that 50 years of programming history are reflected everywhere in this book. You need some quiet place to reflect about it, and a net connection to browse additional material.
First, I will make a description of the book parts with the essential aspects discussed. In the end, I will express my feelings and impressions about the content.
PART 1 - Introduction
Design and architecture are the same - architecture is sometimes considered to be the structuring at the high level of the system and design to be the structuring of the low level of the system. Both are architecture!
The goal of architecture is to 'minimise the human resources required to build and maintain the required system'. The author gives an example of a wrong application, where for release 8, the code was 40 times more expensive to produce than in release 1.
The software provides two values to the stakeholders: behaviour and structure. The developers have the responsibility to keep both these values high. The software has to be 'soft' = easy to be changed and maintained. That's why the structure = architecture is essential.
PART 2 - Programming Paradigms
This part of the book is dedicated to the programming paradigms. There are three paradigms that are discovered between 1958 and 1968, and no paradigms were discovered in the following decades. This is a sign that these are all we can have:
Structured programming = programming based just on these structures of control: sequence, selection and iteration. 'Go To statement considered Harmful' article of Dijkstra imposed the 'discipline on direct transfer of control' - jumps from one line of code to others are evil and are not needed.
Object Oriented programming - in ALGOL the class/object concept was born = the function call stack frame can be moved in Heap, the function can become the class constructor, and the inner functions can be the class methods.
The essence of OO consists not in the ability to model the world with objects - 'everything is an object' - but by the benefits of OO: encapsulation, inheritance and polymorphism. While encapsulation, inheritance and polymorphism can be achieved in non-POO languages, in POO these are more simple or even trivial to be expressed.
And especially the polymorphism is giving us dependency inversion = absolute control of all source code dependencies in the system → independent deployability → independent develop-ability. OO polymorphism imposes 'discipline on indirect transfer of control'.
Functional programming - variables in a functional language do not vary = are immutable. This is a big advantage because all the concurrent problems are because of mutable variables.
One idea is to use a technique named 'event sourcing' = to evict the update/delete operations (CR from CRUD) and to keep a ledger of events like in accounting - anytime we can recalculate the state from events. Functional programming is about 'discipline imposed upon variable assignment.'
As a conclusion, the software these days is composed of sequence, selection, iteration and indirection. The hardware changed drastically in time, but the software remains the same as in the first days of programming.
Part 3 - Design Principles
Here is a description of the SOLID principles seen from the architecture point of view. SOLID is about how to arrange our functions and data into classes and how classes should be interconnected like 'bricks in the walls'. The next part will talk about how to structure components in the systems = 'rooms in the buildings'.
These principles are used to create structures 'bricks in the walls' that tolerate change and make the code easy to understand = the global scope of design/architecture.
The principles:
- Single Responsibility Principle
A module should have one, and only one reason to change. More precise, to only one actor: user or stakeholder. The classical solution for this problem is to split into more functions/classes.
- Open/Closed Principle
A software artefact should be open for extension but closed for modifications. An example is shown about how we can protect higher-level components from changes in lower-level components.
- Liskov Substitution Principle
Barbara Liskov (1988) If for each object o1 of type S there is an object o2 of type T such for all programs P defined in terms of T, the behaviour of P is unchanged when o1 is substituted for o2 then S is a subtype of T. The author is using the classical rectangle-square problem to reflect the principle.
- Interface Segregation Principle
Depending on something that carries baggage that you don't need can cause you troubles. So split the interfaces in pieces and in this way you will evict inutile recompilations and redeployments.
- Dependency Inversion Principle
Interfaces are less volatile than implementations. So don't refer to volatile concrete classes, but interfaces. The author shows an Abstract Factory pattern implementation for illustrating the dependency inversion.
My ideas(maybe not so well articulated, so you can skip this part)
Three in one coffee
I think the tendency in modern languages is to have all three programming paradigms inside - structural, OO and functional. For example, Java has structural control blocks similar to the C language. It is OO by design. And in these days it has functional support starting with Java 8 lambda functions and streams (functional also with Groovy support). Javascript (another language that I am interested in) is structural at ES5 level, and become OO at ES6, and can be used in a functional style with libraries like Underscore.JS and Lodash JS.
First time I saw a computer and I wrote a program
I miss 30 years of experience from my uncle Bob's history - that's why we need an uncle to tell us stories about the past!
I started to use Java in the 2000s, and it was my primary programming language till now. My feeling is that we had two waves of POO. First was C++ and Objective C, where we still can see pointers and references to the machine. The second wave was Java and C# with Virtual Machines (JVM and CLR), Garbage Collectors, and no pointers. These were high-level programming language where we think just in objects - without any reference to the machine in our code (Threads, Sockets, etc. are OS-specific and are modelled as objects). Will fill the machine maybe, just when we start the JVM, and we configure the memory, GC type.
But the first language I learned in school was BASIC in the '90s, and it has GOTO, and there I was giving numbers to the lines, and I remember I used to jump from one line to another - imperative programming we named it. The second main language in school was PASCAL - structured programming (procedural).
In that time the Z80 (Zilog) processors were used at home with 64K memory - x86 (Intel/AMD) was already seen as the future but were too expensive at that time to have one at home - still remember first 486, and 586 (Pentium) we have at school. I still remember those games on tapes we used to play all day. I do some assembly language subroutines also on Z80 inspired by the games.
Typed/dynamic languages debate
I think a subject of the programming history is also the typed/dynamic typed dispute in languages. See the languages from the last time (Scala, Groovy). Static compile checked languages like Java, TypeScript versus runtime dynamic languages like Groovy (also running on JVM) or JavaScript. The tendency of dynamic languages to be scripting oriented - a lot of semantic in a concise syntax.
Functional means just immutability?
Not sure what is the essence of functional programming (if immutability) - I think that the possibility to apply a function to a data structure, to make chain of functions in a declarative way, to have the function as a citizen of the language passing it as a parameter - to do all this map/reduce stuff that is hard to be done in structured programming.
Immutability is not the single way to simplify the concurrency problems (locks, pools of threads). Event loops/single-threaded per core programming like Node.JS/Vertex can help as to escape from classical concurrency problems (works hand in hand with non-blocking but is imposing you to have all your system modelled by small non-blocking events - or use workers for long time jobs).
Top comments (0)