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

Building Better Monoliths – Modulithic Applications with Spring Boot

Building Better Monoliths – Modulithic Applications with Spring Boot

Slides of the talk I gave at W-JAX 2018 outlining modularization strategies for monolithic Spring Boot applications and using the Moduliths library to run module tests.

@springcentral

Oliver Drotbohm

November 07, 2018
Tweet

More Decks by Oliver Drotbohm

Other Decks in Programming

Transcript

  1. Modulithic Applications

    with Spring Boot
    Building better monoliths
    @olivergierke
    Oliver Drotbohm ƀ [email protected]
    odrotbohm

    View Slide

  2. 2

    View Slide

  3. Monolith VS. Microservice
    3
    Module

    A
    Module

    B
    Module

    C
    Module

    B
    Module

    C
    Module

    A
    Bounded Context Deployment Unit Internal invocation External invocation

    View Slide

  4. 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.

    View Slide

  5. 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.

    View Slide

  6. 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.

    View Slide

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

    View Slide

  8. How to preserve the
    structure in a

    monolithic application?

    8

    View Slide

  9. Limit active Bounded
    Context interaction!

    9

    View Slide

  10. 10
    Orders
    Business

    Time
    Catalog
    Inventory Accountancy
    User

    Accounts

    View Slide

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

    View Slide

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

    });
    }
    }

    View Slide

  13. 13
    Orders
    Business

    Time
    Catalog
    Inventory Accountancy
    User

    Accounts

    View Slide

  14. 14
    Orders
    Business

    Time
    Catalog
    Inventory Accountancy
    User

    Accounts

    Event

    Event

    View Slide

  15. 15
    Orders
    Business

    Time
    Catalog
    Inventory Accountancy
    User

    Accounts

    Event

    Event

    View Slide

  16. DEMO
    16

    View Slide

  17. How to preserve the
    structure in a

    monolithic application?

    17

    View Slide

  18. Define and monitor
    structure violations!

    18

    View Slide

  19. 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.

    View Slide

  20. 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.

    View Slide

  21. 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.

    View Slide

  22. 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.

    View Slide

  23. 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.

    View Slide

  24. 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.

    View Slide

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

    View Slide

  26. 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.

    View Slide

  27. 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

    View Slide

  28. 28
    Orders
    Business

    Time
    Catalog
    Inventory Accountancy
    User

    Accounts

    Event

    Event
    Component Dependency Type dependency

    View Slide

  29. 29
    Orders
    Business

    Time
    Catalog
    Inventory Accountancy
    User

    Accounts

    Event

    Event
    Component Dependency Type dependency

    View Slide

  30. DEMO
    30

    View Slide

  31. 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.

    View Slide

  32. 32
    Orders
    Business

    Time
    Catalog
    Inventory Accountancy
    User

    Accounts

    Event

    Event
    Component Dependency Type dependency

    View Slide

  33. 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

    View Slide

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

    View Slide

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

    View Slide

  36. 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.

    View Slide

  37. 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

    View Slide

  38. DEMO
    38

    View Slide

  39. Thank you!
    39
    @olivergierke
    Oliver Drotbohm ƀ [email protected]
    odrotbohm

    View Slide

  40. 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

    View Slide