DEV Community

CodingBlocks

76. Clean Architecture – What is the Humble Object Pattern?

It’s time for another deep dive into Robert C. Martin’s Clean Architecture as Joe puts us on the spot, Allen has a new mission, and Michael shares his Easter eggs.

Sponsors

Survey Says

While we patiently wait for Spring to arrive, we ask: When you’re not coding for work/school, in your free time do you …

#yop-poll-container-53_yp5a9f5ddd210c9 { width: 1000; background:#fff; padding:10px; color:#555; overflow:hidden; font-size:12px; } #yop-poll-container-53_yp5a9f5ddd210c9 input[type='text'] { margin:0px 0px 5px 0px; padding:2%; width:96%; text-indent:2%; font-size:12px; } .yop-poll-name-53_yp5a9f5ddd210c9 { font-weight:bold; background:#327BD6; color:#fff; padding:5px; text-align:center; font-size:12px; } #yop-poll-questions-container-53_yp5a9f5ddd210c9 { font-size:14px; margin:5px 0px; } .yop-poll-question-container-53_yp5a9f5ddd210c9 { padding: 2px; } .yop-poll-question-53_yp5a9f5ddd210c9 { background:#327BD6; color:#fff; margin-bottom: 21px; margin-top: -10px; font-style: italic; text-align: center; width: 100%; padding:5px; } .yop-poll-answers-53_yp5a9f5ddd210c9 { } .yop-poll-answers-53_yp5a9f5ddd210c9 ul { list-style: none outside none; margin: 0; padding: 0; } .yop-poll-li-answer-53_yp5a9f5ddd210c9 { font-style:normal; margin:0px 0px 10px 0px; padding:0px; font-size:12px; margin-bottom:20px; } .yop-poll-li-answer-53_yp5a9f5ddd210c9 input { margin:0px; float:none; } .yop-poll-li-answer-53_yp5a9f5ddd210c9 label { margin:0px; font-style:normal; font-weight:normal; font-size:12px; float:none; } .yop-poll-results-53_yp5a9f5ddd210c9 { font-size: 12px; font-style: italic; font-weight: normal; margin-left: 15px; } .yop-poll-customs-53_yp5a9f5ddd210c9 { } .yop-poll-customs-53_yp5a9f5ddd210c9 ul { list-style: none outside none; margin: 0; padding: 0; } .yop-poll-li-custom-53_yp5a9f5ddd210c9 { padding:0px; margin:0px; font-size:14px; } /* Start CAPTCHA div style*/ #yop-poll-captcha-input-div-53_yp5a9f5ddd210c9 { margin-top:5px; } #yop-poll-captcha-helpers-div-53_yp5a9f5ddd210c9 { width:30px; float:left; margin-left:5px; height:0px; } #yop-poll-captcha-helpers-div-53_yp5a9f5ddd210c9 img { margin-bottom:2px; } #yop-poll-captcha-image-div-53_yp5a9f5ddd210c9 { margin-bottom:5px; } #yop_poll_captcha_image_53_yp5a9f5ddd210c9 { float:left; } /* End CAPTCHA div style*/ .yop-poll-clear-53_yp5a9f5ddd210c9 { clear:both; } #yop-poll-vote-53_yp5a9f5ddd210c9 { } /* Start Result bar*/ .yop-poll-results-bar-53_yp5a9f5ddd210c9 { background:#f5f5f5; height:10px; } .yop-poll-results-bar-53_yp5a9f5ddd210c9 div { background:#555; height:10px; } /* End Result bar*/ /* Start Vote Button*/ #yop-poll-vote-53_yp5a9f5ddd210c9 div#yop-poll-vote-53_yp5a9f5ddd210c9 button { float:left; } #yop-poll-vote-53_yp5a9f5ddd210c9 div#yop-poll-results-53_yp5a9f5ddd210c9 { float: right; margin-bottom: 20px; margin-top: -20px; width: auto; } #yop-poll-vote-53_yp5a9f5ddd210c9 div#yop-poll-results-53_yp5a9f5ddd210c9 a { color:#fff; text-decoration:underline; font-size:12px; } #yop-poll-vote-53_yp5a9f5ddd210c9 div#yop-poll-back-53_yp5a9f5ddd210c9 a { color:#555; text-decoration:underline; font-size:12px; } #yop-poll-vote-53_yp5a9f5ddd210c9 div#yop-poll-archive-53_yp5a9f5ddd210c9 a { color:#555; text-decoration:underline; font-size:12px; } #yop-poll-vote-53_yp5a9f5ddd210c9 div { float:left; width:100%; } /* End Vote Button*/ /* Start Messages*/ #yop-poll-container-error-53_yp5a9f5ddd210c9 { font-size:12px; font-style:italic; color:red; text-transform:lowercase; margin-bottom:20px; text-align:center; } #yop-poll-container-success-53_yp5a9f5ddd210c9 { font-size:12px; font-style:italic; color:green; margin-bottom:20px; text-align:center; } /* End Messages*/#yop-poll-container-53_yp5a9f5ddd210c9 img { max-width: 1000; } .yop-poll-forms-display{}
When you are not coding for work/school, in your free time do you ...
  • Eat. Sleep. Code. Repeat. Coding is all that matters.
  • Gotta be well rounded. Get outside. Ride a bike. Climb a mountain. Hike a trail.
  • Netflix. Gotta binge-watch *everything*.
  • Rocket League! Or [INSERT VIDEO GAME TITLE HERE]

