第一章 What is design and architecture
The word “architecture” is often used in the context of something at a high level that is divorced from the lower-level details,whereas "design" more often seems to imply structures and decisions at a lower level.
The goal of software architecture is to minimize the human resources required to build and maintain the required system.
第二章 A tale of two values
Every software system provides two different values to the stakeholders:behavior and structure.Software developers are responsible for ensuring that both values remain high.
The first value of software--behavior--is urgent but not always particularly important.
The second value of software--architecture--is important but never particularly urgent.
第三章 Paradigm overview
Structured programming imposes discipline of direct transfer of control.
Object-oriented programming imposes discipline of indirect transfer of control.
Functional programming imposes discipline upon assignment.
Each of the paradigms removes capabilities from programmer. None of them add new capabilities. The three paradigms together remove goto statements, function pointers, and assignment.
第四章 Structured programming
Structured programming allows modules to be recursively decomposed into provable units, which in turn means that modules can be functionally decomposed.That is, you can take a large-scale problem statement and decompose it into high-level functions. Each of those functions can then be decomposed into lower-level functions, ad infinitum. Moreover, each of those decomposed functions can be represented using the restricted control structures of structured programming.
Dijkstra once said , "Testing shows the presence ,not the absence ,of bugs." In other words, a program can be proven incorrect by a test ,but it cannot be proven correct.
Structured programming forces us to recursively decompose a program into a set of small provable functions. We can then use tests to try to prove those small provable functions incorrect. If such tests fail to prove incorrectness, then we deem the functions to be correct enough for our purposes.
第五章 Object-oriented programming
The fact that OO languages provide safe and convenient polymorphism means that any source code dependency ,no matter where it is, can be inverted.
To the software architect, OO is the ability , through the use of polymorphism, to 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.The low-level details are relegated to plugin modules that can be deployed and developed independently from the modules that contain high-level policies.
第六章 Functional programming
Variables in functional languages do not vary.
All race conditions , deadlock conditions, and concurrent update problems are due to mutable variables.
Design principles
The SOLID principles tell us how to arrange our functions and data structures into classes, and how those classes should be interconnected.
The goal of the principles is the creation of mid-level software structures that:
1 Tolerate change,
2 Are easy to understand ,and
3 Are the basis of components that can be used in many software systems.
第七章 SIP:The single responsibility principle
SIP: A module should be responsible to one ,and only one, actor.
Now, what do we mean by the word "module" ? The simplest definition is just a source file. Most of the time that definition works fine. Some languages and development environment, though ,don't use source files to contain their code. In those cases a module is just a cohesive set of functions and data structures.
The word "cohesive" implies the SRP. Cohesion is the force that binds together the code responsible to a single actor.
第八章 OCP:The open-closed principle
OCP: A software artifact should be open for extension but closed for modification.
The OCP is one of the driving forces behind the architecture of systems. The goal 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.
第九章 LSP:The liskov substitution principle
LSP: What is wanted here is something like the following substitution property: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.
第十章 ISP: The interface segregation principle
The lesson here is that depending on something that carries baggage that you don't need can cause you troubles that you didn't expect.
第十一章 DIP: The dependency inversion principle
The DIP tells us that the most flexible systems are those in which source code dependencies refer only to abstractions ,not to concretions.
We tend to ignore the stable background of operating system and platform facilities when it comes to DIP. We tolerate those concrete dependencies because we know we can rely on them not to change.
It is the volatile concrete elements of our system that we want to avoid depending on . Those are the modules that we are actively developing ,and that are undergoing frequent change.
Stable architectures are those that avoid depending on volatile concretions, and that favor the use of stable abstract interfaces. This implication boils down to a set of very specific coding practices:
Don't refer to volatile concrete classes, refer to abstract interfaces instead. It also puts severe constraints on the creation of objects and generally enforces the use of Abstract Factories.
Don't derive from volatile concrete classes.
Don't override concrete functions.
Never mention the name of anything concrete and volatile.
Components principles:If the SOLID principles tell us how to arrange the bricks into walls and rooms, then the component principles tell us how to arrange the rooms into buildings.
第十二章 Components
Components are the units of deployment. They are the smallest entities that can be deployed as part of a system.
Components can be linked together into a single executable.
第十三章 Components cohesion
REP: The reuse/release equivalence principle ,the granule of reuse is the granule of release.
For a software design and architecture point of view, this principle means that the classes and modules that are formed into a component must belong to a cohesive group. The component cannot simply consist of a random hodgepodge of classes and modules; instead ,there must be some overarching theme or purpose that those modules all share.
CCP:The common closure principle: Gather into components those classes that change for the same reasons and at same times. Separate into different components those classes that change at different times and for different reasons.
The SRP tells us to separate methods into different classes, if they change for different reasons. The CCP tells us to separate classes into different components, if they change for different reasons.
CRP:The common reuse principle. Don't force users of a component to depend on things they don't need.
The CRP is the generic version of the ISP. The ISP advises us not depend on classes that have methods we don't use. The CRP advises us not to depend on components that have classes we don't use.
第十四章 Component coupling
ADP:The acyclic dependency principle: Allow no cycles in the component dependency graph.
SDP:Depend in the direction of stability.
SAP:The stable abstractions principle,A component should be as abstract as it is stable.
The SAP sets up a relationship between stability and abstractness. On the one hand, it says that a stable component should also be abstract so that its stability does not prevent it from being extended. On the other hand, it says that an unstable component should be concrete since its instability allows the concrete code within it to be easily changed.
第十五章 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 ship 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 ultimate goal of the architecture is to minimize the lifetime cost of the system and to maximize programmer productivity.
The goal of the architect is to create a shape for the system that recognizes policy as the most essential element of the system while making the details irrelevant to that policy. This allows decisions about those details to be delayed and deferred.
第十六章 Independence
As we previously stated, 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.
A good architecture make the system easy to change , in all the ways that it must change, by leaving options open.
The following principles help us partition our systems into well-isolated components that allow us to leave many options open as possible, for as long as possible.
Decoupling layers: Thus we find the system divided into decoupled horizontal layers---the UI, application-specific business rules, application-independent business rules, and the database, just to mention a few.
Decoupling use cases: Use cases are narrow vertical slices that cut through the horizontal layers of the system. Each use case use some UI, some application-specific business rules ,some application independent rules, and some database functionality.
Decoupling modes
Source level:We can control dependencies between source code modules so that changes to one module do not force changes or recompilation of others.
Deployment level:We can control dependencies between deployable units such as jar files, DLLs, or shared libraries , so that changes to the source code in one module do not force others to be rebuilt and redeployed.
Service level: We can reduce the dependencies down to the level of data structures ,and communicate solely through network packets such that every execution unit is entirely independent of source and binary changes to others.
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 services and/or micro-services. Later, as things change, it should allow for reversing that progression and sliding all the way back down into a monolith.
第十七章 Boundaries: drawing lines
To draw boundary lines in a software architecture, you first partition the system into components. Some of those components are core business rules; others are plugins that contain necessary functions that are not directly related to the core business. Then you arrange the code in those components such that arrows between them point in one direction--- toward the core business.
第十八章 Boundary anatomy
The architecture of a system is defined by a set of software components and the boundaries that separate them. Those boundaries come in many different forms.
Boundary crossing: a function on one side of the boundary calling a function on the other side.
The dreaded monolith:The simplest and most common of the architecture boundaries has no strict physical representation . It is simply a disciplined segregation of functions and data within a single processor and a single address space.
Deployment components: The simplest physical representation of an architectural boundary is a dynamically linked library like a .Net DLL, a Java jar file, a Ruby Gem, or a UNIX shared library.
Threads: Both monoliths and deployment components can make use of threads.
Local processes: Local processed run in the same processor, or in the same set of processors within a multicore, but run in separate address spaces.
Services:Two communicating services may, or may not ,operate in the same physical processor or multicore. The services assume that all communications take place over the network.
第十九章 Policy and level
Policy: A computer program is a detailed description of the policy by which inputs are transformed into outputs.
Level: A strict definition of "level" is the distance from the inputs and outputs.
第二十章 Business rules
Very strictly speaking ,business rules would make or save the business money, irrespective of whether they were implemented on a computer. They would make or save money even if they were executed manually.
The Entity is pure business and nothing else.
A use case describes application-specific business rules as opposed to the Critical Business Rules within the Entities.
第二十一章 Screaming architecture
Good architectures are centered on use cases so that architects can safely describe the structures that support those use cases without committing to frameworks, tools, and environments.
第二十二章 The Clean Architecture
第二十三章 Presenters and humble objects
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.
第二十四章 Partial boundaries
Skip the last step: One way to construct a partial boundary is to do all the work necessary to create independently compilable and deployable components ,and then simply keep them together in the same component.
One-dimensional boundaries
Facades
第二十五章 Layers and boundaries
As an architect,You must weigh the costs and determine where the architectural boundaries lie, and which should be fully implemented, and which should be partially implemented ,and which should be ignored.
第二十六章 The main component
The main component is the ultimate detail---the lowest-level policy. It is the initial entry point of the system . Nothing , other than the operating system , depends on it.
Think of Main as a plugin to the application--- a plugin that sets up the initial conditions and configurations, gathers all the outside resources, and then hands control over to the high-level policy of the application.
第二十七章 Services:great and small
As useful as services are to the scalability and develop-ability of a system, they are not , in and of themselves ,architecturally significant elements. The architecture of a system is defined by the boundaries drawn within that system ,and by the dependencies that cross those boundaries. That architecture is not defined by the physical mechanisms by which elements communicate and execute.
A service might be a single component ,completely surrounded by an architectural boundary. Alternatively, a service might be composed of several components separated by architectural boundaries.
第二十八章 The test boundary
Tests are not outside the system; rather, they are parts of the system that must be well designed if they are to provide the desired benefit of stability and regression. Tests that are not designed as part of the system tend to be fragile and difficult to maintain.
第二十九章 Clean embedded architecture
Kent Beck describes three activities in building software:
1 "First make it work." You are out of business if it doesn't work.
2 "Then make it right." Refactor the code so that you and others can understand it and evolve it as needs change or are better understood.
3 "Then make it fast." Refactor the code for "needed" performance.
第三十章 The database is a detail
The organizational structure of data , the data model, is architecturally significant. The technologies and systems that move data on and off a rotating magnetic surface are not .Relational database systems that force the data to be organized into tables and accessed with SQL have much more to do with the latter than with the former. The data is significant. The database is a detail.
第三十一章 The web is a detail
The GUI is a detail. The web is a GUI. So the web is a detail.
第三十二章 Frameworks are details
When faced with a framework, try not to marry it right away. See if there aren't ways to date it for a while before you take the plunge. Keep the framework behind an architectural boundary if at all possible, for as long as possible. Perhaps you can find a way to get the milk without buying the cow.