Author: Robert C. Martin (Uncle Bob)

Contents

1 PART 1. Introduction
1.1 Ch1. What is Design and Architecture?
1.2 Ch2. A Tale of Two Values
2 PART 2. Starting with the Bricks: Programming Paradigms
2.1 Ch3. Paradigm Overview
2.2 Ch4. Structured Programming
2.3 Ch5. Object-Oriented Programming
2.4 Ch6. Functional Programming
3 PART 3. Design Principles
3.1 Ch7. SRP: The Single Responsibility Principle
3.2 Ch8. OCP: The Open-Closed Principle
3.3 Ch9. LSP: The Liskov Substitution Princicple
3.4 Ch10. ISP: The Interface Segregation Principle
3.5 Ch11. DIP: The Dependency Inversion Principle
4 PART 4. Component Principles
4.1 Ch12. Components
4.2 Ch13. Component Cohension
4.3 Ch14. Component Coupling
5 PART 5. Architecture
5.1 Ch15. What Is Architecture?
5.2 Ch16. Independence
5.3 Ch17. Boundaries: Drawing Lines
5.4 Ch18. Boundary Anatomy
5.5 Ch19. Policy and Level
5.6 Ch20. Business Rules
5.7 Ch21. Screaming Architecture
5.8 Ch22. The Clean Architecture
5.9 Ch23. Presenters and Humble Objects
5.10 Ch24. Partial Boundaries
5.11 Ch25. Layers and Boundaries
5.12 Ch26. The Main Component
5.13 Ch27. Service: Great and Smal
5.14 Ch28. The Test Boundary
5.15 Ch29. Clean Embedded Architecture
6 PART 6. Details


1 PART 1. Introduction #


1.1 Ch1. What is Design and Architecture? #


  • The Goal?
The goal of software architecture is to minimize the human resources required to build and maintain the required system.

  • The Signature of a Mess

  • Conclusion:
In every case, the best option is for the development organization to recognize and avoid its own overconfidence and to start taking the quality of its software architecture seriously.

To take software architecture seriously, you need to know what good software architecture is. To build a system with a design and an architecture that minimize effort and maximize productivity, you need to know which attributes of system architecture lead to that end.

clean architectures and designs that software developers can build systems that will have long profitable lifetimes.

1.2 Ch2. A Tale of Two Values #


Every software system provides two different values to the stakeholders: behavior and structure

  • Behavior
make machines behave in a way that makes or saves money for the stakeholders

  • Architecture
Software must be soft. It must be easy to change. The more this architecture prefers one shape over another, the more likely new features will be harder and harder to fit into that structure.

  • The Greater Value
There are systems that are practically impossible to change, because the cost of change exceeds the benefit of change. Many systems reach that point in some of their features or configurations.

  • Eisenhower's Matrix
'Behavior' is Urgent + not always particularly important. 'Architecture' is Important + never particularly argent.

The mistake that business managers and developers often make is to elevate items in position 3 to position 1. In other words, they fail to separate those features that are urgent but not important from those features that truly are urgent and important. This failure then leads to ignoring the important architecture of the system in favor of the unimportant features of the system.

It is the responsibility of the software development team (not business managers) to assert the importance of architecure over the urgency of features.

  • Fight For the Architecture
Remember, as a software developer, you are a stakeholder. If architecture comes last, then the system will becom ever more costly to develop, and eventually change will become practically impossible f or part or all of the system. If that is allowed to happen, it means the software development team did not fight hard enough for what they knew was necessary.



2 PART 2. Starting with the Bricks: Programming Paradigms #


2.1 Ch3. Paradigm Overview #


The three paradigms: structured programming, oop, functional programming

  • Structured Programming
Structured programming imposes discipline on direct transfer of control.

  • Object-Oriented Programming
OOP imposes discipline on indirect transfer of control.

  • Functional Programming
Functional programming imposes discipline upon assignment.

  • Food for Thought

  • Conclusion
1) We use polymorphism as the mechanism to cross architectural boundaries. 2) We use functional programming to impose descipline on the location of and access to data. 3) We use structured programming as the algorithmic foundation of our modules. Notice how well those three align with the three big concers of architecture: function, separation of components, and data management.

2.2 Ch4. Structured Programming #


