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.

Bc7aa56e8e004e4984e954e5e4cb2bc0?s=128

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. None
  7. None
  8. Tom Hombergs “You can’t just keep it simple. Make it

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

    with verticals? Clean / Hexagonal architecture? Component-based architecture! Field study
  10. 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?
  11. 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.
  12. Agenda Why organise code? What’s wrong with layers? What’s wrong

    with verticals? Clean / Hexagonal architecture? Component-based architecture! Field study
  13. None
  14. 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.
  15. 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
  16. None
  17. Agenda Why organise code? What’s wrong with layers? What’s wrong

    with verticals? Clean / Hexagonal architecture? Component-based architecture! Field study
  18. None
  19. 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
  20. 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.
  21. None
  22. None
  23. None
  24. Agenda Why organise code? What’s wrong with layers? What’s wrong

    with verticals? Clean / Hexagonal architecture? Component-based architecture! Field study
  25. Clean Architecture

  26. Hexagonal Architecture

  27. It’s great for a rich domain model CLEAN / HEXAGONAL

    ARCHITECTURE
  28. We often don’t have a rich domain model! It’s great

    for a rich domain model CLEAN / HEXAGONAL ARCHITECTURE
  29. Everybody interprets it differently CLEAN / HEXAGONAL ARCHITECTURE

  30. We need simplicity! Everybody interprets it differently CLEAN / HEXAGONAL

    ARCHITECTURE
  31. Is there a simple architecture without the overhead?

  32. Agenda Why organise code? What’s wrong with layers? What’s wrong

    with verticals? Clean / Hexagonal architecture? Component-based architecture! Field study
  33. COMPONENTS

  34. COMPONENTS

  35. 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
  36. Namespace Each component has a unique namespace.

  37. Namespace Each component has a unique namespace.

  38. Namespace Each component has a unique namespace.

  39. API Each component has a dedicated API package. Internal Each

    component has a dedicated “internal” package.
  40. Nesting It’s components all the way down.

  41. Nesting It’s components all the way down.

  42. Nesting It’s components all the way down.

  43. Nesting It’s components all the way down. implements

  44. Nesting It’s components all the way down.

  45. Nesting It’s components all the way down. implements

  46. Agenda Why organise code? What’s wrong with layers? What’s wrong

    with verticals? Clean / Hexagonal architecture? Component-based architecture! Field study
  47. 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
  48. Building a “check engine” component Execute checks Store check results

    Retrieve check results Schedule checks
  49. Namespace

  50. API & Internal packages

  51. API to schedule checks Schedule checks Checks should be queued

  52. API to query check results Retrieve check results Provides an

    API to retrieve check results
  53. Sub-component for running checks Execute checks Dequeues and executes checks

  54. Sub-component for database storage Store check results Stores check results

    in the database Retrieve check results Provides an API to retrieve check results
  55. Sub-component for queueing and dequeueing checks Schedule checks Checks should

    be queued
  56. Con fi guration to load all Spring beans of the

    top- level component into the application context
  57. Auto-load the con fi guration when “check-engine” component is in

    the classpath
  58. External API to schedule a check Schedule checks Checks should

    be queued implements
  59. No API package because the API is exposed by the

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

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

    results Stores check results in the database
  62. Internal API to run checks calls Execute checks Dequeues and

    executes checks implements
  63. External API to retrieve check results implements Retrieve check results

    Provides an API to retrieve check results
  64. Why build components? Maintainability Understand- ability Evolvability Easily bridge the

    architecture / code gap Minimal mental mapping from the architecture to the code and back.
  65. 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.
  66. 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.
  67. None
  68. re fl ectoring.io/spring-io re fl ectoring.io/java-components-clean-boundaries/ Thank you for your

    patience!
  69. None