News

  • We take a moment to appreciate and thank those that left us a review:
    • iTunes : Srolesen
    • Stitcher: JackOfAllPythons
  • The 5 worst programming languages to learn in 2018 – TechRepublic
  • In the Orlando area March 17th? Come see Joe speak at the Orlando Code Camp. Head to https://www.orlandocodecamp.com/ for more information.
  • Care to help us out? Visit our sponsors and affiliate resources.

main.c

Presenters and Humble Objects

  • Humble Object Pattern – strip all the hard to test portions of an app into separate classes.
    • Humble = harder to test.
      • Things like the UI.
    • View object is the transport object that isn’t tested.
    • Presenter object is what takes data from the application (business tiers), and formats it and passes it to the view.
    • Application –> Presenter –> ViewModel –> View
    • ViewModel will have a mapping of all fields / properties that can be modified in the UI and that data will be filled in by the Presenter.
  • Testing is made possible by good architecture.
  • The separation of behaviors into those that can be tested and those that can’t is (often) a boundary.

Database Gateways

  • Contains all the interfaces for the CRUD operations to be implemented.
  • The gateways sit between the use cases and the database.
    • NO SQL LIVES HERE – these interfaces are to be implemented by the appropriate classes – database implementations, etc.

Data Mappers

  • No such things as ORMs.
    • Objects are not data structures from the user’s point of view.
      • The users can only see the exposed behaviors.
  • A data structure has no implied behavior, which is what an ORM creates.
  • ORMs belong in the database layer – the layer that implements the Database Gateway Interfaces.
  • ORMs are a type of humble object boundary between the gateway interfaces and the database.

Service Listeners

  • Also implements the humble object patterns.
  • You’ll basically have data transformation proxy classes set up to take data from the application, format it and pass it over to an external service.
  • Same basic principle for incoming data from a service – it’ll be received, transformed, and passed to the application layer.

Partial Boundaries

  • Full blown architectural boundaries are expensive!!!
    • Must implement polymorphic interfaces both ways.
    • Must have sets of input and output classes.
    • Dependency management for both sides.
  • Expensive to build and work to maintain.
  • Sometimes, rather than do the full on implementation, you make a decision to do a Partial Boundary.
    • One way to do this is to keep everything in a single component – the inputs, outputs, interfaces, etc.
      • What does this buy you? Simpler build pipeline, no version number tracking for interoperability, multiple component releases, etc.
  • Full on implementation also requires two way reciprocation of the implementation of the boundary interfaces – quite a bit of work.
    • A solution is to use the strategy pattern (selecting an algorithm at runtime).
      • Weekly Dev Tips episode on Strategy Pattern: http://www.weeklydevtips.com/019
      • Client –> Service Boundary Interface –> Service Implementation
      • Only problem with this is someone could easily bypass the interface and just use the Service Implementation.
    • Another solution is the Facade pattern.
      • The facade will call the necessary services when the client makes a call / request.
      • This means that the client is still dependent on all the services and any service changes will require recompiles of the client.
      • There is no inversion of control in this pattern.

Layers and Boundaries

  • Remember that boundaries are expensive, but when ignored, they are *very* expensive to add, even with test cases.
  • So what’s the architect to do? Guess. Wisely.
    • Discover where the boundaries currently are.
    • Weigh the costs of the boundaries: which can be ignored, which should be partially vs fully implemented.
    • Rinse-n-repeat. It is *not* a one time decision.
      • Watch for friction where boundaries don’t exist, then reevaluate.
      • The goal is to implement the boundaries when the cost to implement is less than the cost to ignore.

Main Component

  • The entry point / overseer of everything else.
  • “The Ultimate Detail” – the lowest-level policy.
    • Nothing other than the OS depends on it.
    • Should create all the factories, strategies, global facilities, etc.
    • This is where dependencies should be injected by a DI framework.
  • It’s “the dirtiest of all dirty components”. It’s the outermost circle.
  • Main does all the dirty work.
    • Creates the game engine by passing in the NAME of the class.
    • Sets up all the constants.
    • Creates the input stream and contains the main loop of the game.
    • But it defers all other processing to higher level components.
  • Main is basically a plugin, so you can have many versions of it – Dev, Prod, etc.
    • You _can_. But _should_ you?

