Clean Architecture: A Craftsman's Guide to Software Structure and Design
Rate it:
Open Preview
42%
Flag icon
In a good architecture, the direction of those dependencies is based on the level of the components that they connect. In every case, low-level components are designed so that they depend on high-level components.
42%
Flag icon
strict definition of “level” is “the distance from the inputs and outputs.” The farther a policy is from both the inputs and the outputs of the system, the higher its level.
42%
Flag icon
Note that the data flows and the source code dependencies do not always point in the same direction. This, again, is part of the art of software architecture. We want source code dependencies to be decoupled from data flow and coupled to level.
44%
Flag icon
use case is an object. It has one or more functions that implement the application-specific business rules. It also has data elements that include the input data, the output data, and the references to the appropriate Entities with which it interacts.
44%
Flag icon
Go back and read Ivar Jacobson’s seminal work on software architecture: Object Oriented Software Engineering.
45%
Flag icon
Hexagonal Architecture (also known as Ports and Adapters), developed by Alistair Cockburn, and adopted by Steve Freeman and Nat Pryce in their wonderful book Growing Object Oriented Software with Tests
45%
Flag icon
DCI from James Coplien and Trygve Reenskaug
45%
Flag icon
The diagram in Figure 22.1 is an attempt at integrating all these architectures into a single actionable idea.
45%
Flag icon
The outer circles are mechanisms. The inner circles are policies. The overriding rule that makes this architecture work is the Dependency Rule: Source code dependencies must point only inward, toward higher-level policies.
47%
Flag icon
Figure 22.2 A typical scenario for a web-based Java system utilizing a database
47%
Flag icon
In Chapter 22, we introduced the notion of presenters. Presenters are a form of the Humble Object pattern, which helps us identify and protect architectural boundaries. Actually, the Clean Architecture in the last chapter was full of Humble Object implementations.
47%
Flag icon
The Humble Object pattern1 is a design pattern that was originally identified as a way to help unit testers to separate behaviors that are hard to test from behaviors that are easy to test.
47%
Flag icon
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.
50%
Flag icon
You pay attention as the system evolves. You note where boundaries may be required, and then carefully watch for the first inkling of friction because those boundaries don’t exist.
50%
Flag icon
Your goal is to implement the boundaries right at the inflection point where the cost of implementing becomes less than the cost of ignoring.
52%
Flag icon
When you think about Main as a plugin component, sitting behind an architectural boundary, the problem of configuration becomes a lot easier to solve.
52%
Flag icon
As for interfaces being well defined, that’s certainly true—but it is no less true for functions. Service interfaces are no more formal, no more rigorous, and no better defined than function interfaces. Clearly, then, this benefit is something of an illusion.
52%
Flag icon
THE FALLACY OF INDEPENDENT DEVELOPMENT AND DEPLOYMENT Another of the supposed benefits of services is that they can be owned and operated by a dedicated team.
53%
Flag icon
This is the problem with cross-cutting concerns. Every software system must face this problem, whether service oriented or not. Functional decompositions, of the kind depicted in the service diagram in Figure 27.1, are very vulnerable to new features that cut across all those functional behaviors.
53%
Flag icon
Figure 27.2 Using an object-oriented approach to deal with cross-cutting concerns
53%
Flag icon
Services do not need to be little monoliths. Services can, instead, be designed using the SOLID principles, and given a component structure so that new components can be added to them without changing the existing components within the service.
53%
Flag icon
What we have learned is that architectural boundaries do not fall between services. Rather, those boundaries run through the services, dividing them into components. To deal with the cross-cutting concerns that all significant systems face, services must be designed with internal component architectures that follow the Dependency Rule, as shown in the diagram in Figure 27.4. Those services do not define the architectural boundaries of the system; instead, the components within the services do.
53%
Flag icon
Figure 27.4 Services must be designed with internal component architectures that follow the Dependency Rule
54%
Flag icon
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.
54%
Flag icon
The issue, of course, is coupling. Tests that are strongly coupled to the system must change along with the system. Even the most trivial change to a system component can cause many coupled tests to break or require changes.
55%
Flag icon
“Although software does not wear out, firmware and hardware become obsolete, thereby requiring software modifications.”
55%
Flag icon
Although software does not wear out, it can be destroyed from within by unmanaged dependencies on firmware and hardware.
55%
Flag icon
Firmware does not mean code lives in ROM. It’s not firmware because of where it is stored; rather, it is firmware because of what it depends on and how hard it is to change as hardware evolves.
55%
Flag icon
“First make it work.” You are out of business if it doesn’t work.
55%
Flag icon
“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.
55%
Flag icon
“Then make it fast.” Refactor the code for “needed” performance.
56%
Flag icon
Getting an app to work is what I call the App-titude test for a programmer. Programmers, embedded or not, who just concern themselves with getting their app to work are doing their products and employers a disservice. There is much more to programming than just getting an app to work.
57%
Flag icon
Figure 29.3 The line between software and firmware is a bit fuzzier than the line between code and hardware One of your jobs as an embedded software developer is to firm up that line. The name of the boundary between the software and the firmware is the hardware abstraction layer (HAL)
59%
Flag icon
The idea of a layered architecture is built on the idea of programming to interfaces. When one module interacts with another though an interface, you can substitute one service provider for another.
61%
Flag icon
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.
61%
Flag icon
The data is significant. The database is a detail.
61%
Flag icon
I do hope the architects at A, and the architects of the apps, keep their UI and business rules isolated from each other, because there are always marketing geniuses out there just waiting to pounce on the next little bit of coupling you
61%
Flag icon
The upshot is simply this: The GUI is a detail. The web is a GUI. So the web is a detail. And, as an architect, you want to put details like that behind boundaries that keep them separate from your core business logic.
62%
Flag icon
The complete input data and the resultant output data can be placed into data structures and used as the input values and output values for a process that executes the use case. With this approach, we can consider each use case to be operating the IO device of the UI in a device-independent manner.
62%
Flag icon
The architecture of the framework is often not very clean. Frameworks tend to violate the Dependency Rule. They ask you to inherit their code into your business objects—your Entities! They want their framework coupled into that innermost circle. Once in, that framework isn’t coming back out. The wedding ring is on your finger; and it’s going to stay there.
62%
Flag icon
The framework may help you with some early features of your application. However, as your product matures, it may outgrow the facilities of the framework. If you’ve put on that wedding ring, you’ll find the framework fighting you more and more as time passes.
62%
Flag icon
The framework may evolve in a direction that you don’t find helpful. You may be stuck upgrading to new versions that don’t help you. You may even find old features, which you made use of, disappearing or changing in ways that are difficult for you to keep up with.
62%
Flag icon
A new and better framework may come along that you wish you could switch to.
62%
Flag icon
Don’t marry the framework! Oh, you can use the framework—just don’t couple to it. Keep it at arm’s length. Treat the framework as a detail that belongs in one of the outer circles of the architecture. Don’t let it into the inner circles.
62%
Flag icon
If the framework wants you to derive your business objects from its base classes, say no! Derive proxies instead, and keep those proxies in components that are plugins to your business rules.
63%
Flag icon
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.
63%
Flag icon
Figure 33.1 shows a typical use-case analysis.
63%
Flag icon
Note the dashed use cases in the center of Figure 33.1. They are abstract1 use cases. An abstract use case is one that sets a general policy that another use case will flesh out. As you can see, the View Catalog as Viewer and View Catalog as Purchaser use cases both inherit from the View Catalog abstract use case.
63%
Flag icon
Figure 33.2 A preliminary component architecture
63%
Flag icon
Keeping these options open will allow us to adapt the way we deploy the system based on how the system changes over time.