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

Bottom-Up Architecture – Bridging the Achitectu...

Bottom-Up Architecture – Bridging the Achitecture Code Gap

Hard to change code bases often suffer from two primary problems: a lack of alignment with domain boundaries and failure to effectively express architectural ideas in code. To address that critical issue, developers have turned to Separation of Concerns Architectures, such as Onion-, Clean and Hexagonal Architecture. However, these approaches typically yield mixed results, as they primarily focus on separating technical and business code, without addressing the structural aspects of the domain.

This presentation explores strategies for transferring architectural ideas and design pattern languages into code at various levels of abstraction. We will discuss how different frameworks and libraries in the Java ecosystem can aid in this process, leveraging the presence of meta-information within the code to support critical aspects such as structural verification, testability, and documentation. By employing these approaches and tools, developers can write more maintainable code that is less susceptible to degradation over time.

Version:
- Software Architecture Alliance (Keynote, current) - https://static.odrotbohm.de/slides/2024/10/bottom-up-architecture-saa.pdf
- Java Forum Nord – https://static.odrotbohm.de/slides/2024/09/bottom-up-architecture-jfn.pdf
- GOTO Amsterdam 2024 – https://static.odrotbohm.de/slides/2024/06/bottom-up-architecture-goto-ams.pdf

Oliver Drotbohm

June 11, 2024
Tweet

More Decks by Oliver Drotbohm

Other Decks in Programming

