Slide 1

Slide 1 text

Modulithic Applications
 with Spring Boot Building better monoliths @olivergierke Oliver Drotbohm ƀ odrotbohm@pivotal.io odrotbohm

Slide 2

Slide 2 text

2

Slide 3

Slide 3 text

Monolith VS. Microservice 3 Module
 A Module
 B Module
 C Module
 B Module
 C Module
 A Bounded Context Deployment Unit Internal invocation External invocation

Slide 4

Slide 4 text

Monolith 4 The application packages the implementation of multiple Bounded Contexts as a single deployment unit. Easy to refactor Simple IDE features can be used to move significant parts of the codebase around and update all client code at the same time. Easy to test the overall system An overall test run is a matter of running a Spring (Boot) integration test.

Slide 5

Slide 5 text

Monolith 5 The application packages the implementation of multiple Bounded Contexts as a single deployment unit. Likely to degrade unless explicitly managed Unless explicit means of monitoring the structure are employed, the architecture will degrade over time. Harder to test individual Bounded Contexts Testing individual bounded contexts requires extra effort in setting up Spring configuration to make sure components can be picked up for each module individually.

Slide 6

Slide 6 text

Microservices 6 A bounded context defines the boundaries of the deployment artifact. Often parts of the context are even deployed as separate processes. ○ Testing individual modules Testing individual modules usually requires collaborating systems to be bootstrapped, stubbed or mocked. ○ Bounded Context interaction is remote The interaction between BCs involved remote interaction, either by sending messages or invoke other systems directly. This accumulates latency, and requires additional measures against the collaborators being unavailable which increases complexity.

Slide 7

Slide 7 text

Microservices 7 A bounded context defines the boundaries of the deployment artifact. Often parts of the context are even deployed as separate processes. Hard to refactor context boundaries Context boundaries are implemented as published APIs that have to be evolved carefully as clients cannot be updated to server changes immediately. Hard to test the overall system While testing an individual system usually works reasonably well, integration testing multiple systems together is complex and creates additional overhead.

Slide 8

Slide 8 text

How to preserve the structure in a
 monolithic application? “ 8

Slide 9

Slide 9 text

Limit active Bounded Context interaction! “ 9

Slide 10

Slide 10 text

10 Orders Business
 Time Catalog Inventory Accountancy User
 Accounts

Slide 11

Slide 11 text

11 @Component class OrderManagement { @Transactional void completeOrder(Order order) { order.complete(); this.orders.save(order); this.inventory.updateStockFor(order); } }

Slide 12

Slide 12 text

12 @Component class Inventory { void updateStockFor(Order order) { order.getLineItems().foreach(it -> { … }); } }

Slide 13

Slide 13 text

13 Orders Business
 Time Catalog Inventory Accountancy User
 Accounts

Slide 14

Slide 14 text

14 Orders Business
 Time Catalog Inventory Accountancy User
 Accounts 
 Event 
 Event

Slide 15

Slide 15 text

15 Orders Business
 Time Catalog Inventory Accountancy User
 Accounts 
 Event 
 Event

Slide 16

Slide 16 text

DEMO 16

Slide 17

Slide 17 text

How to preserve the structure in a
 monolithic application? “ 17

Slide 18

Slide 18 text

Define and monitor structure violations! “ 18

Slide 19

Slide 19 text

Singular Artifact 19 Package per module. Usage of Java package scope to hide component internals. Package per module is a simple convention Does not need explicit definition. Easy to pick up and remember. Package scope to hide components: the compiler becomes the simplest possible architecture management tool. Easy to refactor Changes in bounded context can be fully applied by using the IDE refactoring tools.

Slide 20

Slide 20 text

Singular Artifact 20 Package per module. Usage of Java package scope to hide component internals. Monolithic bootstrap Integration tests usually start the entire application. More control can be gained by more fine-grained Spring configuration. Layers can be tests using Spring Boot’s test annotations. More complex package setup breaks control Once sub-packages are introduced access control becomes a lot harder as types need to become public to be consumable from other packages of the same component.

Slide 21

Slide 21 text

Multiple Artifacts 21 Every module port becomes a dedicated build artifact. Dedicated control over dependencies Each artifact defines it’s dependencies and thus controls which other artifacts are visible at compile time. Test scope is defined by the artifact tests don’t need to run the entire application but need to select the artifacts on the test runtime classpath.

Slide 22

Slide 22 text

Multiple Artifacts 22 Every module port becomes a dedicated build artifact. Potential explosion of number of artifacts Bounded Context with more than a simple API / implementation structure cause the project to consist of a lot of artifacts which increases build setup complexity. Artifacts still available for consumption Implementation artifacts are still available for others to depend on. No separation of logical module and internal structure.

