Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Let's build components, not layers

Let's build components, not layers

Code is written once and read often. So we should focus on making it easily readable. We all know that.

The same is true for the architecture of an application. It’s usually created once and then read and re-read, understood and misunderstood, interpreted and misinterpreted a lot of times by a lot of people during its lifetime.

We try to get a grip on this problem by using architectural patterns like layers. While layers are a well-understood structure, they only help in understanding the big picture of the application’s architecture. They usually don’t do a good job of organizing the code into pieces that are easy to understand and reason about.

Usually, a better approach to structure the code is to build components. Like lego bricks, we can compose multiple (vertical and horizontal) components to a bigger component, until we have built a complete application. We can zoom in and out of the codebase along the seams of the components to understand what we need to understand at the level of abstraction that we are currently concerned with.

In this talk, we’ll explore a lightweight way of structuring the codebase of a Spring Boot application that will feel like building lego. This component-based structure is easy to understand and reason about. It’s also easy to maintain over time thanks to a single rule that can be enforced by tooling. Java, Spring Boot, and ArchUnit bring all the tools we need for a nice component-based codebase, we just need to use them.

Tom Hombergs

May 26, 2022
Tweet

More Decks by Tom Hombergs

Other Decks in Programming

Transcript

  1. TOM HOMBERGS | SENIOR SOFTWARE ENGINEER | @TOMHOMBERGS Let’s build

    components, not layers Component-based architecture with Spring
  2. Software is over- engineered Software is under- engineered Software is

    over- engineered Software is under- engineered
  3. Software is over- engineered Software is under- engineered (at the

    code level) (at the system level) Software is over- engineered Software is under- engineered
  4. Software is over- engineered Software is under- engineered MONOLITH MICROSERVICES

    Software is over- engineered Software is under- engineered (at the code level) (at the system level)
  5. How can I organise my code to support a modular

    monolith*? LEAD QUESTION * monolith = a deployment unit that covers more than one bounded context
  6. Tom Hombergs “You can’t just keep it simple. Make it

    simple, then it’s easy.” re fl ectoring.io
  7. Agenda Why organise code? What’s wrong with layers? What’s wrong

    with verticals? Clean / Hexagonal architecture? Component-based architecture! Field study
  8. Where is this feature in the code? How can I

    replace this part of the code? What is this part of the code doing? How can I move this feature from this codebase to another? What can break if I modify this part of the code? Where do I add this new feature to the code? Who is using this part of the code? How does this code fit into the overall architecture?
  9. Goals of code organisation Understandability Easy to navigate the codebase.

    Get onboarded quicker. And more … What do you want from good code organisation? Maintainability Easy to keep the codebase in a good shape. Evolvability Easy to add to / remove from / modify the codebase.
  10. Agenda Why organise code? What’s wrong with layers? What’s wrong

    with verticals? Clean / Hexagonal architecture? Component-based architecture! Field study
  11. What’s wrong with layers? Blurred boundaries Mental capacity Horizontal dependencies

    everywhere The only rule is that dependencies may not go to a layer above. This means that a lot of horizontal dependencies creep in over time. The business use cases are hidden Use cases are often hidden in very broad services within the business layer and are hard to fi nd and reason about.
  12. What’s wrong with layers? Architecture / code gap A codebase

    with 3 layers has 3 high-level components to reason about. The actual architecture is more complex and not evident from looking at the code. We have to do mental mapping continuously. A layer is doing too much We can’t grasp what a layer is doing because it’s too broad. Our brain can only process a couple of concepts at the same time (my brain, at least). Blurred boundaries Mental capacity
  13. Agenda Why organise code? What’s wrong with layers? What’s wrong

    with verticals? Clean / Hexagonal architecture? Component-based architecture! Field study
  14. What’s wrong with slices? Slices have a large surface area

    Slices don’t have a dedicated API, making all dependencies to other slices fair game. Limited evolvability Large surface area
  15. What’s wrong with slices? Limited evolvability Large surface area Replacing

    or removing a slice is risky Moving a slice out of the codebase is hard due to accidental dependencies.
  16. Agenda Why organise code? What’s wrong with layers? What’s wrong

    with verticals? Clean / Hexagonal architecture? Component-based architecture! Field study
  17. We often don’t have a rich domain model! It’s great

    for a rich domain model CLEAN / HEXAGONAL ARCHITECTURE
  18. Agenda Why organise code? What’s wrong with layers? What’s wrong

    with verticals? Clean / Hexagonal architecture? Component-based architecture! Field study
  19. Namespace Each component has a unique namespace. API Each component

    has a dedicated API package. Internal Each component has a dedicated “internal” package. Nesting It’s components all the way down. Component rules
  20. API Each component has a dedicated API package. Internal Each

    component has a dedicated “internal” package.
  21. Agenda Why organise code? What’s wrong with layers? What’s wrong

    with verticals? Clean / Hexagonal architecture? Component-based architecture! Field study
  22. Schedule checks Checks should be queued Execute checks Dequeues and

    executes checks Store check results Stores check results in the database Retrieve check results Provides an API to retrieve check results Building a “check engine” component
  23. Sub-component for database storage Store check results Stores check results

    in the database Retrieve check results Provides an API to retrieve check results
  24. Con fi guration to load all Spring beans of the

    top- level component into the application context
  25. No API package because the API is exposed by the

    parent component! Schedule checks Checks should be queued
  26. Con fi guration to load all Spring beans of the

    nested component into the application context Schedule checks Checks should be queued
  27. Internal API to store check results implements calls Store check

    results Stores check results in the database
  28. Why build components? Maintainability Understand- ability Evolvability Easily bridge the

    architecture / code gap Minimal mental mapping from the architecture to the code and back.
  29. Why build components? Maintainability Understand- ability Evolvability Easily enforce dependencies

    One simple dependency rule: no access to an internal package from outside of that package.
  30. Why build components? Maintainability Understand- ability Evolvability Easily move code

    around Thanks to narrow API surfaces and clear dependencies, code is easy to move around within the codebase or to another codebase.