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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
{ … } package com.acme.moduleA @ModuleTest // Instead of @SpringBootTest class MyModuleTests { … } Applies package conventions Bootstraps single module for 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.
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