More on this book
Community
Kindle Notes & Highlights
The more memory we have, and the faster our machines are, the less we need mutable state.
Event sourcing is a strategy wherein we store the transactions, but not the state. When state is required, we simply apply all the transactions from the beginning of time.
If we have enough storage and enough processor power, we can make our applications entirely immutable—and, therefore, entirely functional.
If this still sounds absurd, it might help if you remembered that this is precisely the way your source code control system works.
Structured programming is discipline imposed upon direct transfer of control.
Object-oriented programming is discipline imposed upon indirect transfer of control.
Functional programming is discipline imposed upon variable assignment.
Each of these three paradigms has taken something away from us. Each restricts some aspect of the way we write code. None of them has added to our power or our capabilities.
What we have learned over the last half-century i...
This highlight has been truncated due to consecutive passage length restrictions.
With that realization, we have to face an unwelcome fact: Software is not a rapid...
This highlight has been truncated due to consecutive passage length restrictions.
The rules of software are the same today as they were in 1946, when Alan Turing wrote the very first code that would ...
This highlight has been truncated due to consecutive passage length restrictions.
The tools have changed, and the hardware has changed, but the essence of sof...
This highlight has been truncated due to consecutive passage length restrictions.
the stuff of computer programs—is composed of sequence, selection, iteration, and indirection....
This highlight has been truncated due to consecutive passage length restrictions.
Good software systems begin with clean code. On the one hand, if the bricks aren’t well made, the architecture of the building doesn’t matter much.
On the other hand, you can make a substantial mess with well-made bricks. This is where the SOLID principles come in.
SRP: The Single Responsibility Principle
OCP: The Open-Closed Principle
they must be designed to allow the behavior of those systems to be changed by adding new code, rather than changing existing
LSP: The Liskov Substitution Principle
In short, this principle says that to build software systems from interchangeable parts, those parts must adhere to a contract that allows those parts to be substituted one for another.
The Interface Segregation Principle This principle advises software designers to avoid depending on things that they don’t use.
The Dependency Inversion Principle The code that implements high-level policy should not depend on the code that implements low-level details. Rather, details should depend on policies.
Historically, the SRP has been described this way: A module should have one, and only one, reason to change.
A module should be responsible to one, and only one, actor.
SYMPTOM 1: ACCIDENTAL DUPLICATION
The SRP says to separate the code that different actors depend
SYMPTOM 2: MERGES
Once again, the way to avoid this problem is to separate code that supports different actors.
The EmployeeFacade contains very little code. It is responsible for instantiating and delegating to the classes with the functions.
business rules closer to the data. This can be done by keeping the most important method in the original Employee class and then using that class as a Facade for the lesser functions
A software artifact should be open for extension but closed for modification.
Interactors are the highest-level concept, so they are the most protected. Views are among the lowest-level concepts, so they are the least protected. Presenters are higher level than Views, but lower level than the Controller or the Interactor.
This is how the OCP works at the architectural level. Architects separate functionality based on how, why, and when it changes, and then organize that separated functionality into a hierarchy of components.
Transitive dependencies are a violation of the general principle that software entities should not depend on things they don’t directly use.
even though our first priority is to protect the Interactor from changes to the Controller, we also want to protect the Controller from changes to the Interactor by hiding the internals of the Interactor.
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.
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.
Since the behavior of the User depends on the types it uses, those types are not substitutable.
The LSP can, and should, be extended to the level of architecture. A simple violation of substitutability, can cause a system’s architecture to be polluted with a significant amount of extra mechanisms.
Let’s assume that User1 uses only op1, User2 uses only op2, and User3 uses only op3.
Now imagine that OPS is a class written in a language like Java. Clearly, in that case, the source code of User1 will inadvertently depend on op2 and op3, even though it doesn’t call them.
This problem can be resolved by segregating the operations into interfaces as shown in Figure 10.2.
In general, it is harmful to depend on modules that contain more than you need. This is obviously true for source code dependencies that can force unnecessary recompilation and redeployment—but it is also true at a much higher, architectural level.
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. We’ll explore this idea in more detail when we discuss the Common Reuse Principle in Chapter 13, “Component Cohesion.”
The Dependency Inversion Principle (DIP) tells us that the most flexible systems are those in which source code dependencies refer only to abstractions, not to concretions.
In a statically typed language, like Java, this means that the use, import, and include statements should refer only to source modules containing interfaces, abstract classes, or some other kind of abstract declaration. Nothing concrete should be depended on.
The same rule applies for dynamically typed languages, like Ruby and Python. Source code dependencies should not refer to concrete modules. However, in these languages it is a bit harder to define what a concrete module is. In particular, it i...
This highlight has been truncated due to consecutive passage length restrictions.
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.
Therefore interfaces are less volatile than implementations.
Indeed, good software designers and architects work hard to reduce the volatility of interfaces.