Skip to content

Instantly share code, notes, and snippets.

@wataruoguchi
Created June 22, 2021 05:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wataruoguchi/054d5019f3c245cb271a932f8306e446 to your computer and use it in GitHub Desktop.
Save wataruoguchi/054d5019f3c245cb271a932f8306e446 to your computer and use it in GitHub Desktop.
Clean Architecture - PART 5 - Architecture (Chapter 21 - 25) #bookclub

Clean Architecture

PART V - Architecture

Chapter 21 - Screaming Architecture

The Theme of an Architecture

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.

The Purpose of an Architecture

A good architecture emphasizes the use cases and decouples them from peripheral concerns.

But What About the Web?

The fact that your application is delivered over the web is a detail and should not dominate your system structure.

Frameworks Are Tools, Not Ways of Life

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.

Testable Architectures

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.

Conclusion

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

Chapter 22 - The Clean Architecture

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.

The Dependency Rule

  • 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

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.

Use Cases

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.

Interface Adapters

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.

Frameworks and Drivers

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.

Only four circles?

There's no rules that it has to be four.

Source code dependencies always point inward.

Crossing boundaries

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.

Which data crosses the boundaries

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.

A Typical Scenario

  • 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.

Conclusion

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.

Chapter 23 - Presenters and Humble Objects

Presenters are a form of the Humble Object pattern, which helps us identify and protect architectural boundaries.

The Humble Object Pattern

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.

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.

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.

Testing and Architecture

It has long been known that testability is an attribute of good architectures.

Database Gateways

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.

Data Mappers

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"

Service Listeners

Conclusion

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.

Chapter 24 - Partial Boundaries

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.

Skip the Last Step

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.

One-Dimensional Boundaries

  • 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.

Facades

  • Fig 24.2 The Facade pattern

you can imagine how easy backchannels are to create with this structure.

Conclusion

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.

Chapter 25 - Layers and Boundaries

It is easy to think of systems as being composed of three components: UI, business rules, and database.

I guess it meant about "MVC".

Hunt the Wumpus

Hunt the Wumpus

Clean Architecture?

  • 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.

Crossing the Streams

  • Fig 25.5 Adding a network component

Splitting the Streams

  • Fig 25.6 The higher-level policy manages the player
  • Fig 25.7 Adding a micro-service API

Conclusion

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment