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.
artifact. Often parts of the context are even deployed as separate processes. ◦ 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. ◦ Testing individual modules Testing individual modules usually requires collaborating systems to be bootstrapped, stubbed or mocked.
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.
easy to understand what the code is there for, and easy to navigate to wherever you need to be for anything more complicated than “make that button light blue”. Instead, the layout of code—the directory names, the relationships of child and sibling folders, the grouping and naming of related files—should mirror the problem domain as closely as possible. Domain-Based Structure Domain-Based Boundaries “ “ Source: Dan North, CUPID—for joyful coding
event listeners up until the failure will have been executed. Assuming the already triggered event listeners also execute transactional logic, the local transaction is rolled back and the system is still in a strongly consistent state. Transactional event listeners are not invoked in the first place.
In case a normal event listener fails the entire transaction will roll back. This enables strong consistency between the event producer and the listeners registered but also bears the risk of supporting functionality interfering with the primary one, causing the latter to fail for less important reasons. The tradeoff here could be to move to a transactional event listener and embrace eventual consistency.
In case a transactional event lister fails or the application crashes while transactional event listeners are executed, the event is lost and functionality might not have been invoked. Other transactional event listeners will still be triggered. BONUS: An asynchronous event listener fails The event is lost but the primary functionality can still succeed as the event is handled in a separate thread. Retry mechanisms can (should?) be deployed in case some form of recovery is needed.
for DI Exposed Aggregates Primary elements of the domain and constraints Published events Events the component emits Required Interface Consumed Service API External dependencies of Spring beans Configuration Spring Boot configuration properties Consumed events Events that the component reacts to
set of access rules and API to verify Test support to bootstrap modules Event publication registry Documentation support Actuator and observability support
Enough Software Architecture George Fairbanks – Book Architecture, Design, Implementation Ammon H. Eden, Rick Kazman – Paper Sustainable Software Architecture Carola Lilienthal – Book The Programmer's Brain Felienne Hermans – Book