Services Great and Small

  • Service Oriented Architectures and Micro Service Architectures:
    • Services _seem_ to be strongly decoupled.
    • And they _seem_ to support independent development and deployment.
    • But are they truly decoupled?
  • Services are not an architecture.
    • Services that only separate application behaviors are not much more than expensive function calls.
  • This doesn’t mean that _all_ services need to be significant.
    • Even when they break the Dependency Rule, there are, at times, benefits to separating the functionality.
    • But this does not define an architecture.

Service Benefits?

  • Are services truly decoupled? – Not really…
    • They are tightly bound by the data they share.
      • If a new field is added to a data structure that services pass to each other, then all those services must be modified.
      • They must all agree on how to use the various fields on the data, so again they’re tightly coupled.
    • Service interfaces are not any more formal, rigorous, or defined than functions.
  • But are they independently developed and deployed?
    • It’s been proven that large enterprise systems can exist in the form of monoliths, services, or component based systems.
    • Because services are often tightly bound to the data they ingest or output, they are still tightly coupled to their dependencies and their deployments must still be coordinated with those external changes.
  • The Kitty Problem…
    • A taxi service turns into a kitty delivery service – how many services would have to change?
    • Cross cutting concerns impact all things – functional, service oriented, whatever.
    • Take care so that new features don’t cut across all of your services.
  • How to fix this? SOLID design principles.
  • Services can also be abstracted away with those same principles / abstractions.

Cross-Cutting Concerns

  • Architectural boundaries don’t run between services but rather through them.
  • Internal pieces of services must follow the Dependency Rule in order to keep them decoupled.
  • The architecture is defined by the boundaries within the system and the dependencies that cross those boundaries.
  • Architecture is not defined by the physical mechanisms of how the code communicates and runs.

Test Boundary

  • Yes, the tests are a part of an architecture.
  • Very detailed and concrete – following the dependency rule – always pointing inward.
  • Think of them as the outermost circle.
  • They are independently deployable and the most isolated system component.
  • NOTHING should depend on them.
    • They support development, not operation.
  • All code should be modeled like tests.
  • Fragile Tests Problem
  • Solution is to design for testability.
    • Don’t depend on the volatile things.
  • Creating a test API:
    • A superset of the interfaces used by the UI.
      • Decouples tests from the application.
    • Rather than writing unit tests against all the production classes, you write tests against the API allowing the primary application to change without worrying about breaking many unit tests.
    • Testing API hides the structure of the application.
      • This goes against a lot of how-to unit philosophies.
    • Testing API should have super-powers – outside the normal powers of a regular system user – therefore it’s probably best if deployed separately so it never ships with your product.

Clean Embedded Systems

  • Software doesn’t wear out, but it can be destroyed by unmanaged dependencies on firmware and hardware.
  • Firmware isn’t firm because of _where_ it is stored; rather what it depends on and how hard it is to change as the hardware changes.
  • “Although software does not wear out, it can be destroyed from within by unmanaged dependencies on firmware and hardware.”
  • Non-embedded developers also create firmware when they embed SQL statements in your code.
  • Make it work. Then make it better.
    • Only making it work is doing everyone a disservice.
    • There more to software development than just making it work.
  • Intermingling software and firmware is an anti-pattern.
    • Intermingled code “resist” changes.
    • Worst these changes are dangerous and require full regression tests of the entire system.
    • Target hardware bottleneck – if code is written that will only work on the target hardware, that will simply slow you down and it is usually due to a poor architecture.
  • HAL is the boundary between the software and the firmware.
  • It provides a service without revealing how.
  • You’re doing it right if you can test the software off of the hardware.
  • PAL is the boundary that sits between the firmware and the hardware.
  • Anything that knows about the hardware registers _becomes_ firmware.
  • By implementing a PAL, you can make the firmware less firm and testable off of the hardware.
  • OSAL is the boundary between the software and the OS.
  • Treat the OS as a detail to protect yourself against dependencies.
  • A layered architecture is premised on the idea of programming to interfaces.
    • This allows for substitutability.
  • A clean embedded architecture’s software is testable:
    • Off of the target hardware.
    • Off of the target operating system.
    • Within layers because the modules interface through interfaces.
  • #ifdef statements violate the DRY principle when you have them sprinkled all over your code.
    • Keep those isolated to a single file.
  • Letting all code become firmware is bad.
  • Only being able to test on the target hardware is bad.
  • Following the principles outlined in Clean Architecture is good for your code.

Resources We Like

  • Clean Architecture by Robert C. Martin (Amazon)

Tip of the Week

Episode source