Edsger Wybe Dijkstra

  • Proof
  • A Harmful Proclamation
  • Functional Decomposition
  • No Formal Proofs
  • Science to the Rescue
  • Tests
  • Conclusion
At every level, from the smallest function to the largest component, software is like a science and, therefore, si diven by falsifiability. Software architects strive to defin modules, components, and services that are easily falsifiable(testable). To do so, they employ restrictive disciplines similar to structured programming, albeit at a much higher level.

2.3 Ch5. Object-Oriented Programming #


  • Encapsulation?
  • Inheritance?
  • Polymorphism?
  • The Power of Polymorphism
OO allows the plugin architecture to be used anywhere, for anything.

  • Dependency Inversion
Before a safe and convenient mechanism for polymorphism was avilable. In a calling tree, source code dependencies followed the flow of control.

[HL1] --> <I.f()> <== [ML1.f()]
--> : control
==> : inheritence
Module HL1 calls f() function in module ML1. It calls this function through an interface I.f(). Note that the source code dependency between ML1 and the interface I points in the opposite direction compared to the flow of control. This is called 'dependency inversion' The fact that OO languages provide safe and convenient polymorphism means that any source code dependency, no matter where it is, can be inverted. That si the power that OO provides. That's what OO is really all about.

  • Conclusion
Using OO polymorphism, architect can gain absolute control over every source code dependency in the system. It allows the architect to create a plugin architecture, in which modules that contain high-level policies are independent of modules that contain low-level details.

2.4 Ch6. Functional Programming #


This paradigm is strongly based on the lambda-calculus by Alonzo Church in the 1930s.

  • Squares of Integers
Variables in functional languages do not vary.

  • Immutability and Architecture
All race conditions, deadlock conditions, and concurrent update problems are due to mutable variables.

  • Segregation of Mutability
The point is that well-structrued applications will be segregated into those components that do not mutate variables and those that do. This kind of segregation is supported by the use of appropriate disciplines to protect those mutated variables.

Architects would be wise to push as much processing as possible into the immutable components, and to drive as much code as possible out of those components that must allow mutation.


  • Event Sourcing
If we have enough storage and enough processor power, we can make our applications entirely immutable - and, therefore, entirely functional.

  • Conclusion

3 PART 3. Design Principles #


  • SRP: The Single Responsibililty Principle
  • OCP: The Open-Closed Principle
  • LSP: The Liskov Substitution Principle
  • ISP: The Inteface Segregation Principle
  • DIP: The Dependency Inversion Principle

3.1 Ch7. SRP: The Single Responsibility Principle #


Historically, the SRP has been described this way:
A module should have one, and only one, reason to change.
This can be rephrased:
A module should be responsible to one, and only one, actor.

The symptoms of violating this:
  • Symptom 1: Accidental Duplication
The SRP says to
separate the code that different actors depend on.

  • Symptom 2: Merges

  • Solutions
The most obvious way to solve the problem is to separate the data from the functions.

  • Conclution
The SRP is about functions and classes - but it reappears in a different form at two more levels. At the level of components, it becomes the Common Closure Principle. At the architectural level, it becomes the Axis of Change responsible for the creation of Architectural Boundaries.

3.2 Ch8. OCP: The Open-Closed Principle #


A software artifact should be open for extension but closed for modification.

The goal of OCP is to make the system easy to extend without incurring a high impact of change. This goal is accomplished by partitioning the system into components and arranging those components into a dependency hierarchy that protects higher-level components from changes in lower-level components.

3.3 Ch9. LSP: The Liskov Substitution Princicple #

The LSP can, and should, be extended to the level of architecture. A simple violation of substitutablity, can cause a system's architecture to be polluted with a significant amount of extra mechanisms.

3.4 Ch10. ISP: The Interface Segregation Principle #


User1 --> +--------------------
User2 --> | OPS +op1 +op2 +op3
User3 --> +--------------------

A change to the source code of op2 in OPS will force User1 to be recompiled and redeployed, even though nothing that it cared about has actually changed.

  • segregated operations
User1 --> <I>U1Ops+op1  <==+   +--------------------
User2 --> <I>U2Ops+op2  <==+== | OPS +op1 +op2 +op3
User3 --> <I>U3Ops+op3  <==+   +--------------------

