Architectures should not be supplied by frameworks. Frameworks are tools to be used, not architectures to be conformed to. If your architecture is based on frameworks, then it cannot be based on your use cases.
A good architecture emphasizes the use cases and decouples them from peripheral concerns.
The fact that your application is delivered over the web is a detail and should not dominate your system structure.
Look at each framework with a jaded eye. View it skeptically. Yes, it might help, but at what cost? Ask yourself how you should use it, and how you should protect yourself from it. Think about how you can preserve the use-case emphasis of your architecture. Develop a strategy that prevents the framework from taking over that architecture.
If your system architecture is all about the use cases, and if you have kept your frameworks at arm's length, then you should be able to unit-test all those use cases without any of the frameworks in place.
If you are building a health care system, then when new programmers look at the source repository, their first impression should be, "Oh, this is a health care system." Those new programmers should be able to learn all the use cases of the system
There're multiple architecture ideas. They all have something in common:
- Independent of frameworks. The architecture does not depend on the existence of some library of feature-laden software.
- Testable. The business rules can be unit-tested.
- Independent of the UI. The UI can change easily.
- Independent of the database. You can swap out SQL servers.
- Independent of any external agency. Your business rules don't know anything at all about the interfaces to the outside world.
- Fig 22.1 The clean architecture
In general, the further in you go, the higher level the software becomes. The outer circles are mechanisms. The inner circles are policies.
Entities encapsulate enterprise-wide Critical Business Rules. An entity can be an object with methods, or it can be a set of data structures and functions.
The software in the use cases layer contains application-specific business rules. It encapsulates and implements all of the use cases of the system.
We do not expect changes in this layer to affect the entities. We also do not expect this layer to be affected by changes to externalities such as the database, the UI, or any of the common frameworks.
The software in the interface adapters layer is a set o adopters that convert data from the format most convenient for the use cases and entities, to the format most convenient for some external agency such as the database or the web.
The outermost layer of the model in Figure 22.1 is generally composed of frameworks and tools such as the database and the web framework. Generally you don't write much code in this layer, other than glue code that communicates to the next circle inward.
There's no rules that it has to be four.
Source code dependencies always point inward.
No name in an outer circle can be mentioned by an inner circle. So we have the use case call an interface (shown in figure 22.1 as "use case output port") in the inner circle, and have the presenter in the outer circle implement it.
The important thing is that isolated, simple data structures are passed across the boundaries. We don't want to cheat and pass Entity objects or database rows.
- Fig 22.2 A typical scenario for a web-based Java system utilizing a database
In the example, there's model for "View", called the ViewModel.
By separating the software into layers and conforming to the Dependency Rule, you will create a system that is intrinsically testable, with all the benefits that implies.
Presenters are a form of the Humble Object pattern, which helps us identify and protect architectural boundaries.
The Humble Object pattern is a design pattern that was originally identified as a away to help unit testers to separate behaviors that are hard to test from behaviors that are easy to test.
For example, GUIs are hard to unit test because it is very difficult to write tests that can see the screen and check that the appropriate elements are displayed there. However, most of the behavior of a GUI is, in fact, easy to test.
The View is the humble object that is hard to test. The code in this object is kept as simple as possible.
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.
It has long been known that testability is an attribute of good architectures.
Between the use case interactors and the database are the database gateways. Thse gateways are polymorphic interfaces that contain methods for every create, read, update, or delete operation that can be performed by the application on the database.
Recall that we do not allow SQL in the use cases layer; instead, we use gateway interfaces that have appropriate methods.
Objects are not data structures. At least, they are not data structures from their users' point of view. The users of an object cannot see the data, since it is all private.
ORMs would be better named "data mappers"
The communication across that boundary will almost always involve some kind of simple data structure, and the boundary will frequently divide something that is hard to test from something that is easy to test.
In many situations, a good architect might judge that the expense of such a boundary is too high
YAGNI: "You Aren't Going to Need It." Architects, however, sometimes look at the problem and think, "Yeah, but I might." In that case, tehy may implement a partial boundary.
Obviously, that kind of partial boundary requires the same amount of code and preparatory design work as a full boundary. However, it does not require the administration of multiple components. There's no version number tracking or release management burden. That difference should not be taken lightly.
The idea was that we might want to create other web-based applications by using that web component.
- Fig 24.1 The Strategy pattern
Without reciprocal interfaces, nothing prevents this kind of backchannel other than the diligence and discipline of the developers and architects.
- Fig 24.2 The Facade pattern
you can imagine how easy backchannels are to create with this structure.
Each of these approaches has its own set of costs and benefits. Easy is appropriate, in certain contexts, as a placeholder for an eventual full-fledged boundary.
It is easy to think of systems as being composed of three components: UI, business rules, and database.
I guess it meant about "MVC".
- Fig 25.1 Any number of UI components can reuse the game rules
- Fig 25.2 Following the Dependency Rule
- Fig 25.3 The revised diagram
In each case, the API defined by those Boundary interface is owned by the upstream component.
- Fig 25.4 Simplified diagram
This organization effectively divides the flow of data into two streams. The stream on the left is concerned with communicating with the user, and the stream on the right is concerned with data persistence.
- Fig 25.5 Adding a network component
- Fig 25.6 The higher-level policy manages the player
- Fig 25.7 Adding a micro-service API
This example is intended to show that architectural boundaries exist everywhere.
we have to recognize that when such boundaries are ignored, they are very expensive to add in later - even in the presence of comprehensive test-suites and refactoring discipline.
we should not anticipate the need for abstraction. Tis is the philosophy of YAGNI
when you discover that you truly do need an architectural boundary where none exists, the costs and risks can be very high to add such a boundary.
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.
But this is not a one-time decision.