Transcript

  1. "Architecture is a property of a system, not a description

    of its intended design." — Stefan Tilkov in "Good Enough Architecture"
  2. Vocabulary Domain Terms Abstraction Pattern Languages Level of Detail Encapsulation

    Concepts & Rules Extensional Intensional Enumerated Specified
  3. Deployables / Build modules / Packages Classes, methods, fields 

      Extensional Intensional Naming conventions What else? " Invoicing, Shipment Components / Modules EmailAddress, ZipCode Domain language Concepts ValueObject, Entity, Aggregate Layers, Rings Concepts & Rules
  4. A simple Aggregate arrangement Orders Customers «Aggregate» Order id: OrderId

    lineItems: List<LineItem> customer: Customer «Entity» LineItem amount: int price: MonetaryAmount «Aggregate» Customer id: CustomerId contains 1 1..* belongs to * 1 This and that imply that this is wrong! #  
  5.  Tools  Frameworks  User Code  Concepts 

    Rules Code Architecture Technology
  6. MATCH (repo:Java:Type) -[:IMPLEMENTS_GENERIC]-# (superType) -[:OF_RAW_TYPE]-# (:Java:Type { fqn: "o.s.d.r.Repository"}), (superType)

    -[:HAS_ACTUAL_TYPE_ARGUMENT { index: 0 }]-# () -[:OF_RAW_TYPE]-# (aggregateType) SET aggregateType:Aggregate RETURN repo, aggregateType Establishing an Aggregate… in jQAssistant MATCH (aggregate:Aggregate) -[:DECLARES]-# (f:Field) -[:OF_TYPE]-# (fieldType:Aggregate) WHERE aggregate <& fieldType RETURN aggregate, fieldType Establishes the concept Establishes the rule Reference to tech stack $
  7. @AnalyzeClasses(packagesOf = Application.class) public class ArchitectureTest { @ArchTest void verifyAggregates(JavaClasses

    types) { var aggregates = new AggregatesExtractor(); var aggregateTypes = aggregates.doTransform(types); all(aggregates) .should(notReferToOtherAggregates(aggregateTypes)) .check(types); } } Establishing an Aggregate… in ArchUnit Establishes the concept Establishes the rule
  8. Architectural Goal Architectural Concept Supporting Technology Development Practice supports drives

    selection drives selection enables supports supported by Understandability DDD Building Blocks jMolecules
  9. @Entity @NoArgsConstructor(force = true) @EqualsAndHashCode(of = "id") @Table(name = "SAMPLE_ORDER")

    @Getter public class Order { private final @EmbeddedId OrderId id; @OneToMany(cascade = CascadeType.ALL) private List<LineItem> lineItems; private CustomerId customerId; public Order(CustomerId customerId) { this.id = OrderId.of(UUID.randomUUID()); this.customerId = customerId; } @Value @RequiredArgsConstructor(staticName = "of") @NoArgsConstructor(force = true) public static class OrderId implements Serializable { private static final long serialVersionUID = …; private final UUID orderId; } } What is the role of this class in the overall arrangement?
  10. Conceptual Density 1 N N = Number of source code

    elements needed to determine the architectural role of a piece of code
  11. @Entity @NoArgsConstructor(force = true) @EqualsAndHashCode(of = "id") @Table(name = "SAMPLE_ORDER")

    @Getter public class Order implements o.j.d.t.AggregateRoot<Order, OrderId> { private final @EmbeddedId OrderId id; @OneToMany(cascade = CascadeType.ALL) private List<LineItem> lineItems; private CustomerId customerId; public Order(CustomerId customerId) { this.id = OrderId.of(UUID.randomUUID()); this.customerId = customerId; } @Value @RequiredArgsConstructor(staticName = "of") @NoArgsConstructor(force = true) public static class OrderId implements o.j.d.t.Identifier { private static final long serialVersionUID = …; private final UUID orderId; } }
  12. @Entity @NoArgsConstructor(force = true) @EqualsAndHashCode(of = "id") @Table(name = "SAMPLE_ORDER")

    @Getter public class Order { private final @EmbeddedId OrderId id; @OneToMany(cascade = CascadeType.ALL) private List<LineItem> lineItems; private CustomerId customerId; public Order(CustomerId customerId) { this.id = OrderId.of(UUID.randomUUID()); this.customerId = customerId; } @Value @RequiredArgsConstructor(staticName = "of") @NoArgsConstructor(force = true) public static class OrderId implements Serializable { private static final long serialVersionUID = …; private final UUID orderId; } } JPA-induced boilerplate Model characteristics expressed implicitly or through technical means
  13. @Entity @NoArgsConstructor(force = true) @EqualsAndHashCode(of = "id") @Table(name = "SAMPLE_ORDER")

    @Getter public class Order implements AggregateRoot<Order, OrderId> { private final @EmbeddedId OrderId id; @OneToMany(cascade = CascadeType.ALL) private List<LineItem> lineItems; private Association<Customer, CustomerId> customer; public Order(CustomerId customerId) { this.id = OrderId.of(UUID.randomUUID()); this.customer = Association.forId(customerId); } @Value @RequiredArgsConstructor(staticName = "of") @NoArgsConstructor(force = true) public static class OrderId implements Identifier { private static final long serialVersionUID = …; private final UUID orderId; } }
  14. @Entity @NoArgsConstructor(force = true) @EqualsAndHashCode(of = "id") @Table(name = "SAMPLE_ORDER")

    @Getter public class Order implements AggregateRoot<Order, OrderId> { private final @EmbeddedId OrderId id; @OneToMany(cascade = CascadeType.ALL) private List<LineItem> lineItems; private Association<Customer, CustomerId> customer; public Order(CustomerId customerId) { this.id = OrderId.of(UUID.randomUUID()); this.customer = Association.forId(customerId); } @Value @RequiredArgsConstructor(staticName = "of") @NoArgsConstructor(force = true) public static class OrderId implements Identifier { private static final long serialVersionUID = …; private final UUID orderId; } }
  15. [INFO] □─ example.order.Order [INFO] ├─ JPA - Adding @j.p.Entity. [INFO]

    ├─ JPA - Adding default constructor. [INFO] ├─ JPA - Adding nullability verification using new callback methods. [INFO] ├─ JPA - Defaulting id mapping to @j.p.EmbeddedId(). [INFO] ├─ JPA - Defaulting lineItems mapping to @j.p.JoinColumn(…). [INFO] ├─ JPA - Defaulting lineItems mapping to @j.p.OneToMany(…). [INFO] ├─ Spring Data JPA - Implementing o.s.d.d.Persistable<e.o.Order$OrderIdentifier>. [INFO] └─ Spring JPA - customer - Adding @j.p.Convert(converter=…). Meanwhile in your IDE…
  16. @Entity @NoArgsConstructor(force = true) @EqualsAndHashcode(of = "id") @Table(name = "SAMPLE_ORDER")

    @Getter public class Order { private final @EmbeddedId OrderId id; @OneToMany(cascade = CascadeType.ALL) private List<LineItem> lineItems; private CustomerId customerId; public Order(Customer customer) { this.id = OrderId.of(UUID.randomUUID()); this.customerId = customer.getId(); } @Value @RequiredArgsConstructor(staticName = "of") @NoArgsConstructor(force = true) public static class OrderId implements Serializable { private static final long serialVersionUID = …; private final UUID orderId; } } @Table(name = "SAMPLE_ORDER") @Getter public class Order implements AggregateRoot<Order, OrderId> { private final OrderId id; private List<LineItem> lineItems; private Association<Customer, CustomerId> customer; public Order(CustomerId customerId) { this.id = OrderId.of(UUID.randomUUID()); this.customer = Association.forId(customerId); } record OrderId(UUID orderId) implements Identifier {} }
  17. A B

  18.   ✉  Domain Application   ✉ 

    Infrastructure Domain Application   ✉  Infrastructure Domain Application Infrastructure
  19.       ✉  ✉ 

    ✉  Infrastructure Infrastructure Domain Domain Domain Application Application Application Infrastructure
  20.         ✉ 

    ✉  ✉    Infrastructure Infrastructure Domain Domain Domain Application Application Application Infrastructure
  21. Module Module Module       

     ✉  ✉  ✉   
  22. Architectural Goal Architectural Concept Supporting Technology Development Practice supports drives

    selection drives selection enables supports supported by Evolvability Modules Spring Modulith
  23. Package Conventions  ….modulith  ….modulith.moduleA  ….modulith.moduleA.internal  ….modulith.moduleB

     ….modulith.moduleB.internal Access to components residing in internal packages forbidden and checked during tests. API packages
  24. package com.acme.modulith @SpringBootApplication class MyApplication { … } var modules

    = ApplicationModules.of(MyApplication.class); modules.verify(…); Standard Spring Boot Application Verifies rules for MyApplication
  25. A B C D E Unit of… - Understanding -

    Consistency - Testing - Documentation - Observation
  26. Web Business logic Data access Module A Module B Module

    C @Data…Test @WebMvcTest @ApplicationModuleTest
  27. Module   ✉  Module    ✉

      Module    ✉  
  28. Stimulate the module by… …invoking a business operation …publishing an

    event listened to Module    ✉  
  29. @ApplicationModuleTest @RequiredArgsConstructor class OrderIntegrationTests { private final OrderManagement orders; @Test

    void completionCausesEventPublished(Scenario scenario) { var order = new Order(new CustomerIdentifier(UUID.randomUUID())); scenario.stimulate(() -# orders.complete(order)) .andWaitForEventOfType(OrderCompleted.class) .matchingMappedValue(OrderCompleted:%id, order.getId()) .toArrive(); } } Test scoped to the module Public API primary interaction target Stimulate module Given When Then
  30. A B C D E Unit of… - Understanding -

    Consistency - Testing - Documentation - Observation
  31. A B C D E Unit of… - Understanding -

    Consistency - Testing - Documentation - Observation
  32. A B C D E Unit of… - Understanding -

    Consistency - Testing - Documentation - Observation
  33. A B C D E Change detected in C. We

    need to test C and A!
  34. A B C D E Unit of… - Understanding -

    Consistency - Testing - Documentation - Observation
  35. Salespoint «Component: Module» Salespoint :: Accountancy «Component: Module» Salespoint ::

    Catalog «Component: Module» Salespoint :: Inventory «Component: Module» Salespoint :: Order «Component: Module» Salespoint :: Payment «Component: Module» Salespoint :: Quantity «Component: Module» Salespoint :: Storage «Component: Module» Salespoint :: Time «Component: Module» Salespoint :: User Account listens t o depends on uses depends on depends on depends on listens t o depends on depends on depends on depends on uses depends on depends on
  36. Salespoint «Component: Module» Salespoint :: Accountancy «Component: Module» Salespoint ::

    Catalog «Component: Module» Salespoint :: Inventory «Component: Module» Salespoint :: Order «Component: Module» Salespoint :: Payment «Component: Module» Salespoint :: Quantity «Component: Module» Salespoint :: Storage «Component: Module» Salespoint :: Time «Component: Module» Salespoint :: User Account listens t o depends on uses depends on depends on depends on listens t o depends on depends on depends on depends on uses depends on depends on Salespoint :: Inventory «Component: Module» Salespoint :: Catalog «Component: Module» Salespoint :: Inventory «Component: Module» Salespoint :: Order «Component: Module» Salespoint :: Quantity depends on depends on listens t o depends on depends on depends on
  37. Salespoint «Component: Module» Salespoint :: Accountancy «Component: Module» Salespoint ::

    Catalog «Component: Module» Salespoint :: Inventory «Component: Module» Salespoint :: Order «Component: Module» Salespoint :: Payment «Component: Module» Salespoint :: Quantity «Component: Module» Salespoint :: Storage «Component: Module» Salespoint :: Time «Component: Module» Salespoint :: User Account listens t o depends on uses depends on depends on depends on listens t o depends on depends on depends on depends on uses depends on depends on Salespoint :: Inventory «Component: Module» Salespoint :: Catalog «Component: Module» Salespoint :: Inventory «Component: Module» Salespoint :: Order «Component: Module» Salespoint :: Quantity depends on depends on listens t o depends on depends on depends on Stereotypes present in the code base (via jMolecules)
  38. Summary  To strengthen the relationship between architecture and code,

    find means to represent architectural and design concepts in your codebase.  Align software engineering practices with architectural abstractions.  Understand how technology choices affect the overall architecture of the system.  The scope of change drives decomposition.
  39. Resources  Just Enough Software Architecture George Fairbanks – Book

     Architecture, Design, Implementation Ammon H. Eden, Rick Kazman – Paper  Sustainable Software Architecture Carola Lilienthal – Book  Software Architecture for Developers Simon Brown – Books
  40. Links  xMolecules Project website  jMolecules Project website 

    jMolecules Examples Example project @ GitHub  Spring Modulith Project website  Archictecturally Evident Spring Example project @ GitHub