Depending on something that carries baggage that you don't need can cause you troubles that you didn't expect.

3.5 Ch11. DIP: The Dependency Inversion Principle #

  • Stable Abstractions

    Don't refer to volatile concrete classes.
    Don't derive fro volatile concrete classes.
    Don't override concrete functions.
    Never mention the name of anything concrete and volatile.

Figure 11.1 Use of the Abstract Factory pattern to manage the dependency
clean-architecture-2020-figure-11-1.jpg

All source code dependencies cross the curved line pointing in the same direction, toward the abstract side. Note that the flow of control crosses the curved line in the opposite direction of the source code dependencies. The source code dependencies are inverted against the flow of control.

4 PART 4. Component Principles #


4.1 Ch12. Components #


4.2 Ch13. Component Cohension #

  • REP: The Reuse/Release Equivalence Principle
The granule of reuse is the granule of release.
  • CCP: The Common Closure Principle
Gather into components those classes that change for the same reasons and at the same times. Separate into different components those classes that change at different times and for different reasons.

  • CRP: The Common Reuse Principle
Don't force users of a component to depend on things they don't need.




4.3 Ch14. Component Coupling #


Allow no cycles in the component dependency graph.

  • Breaking the Cycle
inverting-the-dependency.png

  • The Stable Dependencies Principle
Stability:

X is a stable component
ComponentA ---> +------------+
ComponentB ---> | ComponentX |
ComponentC ---> +------------+

Y is a very unstable component
+------------+ ---> ComponentA
| ComponentY | ---> ComponentB
+------------+ ---> ComponentC

Stability Metrics
 Fan-in: Incoming dependencies.
 Fan-out: Outgoing dependencies.
 I: Instability = FanOut / (FanIn + FanOut)

  • The Stable Abstractions Principle
A component should be as abstract as it is stable.

A:Abstractness = Na / Nc
Nc: The number of classes in the component.
Na: The number of abstract classes and interfaces in the component

5 PART 5. Architecture #


5.1 Ch15. What Is Architecture? #

The architecture of a software system is the shape given to that system by those who build it. The form of that shape is in the division of that system into components, the arrangement of those components, and the ways in which those components communicate with each other.

The Purpose of that shape is to facilitate the development, deployment, operation, and maintenance of the software system contained within it.
The strategy behind that facilitation is to leave as many options open as possible, for as long as possible.

Good architects carefully separate details from policy, and then decouple the policy from the details so thoroughly that the policy has no knowledge of the details and does not depend on the details in any way. Good architects design the policy so that decisions about the details can be delayed and deferred for as long as possible.

5.2 Ch16. Independence #


a good architecture must support:
 * The use cases and operation of the system.
 * The maintenance of the system.
 * The development of the system.
 * The deployment of the system.

  • Use Cases
The architecture of the system must support the intent of the system.

  • Operation
Architecture plays a more substantial, and less cosmetic, role in supporting the operation of the system. If the system must handle 100,000 customers per second, the architecture must support that kind of throughput and response time for each use case that demands it.

  • Development
Architecture plays a significant role in supporting the development environment. This is whare Conway's law comes into play. "Any organization that designs a system will produce a design whose structure is a copy of the organization's communication structure."

  • Depolyment
The goal is "immediate deployment" A good architecture helps the system to be immediately deployable after build.

  • Leaving Options Open
A good architecture makes the system easy to change, in all the ways that it must change, by leaving options open.

  • Decoupling Layers
We find the system divided into decoupled horizontal layers - the UI, application-specific business rules, application-independent business rules, and the database.

  • Decoupling Use Cases
Use cases are a very natural way to divide the system. Use cases are narrow vertical slices that cut throught the horizontal layers of the system.

  • Decoupling Mode
Remember, a good architecture leaves options open. The decoupling mode is one of those options.

  • Independent Develop-Ability
So long as the layers and use cases are decoupoled, the architecutre of the system will support the organization of the teams, irrespective of whether they are organized as feature teams, component teams, layer teams, or some other variation.

  • Independent Deployability

  • Duplication
if they change at different rates, and for different reasons, then they are not true duplicates.

  • Decoupling Modes (Again)
- source level: e.g. Ruby Gems - deployment level: e.g. DLLs, jars, shared libraries - service level: e.g. micro-services

