Skip to content

Instantly share code, notes, and snippets.

@wataruoguchi
Created July 3, 2021 21:25
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/7d24513c1939e8a6cdff10a8e8e5ca89 to your computer and use it in GitHub Desktop.
Save wataruoguchi/7d24513c1939e8a6cdff10a8e8e5ca89 to your computer and use it in GitHub Desktop.
Clean Architecture - PART 5 - Architecture (Chapter 26 - 29) #bookclub

Clean Architecture

PART V - Architecture

Chapter 26 - The Main Component

The Ultimate Detail

The point is that Main is a dirty low-level module in the outermost circle of the clean architecture. It loads everything up for the high level system, and then hands control over to it.

Conclusion

Since it is a plugin, it is possible to have many Main components, one for each configuration of your application.

When you think about Main as a plugin component, sitting behind an architectural boundary, the problem of configuration becomes a lot easier to solve.

Chapter 27 - Services: Great and Small

Service-oriented “architectures” and micro-service “architectures” have become very popular of late. The reasons for their current popularity include the following:

  • Services seem to be strongly decoupled from each other. As we shall see, this is only partially true.
  • Services appear to support independence of development and deployment. Again, as we shall see, this is only partially true.

Service Architecture?

The architecture of a system is defined by boundaries that separate high-level policy from low-level detail and follow the Dependency Rule.

Service Benefits?

The Decoupling Fallacy

One of the big supposed benefits of breaking a system up into services is that services are strongly decoupled from each other.

The services must also strongly agree about the interpretation of the data in that field. Thus those services are strongly coupled to the data record and, therefore, indirectly coupled to each other.

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.

  1. services are not the only option for building scalable systems.
  2. the decoupling fallacy means that services cannot always be independently developed, deployed, and operated.

The Kitten Problem

  • Fig 27.1 Services arranged to implement the taxi aggregator system

The elements are:

  • Taxi UI
  • Taxi Finder
  • Candidate Taxis
  • Taxi Selector
  • Taxi Dispatcher
  • Taxi Supplier 1-3

Now we want to add a kitten delivery service.

Look at that diagram of services. How many of those services will have to change to implement this feature? All of them. Clearly, the development and deployment of the kitty feature will have to be very carefully coordinated. In other words, the services are all coupled, and cannot be independently developed, deployed, and maintained.

^ the problem with cross-cutting concerns.

Functional decompositions in the diagram are very vulnerable to new features that cut across all those functional behaviors.

Object to the rescue

portion of the logic that was specific to rides has been extracted into a Rides component. The new feature for kittens has been placed into a Kittens component. These two components override the abstract base classes in the original components using a pattern such as Template Method or Strategy.

the two new components, Rides and Kittens, follow the Dependency Rule.

  • Fig 27.2 Using an object-oriented approach to deal with cross-cutting concerns

Component-based services

Services can, in stead, be designed using the SOLID principles (to apply the same solution as the kitten problem)

adding new features conforms to the Open-Closed Principle.

  • Fig 27.3 Each service has its own internal component design, enabling new features to be added as new derivative classes

Cross-cutting Concerns

What we have learned is that architectural boundaries do not fall between services. Rather, those boundaries run through the services, dividing them into components.

  • Fig 27.4 Services must be designed with internal component architectures that follow the Dependency Rule

Conclusion

The architecture of a system is defined by the boundaries drawn within that system, and by the dependencies that cross those boundaries.

Chapter 28 - The Test Boundary

Test as System Components

Are unit tests and integration tests different things? What about acceptance tests, functional tests, Cucumber tests, TDD tests, BDD tests, component tests, and so on?

In fact, you can think of the tests as the outermost circle in the architecture.

Design for Testability

Changes to common system components can cause hundreds, or even thousands, of tests to break. This is known as the Fragile Tests Problem.

Irf those tests are strongly coupled to the system.

Fragile tests often have the perverse effect of making the system rigid.

The first rule of software design—whether for testability or for any other reason—is always the same: Don’t depend on volatile things. GUIs are volatile.

The Testing API

This API should have superpowers that allow the tests to avoid security constraints, bypass expensive resources (such as databases), and force the system into particular testable states. This API will be a superset of the suite of interactors and interface adapters that are used by the user interface.

This decoupling encompasses more than just detaching the tests from the UI: The goal is to decouple the structure of the tests from the structure of the application.

Structural Coupling

The role of the testing API is to hide the structure of the application from the tests.

Security

Conclusion

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 benefits of stability and regression.

Chapter 29 - Clean Embedded Architecture

"The Growing Importance of Sustaining Software for the DoD" by Doug Schmidt.

“Although software does not wear out, firmware and hardware become obsolete, thereby requiring software modifications.”

Software is this thing that can have a long useful life, but firmware will become obsolete as hardware evolves.

"Although software does not wear out, it can be destroyed from within by unmanaged dependencies on firmware and hardware."

Hardware does evolve, so we should structure our embedded code with that reality in mind.

Stop writing so much firmware and give your code a chance at a long useful life.

Let's look at how we can keep embedded software architecture clean to give the software a fighting chance of having a long useful life.

App-titude Test

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 understand.
  3. "Then make it fast." Refactor the code for "needed" performance.

Learn what works, then make a better solution.

The engineer passed the App-titude test. But the application can't be said to have a clean embedded architecture.

The Target-Hardware Bottleneck

When embedded code is structured without applying clean architecture principles and practices, you will often face the scenario in which you can test your code only on the target.

A clean embedded architecture is a testable embedded architecture
Layers
  • Fig 29.1 Three layers

  • Software

  • Firmware

  • Hardware

  • Fig 29.2 Hardware must be separated from the rest of the system

  • Firmware

  • Hardware

Software and firmware intermingling is an anti-pattern.

The hardware is a detail
  • Fig 29.3 The line between software and firmware is a bit fuzzier than the line between code and hardware.

The hardware abstraction layer (HAL)

  • Fig 29.4 The hardware abstraction layer

  • Software

  • HAL

  • Firmware

  • Hardware

The firmware could provide access to the GPIO bits, where a HAL might provide Led_TurnOn(5). That is a pretty low-level hardware abstraction layer.

What is the LED indicating? Suppose that it indicated low battery power.

Don't reveal hardware details to the user of the HAL
The processor is a detail
The operation system is a detail

a real-time operation system (RTOS)

  • Fig 29.5 Adding in an operating system

  • Software

  • OS

  • Firmware

  • Hardware

an operating system abstraction layer (OSAL)

  • Fig 29.6 The operating system abstraction layer

  • Software

  • OSAL

  • OS

  • HAL

  • Firmware

  • Hardware

A clean embedded architecture's software is testable off the target operating system. A successful OSAL provides that seam or set of substitution points that facilitate off-target testing.

Programming to interfaces and substitutability
  • One basic rule of thumb is to use header files as interface definitions.

  • Don't clutter the interface header files with data structures, constants, and typedefs that are needed by only the implementation.

DRY Conditional Compilation Directives

Conclusion

Letting all code become firmware is not good for your product’s long-term health. Being able to test only in the target hardware is not good for your product’s long-term health. A clean embedded architecture is good for your product’s long-term health.

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