Slide 23

Slide 23 text

Java Module System 23 Every module becomes a Java 9+ module. ○ Strong coupling between module and artifact JPMS assumes a single module with a single logical public API per artifact and thus cannot be used to imply rules within an artifact. With multiple APIs you’re essentially back to „Multiple Artifacts“. ○ Semantics of the „opens“ keyword Required to let frameworks instantiate non-public types via reflection. Creates incentives to create „packages that require reflection“. ○ No test support There’s no concept of running one or more modules in a single test run.

Slide 24

Slide 24 text

External Tools 24 External tools (e.g. jQAssistant, Sonargraph, jDepend) integrated into the build and executed in the CI environment are used to signal structural violations. Most extensive option Tools can not only inspect source code but also other artifacts (architectural descriptions) to verify. Distance to development workflow Feedback is usually delayed as the verification is usually tied to a more extensive test run, which is usually caused by the effort to evaluate the rules.

Slide 25

Slide 25 text

Moduliths https://github.com/odrotbohm/moduliths Binaries available from https://repo.spring.io 25

Slide 26

Slide 26 text

Moduliths 26 1. A convention to map contexts to packages Packages directly nested underneath the main application package are considered context ones. 2. Simple set of access rules Only access components in module API packages 3. API and test support to bootstrap modules Integration with Spring test context framework and Spring Boot integration test support to limit bootstrap (component & entity scanning) to only the involved packages. 4. Documentation support PlantUML integration via Simon Brown’s Structurizr to document module structure and dependency types.

Slide 27

Slide 27 text

27 package com.acme @Modulith // Instead of @SpringBootApplication class MyApplication { … } package com.acme.moduleA @ModuleTest // Instead of @SpringBootTest class MyModuleTests { … } Applies package conventions Bootstraps single module for test

Slide 28

Slide 28 text

28 Orders Business
 Time Catalog Inventory Accountancy User
 Accounts 
 Event 
 Event Component Dependency Type dependency

Slide 29

Slide 29 text

29 Orders Business
 Time Catalog Inventory Accountancy User
 Accounts 
 Event 
 Event Component Dependency Type dependency

Slide 30

Slide 30 text

DEMO 30

Slide 31

Slide 31 text

Bootstrap modes 31 1. Standalone Bootstraps the module the test is contained in. Components in other modules the bootstrapped module depends on need to be provided through @MockBean. 2. Direct dependencies Bootstraps the module including the modules it directly depends on. Transitive dependencies have to be mocked just like in standalone mode. 3. All dependencies Bootstraps the entire sub-tree of modules starting from the current one.

Slide 32

Slide 32 text

32 Orders Business
 Time Catalog Inventory Accountancy User
 Accounts 
 Event 
 Event Component Dependency Type dependency

Slide 33

Slide 33 text

Some conventions… 33 01 10 M M 01 10 Allowed by the compiler Allowed by Moduliths Rejected by the compiler Rejected by Moduliths Public Package protected Bounded Context Java Package

Slide 34

Slide 34 text

API Package Convention 34 ….modulith ….modulith.moduleA ….modulith.moduleB Visibility modifiers and the compiler enough to hide internals.

Slide 35

Slide 35 text

Module B moduleB Module A moduleA 35 ComponentA ComponentAImpl ComponentB ComponentBImpl Public Package protected Bounded Context Java Package 01 10 01 10

Slide 36

Slide 36 text

API Package Convention 36 ….modulith ….modulith.moduleA ….modulith.moduleA.internal ….modulith.moduleB ….modulith.moduleB.internal Access to components
 residing in internal packages
 forbidden and checked
 during tests.

Slide 37

Slide 37 text

Module A moduleA.foo Module B moduleB moduleA 37 ComponentA ComponentAImpl ComponentB ComponentBImpl moduleA.bar SupportA SupportAImpl 01 10 M 01 10 01 10 Public Package protected Bounded Context Java Package

Slide 38

Slide 38 text

DEMO 38

Slide 39

Slide 39 text

Thank you! 39 @olivergierke Oliver Drotbohm ƀ odrotbohm@pivotal.io odrotbohm

Slide 40

Slide 40 text

Resources 40 Moduliths Project website on GitHub Majestic Modular Monoliths Axel Fontaine, 8.11. 16:30-17:30 – Raum: Calgary Video on Youtube Modular Monoliths Simon Brown – Video on YouTube Refactoring to a System of Systems Video on YouTube