My preference is to push the decoupling to the point where a service could be formed. Initially the components are separated at the source code level. If deployment or development issues arise, driving some of the decoupling to a deploymen t level may be sufficient.

As the development, deployment, and operational issues increase, I carefully choose shich deployable units to turn into services, and gradually shift the system in that direction.

A good architecture will allow a system to be born as a monolith, deployed in a single file, but then to grow into a set of independently deployable units, and then all the way to independent service and /or micro-services. Later, as things change, ti should allow for reversing that progression and sliding all the way back down into a monolith.

A good architecture protects the majority of the source code from those changes.

5.3 Ch17. Boundaries: Drawing Lines #

clean-architecture-ch17-5.jpg

The direction of this line shows that the Database does not matter to the BusinessRules, but the Database cannot exists without the BusinessRules. The database could be implemented with Oracle, or MySQL, or even flat files. The business rules don't care at all.

  • Plugin Architecture

5.4 Ch18. Boundary Anatomy #

  • Monolith
  • Deployment Components
  • Threads
  • Local Processes
  • Services

5.5 Ch19. Policy and Level #

- -

5.6 Ch20. Business Rules #


Business rules are rules or procedures that make or save the business money.

Critical Business Rules

Critical Business Data

  • Entities
An object within our computer system that embodies a small set of critical business rules operating on Critical Business Data.

  • Use Cases
A use case describes application-specific business rules as opposed to the Critical Business Rules within the Entities. Use cases contain the rules that specify how and when the Critical Business Rules within the Entities are invoked. Use cases do not describe how the system appears to the user. Instead, they describe the application-specific rules that govern the interaction between the users and the Entities. How the data gets in and out of the system is irrelevant to the use cases. High-level concepts, such as Entities, know nothing of lowe-level concepts, such as use cases.

The business rules should be the most independent and reusable code in the system.

5.7 Ch21. Screaming Architecture #

  • The theme of an architecture
The architecture of a software application scream about the use cases of the application.

5.8 Ch22. The Clean Architecture #

Each of these architectures produces systems that have the following chracteristics:
- Independent of frameworkds
- Testable
- Independent of the UI
- Independent of the database
- Independent of any external agency

clean-architecture-ch22-1.jpg

Source code dependencies must point only inward, toward higher-level policies.

clean-architecture-ch22-2.jpg

5.9 Ch23. Presenters and Humble Objects #

  • The Humble Object Pattern
Split the behaviors into two modules or classes. One of those modules is humble; it contains all the hard-to-test behaviors stripped down to their barest essence. The other module contains all the testable behaviors that were stripped out of the humble object.
  • Presenters and Views
The View is the humble object that is hard to test. The code in this object is kept as simple as possible. It moves data into the GUI but does not process that data.

The Presenter is the testable object. Its job is to accept data from the application and format it for presentation so that the View can simply move it to the screen.

  • Database Gateways
Between the use case interactors and the database are the database gateways. Those gateways are implemented by classes in the database layer. That implementation is the humble object.

  • Data Mappers
ORMs would be better named "data mappers" because they load data into data structures from relational database tables. ORMs form another kind of Humble Object boundary between the gateway interfaces and the database.

  • Service Listeners

5.10 Ch24. Partial Boundaries #

Full-fledged architectural boundaries are expensive.

  • Skip the Last Step
  • One-dimensional Boundaries
  • Facades

5.11 Ch25. Layers and Boundaries #

- - - - -

5.12 Ch26. The Main Component #

- -

5.13 Ch27. Service: Great and Smal #

- - - - -

5.14 Ch28. The Test Boundary #

- - - -

5.15 Ch29. Clean Embedded Architecture #

- -


6 PART 6. Details #


Ch30. The Database Is a Detail - - - - -

Ch31. The Web Is a Detail - - -

Ch32. Frameworks Are Details - - -

Ch33. Case Study: Video Sales - - -

Ch34. The Missing Chapter
  • Package By Layer
Problem: you will quickly find that having three large buckets of code isn't sufficient, and you will need to think about modularizing further. Another problem is that a layerd architecture doesn't scream anything about the business domain.

  • Package By Feature

  • Ports and Adapters

  • Package By Component
Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2022-06-27 21:33:48
Processing time 0.4322 sec