Slide 1

Slide 1 text

BOTTOM-UP ARCHITECTURE BRIDGING THE ARCHITECTURE CODE GAP Oliver Drotbohm  oliver.drotbohm@broadcom.com    odrotbohm

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

Architectural Goal Architectural Concept Supporting Technology Development Practice supports drives selection drives selection enables supports supported by

Slide 4

Slide 4 text

 

Slide 5

Slide 5 text

"Architecture is a property of a system, not a description of its intended design." — Stefan Tilkov in "Good Enough Architecture"

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

Vocabulary Domain Terms Abstraction Pattern Languages Level of Detail Encapsulation Concepts & Rules Extensional Intensional Enumerated Specified

Slide 8

Slide 8 text

Architecturally- Evident Code? !

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

Chunking Hierarchization Pattern languages

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

A simple Aggregate arrangement Orders Customers «Aggregate» Order id: OrderId lineItems: List 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! #  

Slide 13

Slide 13 text

 Tools  Frameworks  User Code  Concepts  Rules Code Architecture Technology

Slide 14

Slide 14 text

      Means of definition

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

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 $

Slide 17

Slide 17 text

@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

Slide 18

Slide 18 text

      Responsibility of definition Means of definition

Slide 19

Slide 19 text

    🕵 Responsibility of definition  

Slide 20

Slide 20 text

  

Slide 21

Slide 21 text

xMolecules

Slide 22

Slide 22 text

jMolecules

Slide 23

Slide 23 text

Architectural Goal Architectural Concept Supporting Technology Development Practice supports drives selection drives selection enables supports supported by Understandability DDD Building Blocks jMolecules

Slide 24

Slide 24 text

 Explicit concepts

Slide 25

Slide 25 text

@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 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?

Slide 26

Slide 26 text

Conceptual Density 1 N N = Number of source code elements needed to determine the architectural role of a piece of code

Slide 27

Slide 27 text

@Entity @NoArgsConstructor(force = true) @EqualsAndHashCode(of = "id") @Table(name = "SAMPLE_ORDER") @Getter public class Order implements o.j.d.t.AggregateRoot { private final @EmbeddedId OrderId id; @OneToMany(cascade = CascadeType.ALL) private List 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; } }

Slide 28

Slide 28 text

Verification

Slide 29

Slide 29 text

Invalid aggregate root reference! Use identifier or Association instead! ☝

Slide 30

Slide 30 text

Eliminate Boilerplate

Slide 31

Slide 31 text

@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 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

Slide 32

Slide 32 text

@Entity @NoArgsConstructor(force = true) @EqualsAndHashCode(of = "id") @Table(name = "SAMPLE_ORDER") @Getter public class Order implements AggregateRoot { private final @EmbeddedId OrderId id; @OneToMany(cascade = CascadeType.ALL) private List lineItems; private Association 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; } }

Slide 33

Slide 33 text

@Entity @NoArgsConstructor(force = true) @EqualsAndHashCode(of = "id") @Table(name = "SAMPLE_ORDER") @Getter public class Order implements AggregateRoot { private final @EmbeddedId OrderId id; @OneToMany(cascade = CascadeType.ALL) private List lineItems; private Association 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; } }

Slide 34

Slide 34 text

[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. [INFO] └─ Spring JPA - customer - Adding @j.p.Convert(converter=…). Meanwhile in your IDE…

Slide 35

Slide 35 text

@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 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 { private final OrderId id; private List lineItems; private Association customer; public Order(CustomerId customerId) { this.id = OrderId.of(UUID.randomUUID()); this.customer = Association.forId(customerId); } record OrderId(UUID orderId) implements Identifier {} }

Slide 36

Slide 36 text

 Decomposition

Slide 37

Slide 37 text

A B

Slide 38

Slide 38 text

A B & Risk of Change Scope of Change?

Slide 39

Slide 39 text

Infrastructure Application Domain

Slide 40

Slide 40 text

  ✉  Application Domain Infrastructure

Slide 41

Slide 41 text

  ✉  Application Domain Infrastructure

Slide 42

Slide 42 text

  ✉  Domain Application   ✉  Infrastructure Domain Application   ✉  Infrastructure Domain Application Infrastructure

Slide 43

Slide 43 text

      ✉  ✉  ✉  Infrastructure Infrastructure Domain Domain Domain Application Application Application Infrastructure

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

Module Module Module         ✉  ✉  ✉   

Slide 46

Slide 46 text

Architectural Goal Architectural Concept Supporting Technology Development Practice supports drives selection drives selection enables supports supported by Evolvability Modules Spring Modulith

Slide 47

Slide 47 text

Spring Modulith

Slide 48

Slide 48 text

package com.acme.modulith @SpringBootApplication class MyApplication { … } Standard Spring Boot Application

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

Verification

Slide 51

Slide 51 text

package com.acme.modulith @SpringBootApplication class MyApplication { … } var modules = ApplicationModules.of(MyApplication.class); modules.verify(…); Standard Spring Boot Application Verifies rules for MyApplication

Slide 52

Slide 52 text

Web Business logic Data access Module A Module B Module C

Slide 53

Slide 53 text

Web Business logic Data access Module A Module B Module C

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

🧪 Testing

Slide 56

Slide 56 text

Web Business logic Data access Module A Module B Module C @Data…Test @WebMvcTest

Slide 57

Slide 57 text

Web Business logic Data access Module A Module B Module C @Data…Test @WebMvcTest @ApplicationModuleTest

Slide 58

Slide 58 text

Subcutaneous Tests

Slide 59

Slide 59 text

Module   ✉  Module    ✉   Module    ✉  

Slide 60

Slide 60 text

Stimulate the module by… …invoking a business operation …publishing an event listened to Module    ✉  

Slide 61

Slide 61 text

@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

Slide 62

Slide 62 text

Scope of Individual Tests

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

Scope of Test Execution

Slide 67

Slide 67 text

A B C D E Change detected here. We only need to test A!

Slide 68

Slide 68 text

A B C D E Change detected in C. We need to test C and A!

Slide 69

Slide 69 text

No content

Slide 70

Slide 70 text

Documentation

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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)

Slide 75

Slide 75 text

Summary

Slide 76

Slide 76 text

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.

Slide 77

Slide 77 text

Thank you! Questions? Oliver Drotbohm  oliver.drotbohm@broadcom.com    odrotbohm

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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