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

Modeling Complexity: Using Modularization and Bounded Contexts in Domain Driven Design

Modeling Complexity: Using Modularization and Bounded Contexts in Domain Driven Design

Lemi Orhan Ergin

May 06, 2023
Tweet

More Decks by Lemi Orhan Ergin

Other Decks in Technology

Transcript

  1. MODELING
    COMPLEXITY
    USING MODULARIZATION AND
    BOUNDED CONTEXTS IN DDD
    LEMİ ORHAN ERGİN
    co-founder, craftgate

    View Slide

  2. Reference: https://www.reddit.com/r/notinteresting/comments/8l4pct/man_decides_to_keep_box_of_cables

    View Slide

  3. everything starts with keeping one

    unused cable from an unknown gadget
    THE START

    View Slide

  4. at the beginning, the order and

    entropy is under control
    THE HONEYMOON

    View Slide

  5. then with every new adapter and

    cable, entropy increases and chaos starts
    THE INEVITABLE CLUTTER

    View Slide

  6. Might work for a while but lead to frustration later


    Hard to find the right cable you need


    Sometimes we connect wrong cables to wrong adapters


    Requires time and patience to simplify the clutter


    At some point, we pour the box and reorganize all the cables


    Throwing the box away completely would also be a solution

    View Slide

  7. THE START
    Java
    Spring Boot
    Hibernate
    Postgres

    View Slide

  8. THE HONEYMOON
    Java
    Spring Boot
    Hibernate
    Postgres
    Gradle
    Kafka
    Lombok

    View Slide

  9. Java
    Hibernate
    Postgres
    Gradle
    Kafka
    Unit Tests
    Lombok
    Kubernetes Integration Tests
    Feign Client
    Docker
    Jenkins
    Sonar
    MongoDB
    JWT
    React Prometheus
    Grafana
    Elastic Search
    Object Mapper
    Actuator
    Open API
    Swagger
    Config Server
    Euroka
    Kotlin
    Groovy
    Spring Cloud
    JPA
    Go
    Node Kibana
    Mockito
    THE REALITY
    Spring Boot

    View Slide

  10. Java
    Hibernate
    Postgres
    Gradle
    Kafka
    Unit Tests
    Lombok
    Kubernetes Integration Tests
    Feign Client
    Docker
    Jenkins
    Sonar
    MongoDB
    JWT
    React Prometheus
    Grafana
    Elastic Search
    Object Mapper
    Actuator
    Open API
    Swagger
    Config Server
    Euroka
    Kotlin
    Groovy
    Spring Cloud
    JPA
    Go
    Node Kibana
    Mockito
    Spring Boot
    the implementation of

    customers’ needs
    THE REALITY
    Where is the
    business logic?

    View Slide

  11. Java
    Hibernate
    Postgres
    Gradle
    Kafka
    Unit Tests
    Lombok
    Kubernetes Integration Tests
    Feign Client
    Docker
    Jenkins
    Sonar
    MongoDB
    JWT
    React Prometheus
    Grafana
    Elastic Search
    Object Mapper
    Actuator
    Open API
    Swagger
    Config Server
    Euroka
    Kotlin
    Groovy
    Spring Cloud
    JPA
    Go
    Node Kibana
    Mockito
    Spring Boot
    scattered somewhere in the code
    THE REALITY
    Where is the
    business logic?

    View Slide

  12. Where is the
    business logic?
    where are the layers ?


    what are the features ?


    where is the business logic ?


    Where should we test ?
    THE REALITY

    View Slide

  13. Might work for a while but lead to frustration later


    Hard to find the right cable you need


    Sometimes we connect wrong cables to wrong adapters


    Requires time and patience to simplify the clutter


    At some point, we pour the box and reorganize all the cables


    Throwing the box away completely would also be a solution
    place to update the code
    grow
    the codebase
    the codebase
    stop adding new features and refactor
    Deleting legacy code and rewriting
    dependencies

    View Slide

  14. BLOATED
    SERVICES
    IS LIKE

    OVERLOADED PLUGS
    every new feature

    is added like a patch

    View Slide

  15. Repository
    One use case, one feature

    all well defined implementation
    Payment

    Service

    View Slide

  16. Repository
    new use cases need new functionality

    added to the same service
    same repository is enlarged to

    fulfill the requirements of all use cases

    and reused the existing ones
    Payment

    Service

    View Slide

  17. Payment

    Service
    Repository Repository it’s time to split the functionality
    payment service is growing gradually

    View Slide

  18. Repository Repository
    Service
    Repository
    Time to split payment service, but


    still dependent to it
    New use cases are comming
    Payment

    Service

    View Slide

  19. Rest Client
    Event
    Publisher
    Soap Client
    Repository Repository
    Manager
    Service
    Service
    Repository
    the dead end

    of complexity
    Payment

    Service
    Service
    Service
    Service
    Service
    Repository
    Repository Repository
    Repository

    View Slide

  20. Complexity is the degree
    of difficulty in sustaining
    quality, maintainability,
    speed, cognitive load,

    and development.
    that is what kills the sustainable growth

    View Slide

  21. Adding new features becomes
    harder and harder in time


    Complexity rises till the dead end


    You either redesign or rewrite to
    continue adding new features


    Adding new people to the team
    makes the progress slower


    Microservices transformation
    projects usually aim to fix these
    kinds of legacy projects
    THE IMPACT OF

    COMPLEXITY AS USUAL
    Feature

    Count
    Time
    Growth

    Limit
    Growth

    Limit
    let’s split it into

    microservices
    we need to

    rewrite it again
    Difficulty Level

    While Adding

    New Features
    Time
    refactor

    redesign

    zone
    Growth

    Limit
    Growth

    Limit
    the increase in
    endurance level is
    due to the new senior
    endurance level:

    do not touch

    if it works
    transfor-

    mation

    project

    zone
    go-live

    night

    View Slide

  22. Feature

    Count
    Time
    Difficulty Level

    While Adding

    New Features
    Time
    THE IMPACT OF

    MANAGABLE

    COMPLEXITY
    aka. SUSTAINABLE GROWTH
    No need for big redesigns or
    rewrites, refactoring is enough


    Refactoring should be continuous


    The system should be open to
    extension and close to modification


    Adding new people to the team has
    a positive impact


    No direct need for microservices

    View Slide

  23. Source code is the real software design.
    Designing software is an exercise in

    What is Software Design? The C++ Journal Vol. 2, No. 2. 1992
    http://user.it.uu.se/~carle/softcraft/notes/Reeve_SourceCodeIsTheDesign.pdf
    Reference:
    Jack W. Reeves
    Author of the "(B)leading Edge"
    column for the publication C++ Report

    managing complexity.

    View Slide

  24. The critical complexity of most
    software projects is in understanding
    the business domain itself.
    “Interview With Eric Evans; Why DDD Matters Today” Dec 20, 2006
    http://www.infoq.com/articles/eric-evans-ddd-matters-today
    Reference:
    Eric Evans
    Author of the "Domain-Driven Design:
    Tackling Complexity in Software" “

    View Slide

  25. Managing complexity for sustainable growth
    Otel-imiz-in karşı-sın-da-ki dükkan-da gör-dü-ğüm bir elbise-yi dene-mek iste-r-im
    needs a paradigm shift
    like the difference between

    structure in English and Turkish
    Unlearn what you’ve known so far and Relearn with no biases and limits

    View Slide

  26. Microservices


    Programming in Go, Rust


    Using Kafka or MongoDB


    Spring Boot, Frameworks


    Cloud Engineering


    Event-based architecture


    Distributed systems


    Scrum, Kanban, etc.


    Story point estimations


    Packaging by infrastructure


    Dependency Injection


    Having modules anywhere
    These may decrease
    complexity but not enough
    for sustainable growth

    View Slide

  27. Having modules everywhere makes your
    software modular but it may not achieve

    sustainable growth

    View Slide

  28. SEPARATION

    OF CONCERNS
    INFORMATION HIDING
    PRINCIPLE
    INVERSION OF CONTROL
    PRINCIPLE
    DEPENDENCY INVERSION
    PRINCIPLE
    COUPLING
    COHESION
    sustainable growth
    The mentality behind modularization, i.e. the principle of
    “Separation of Concerns” - Edsget W. Dijkstra
    1 Know The Fundamentals
    Details can be found in my talk: Modular Architecture For Pragmatic Developer

    https://speakerdeck.com/lemiorhan/modular-architecture-for-pragmatic-developers
    PRINCIPLES

    View Slide

  29. sustainable growth
    Focus on how modules communicate rather
    than what internal properties and behaviors
    should be. - Alan Kay
    2 Focus on Modules & Communication
    Details can be found in my talk: Unlearn OOP

    https://speakerdeck.com/lemiorhan/unlearn-oop-deleted-scenes-misconceptions-unspoken-truths
    OBJECT

    ORIENTED

    PROGRAMMING
    PRINCIPLES

    View Slide

  30. sustainable growth
    SOLID principles are about managing
    dependencies and limiting the impact
    of change. - Robert C. Martin
    3 Learn How to Manage Dependencies
    Details can be found in my talk: It’s Not SOLID Anymore

    https://speakerdeck.com/lemiorhan/it-is-not-solid-anymore
    DESIGN

    PRINCIPLES
    PRINCIPLES

    View Slide

  31. sustainable growth
    The timeless goal of software engineering has been to
    separate code rather than change frequently from stable
    code. - James Coplien
    4 Protect Business Logic from Poisoning
    Details can be found in my talk: Growing Hexagonal Microservices with TDD

    https://speakerdeck.com/lemiorhan/growing-hexagonal-microservices-by-tdd
    HEXAGONAL

    ARCHITECTURE
    APPROACH

    View Slide

  32. sustainable growth
    Adding a new test to the system should
    be adding a new behavior rather than
    adding a method or class. - Ian Cooper
    5 Test Really What Matters: Behaviors
    Details can be found in my talk: Unlearn Unit Testing

    https://speakerdeck.com/lemiorhan/unlearn-unit-testing
    UNIT TESTING
    APPROACH

    View Slide

  33. sustainable growth
    No rules are universal, rules
    need context - Dave Thomas
    6 Focus on Contexts, Build Rules Around
    Details can be found in my talk: Pure Agile

    https://speakerdeck.com/lemiorhan/pure-agile-building-a-culture-without-scrum-kanban-and-xp-extended-version-v22-dot-11
    AGILE IS DEAD

    LONG LIVE AGILITY
    APPROACH
    there are no rules in software,


    there are patterns. The critical part


    is deciding which patterns to use

    View Slide

  34. sustainable growth
    Use cases are sequences of tasks toward
    a goal, context represents use cases, one
    per use case - James Coplien
    7 Put Bricks on the Wall One-by-One
    Details can be found in my talk: Domain Driven Modular Architecture

    https://speakerdeck.com/lemiorhan/domain-driven-modular-architecture
    USE CASE
    DRIVEN
    APPROACH
    DESIGN

    View Slide

  35. sustainable growth
    If your model is telling a story, the modules are chapters. It
    isn’t just code being divided into modules, but concepts.

    - Eric Evans
    8 Shape Software based on Contexts
    BOUNDED CONTEXT
    DOMAIN DRIVEN DESIGN
    Details can be found in my talk: Domain Driven Modular Architecture

    https://speakerdeck.com/lemiorhan/domain-driven-modular-architecture
    DESIGN

    View Slide

  36. 8 Shape Software based on Contexts
    DESIGN
    7 Put Bricks on the Wall One-by-One
    6 Focus on Contexts, Build Rules Around
    5 Test Really What Matters: Behaviors
    4 Protect Business Logic from Poisoning
    APPROACH
    Modularity & Sustainable Growth
    3 Learn How to Manage Dependencies
    2 Focus on Modules & Communication
    1 Know The Fundamentals
    PRINCIPLES

    View Slide

  37. DESIGN
    BOUNDED CONTEXT
    DOMAIN DRIVEN DESIGN
    USE CASE
    DRIVEN
    APPROACH
    UNIT TESTING
    HEXAGONAL

    ARCHITECTURE AGILE IS DEAD

    LONG LIVE AGILITY
    APPROACH
    SEPARATION

    OF CONCERNS
    INFORMATION HIDING
    PRINCIPLE
    INVERSION OF CONTROL
    PRINCIPLE
    DEPENDENCY INVERSION
    PRINCIPLE
    COUPLING
    COHESION
    OBJECT

    ORIENTED

    PROGRAMMING
    DESIGN

    PRINCIPLES
    PRINCIPLES

    View Slide

  38. Let’s build a payment API


    start from the beginning…

    View Slide

  39. Represent the model as how the business sees them
    that is where concepts

    find their meanings, people


    speak the same language and


    share the same concerns
    CARD PAYMENT CONTEXT

    View Slide

  40. Any info in the model can change at any time, that’s chaos.

    We need to control the change!
    CARD PAYMENT CONTEXT

    View Slide

  41. An aggregate defines
    consistency boundaries,
    everything inside (i.e. the
    data) needs to be always
    consistent
    An aggregate is just a
    construct to organize
    business rules
    Aggregate is a unit for the purpose of data changes
    CARD PAYMENT CONTEXT

    View Slide

  42. Each aggregate has a root and a boundary
    Aggregate Root is the
    facade representing the
    whole structure, and it is
    the gateway.


    It enforces the aggregate's
    business/consistency rules
    References to aggregate
    internals from the outer
    world are strictly forbidden
    CARD PAYMENT CONTEXT

    View Slide

  43. AGGREGATE
    BUSINESS
    RULES
    DOMAIN MODEL
    CONTEXT
    TRANSACTIONAL AND
    LINGUISTIC BOUNDARY
    INPUT OUTPUT
    Each BC is loosely coupled &
    independent and has its own
    ubiquitous language, own data, and
    an explicit owner.


    We split software into BCs, like
    microservices.


    Unlike modules, which are organized
    primarily around technical concerns,
    BCs are organized around business
    concerns.
    is it a realization of Moduliths ?

    View Slide

  44. CONTEXT A CONTEXT B
    CONTEXT C
    CONTEXT D
    Context Map includes a high-level
    view of the domains involved, along
    with the relationships and interactions
    between them
    communication patters used in

    microservices are applicable here
    Relationship Models
    Customer-Supplier


    Conformist


    Partneship


    Anti-Corruption Layer


    Separate Ways


    Open-Host Service


    Shared Kernel

    View Slide

  45. The communication strategy between
    bounded contexts defines how extensible
    your software is
    Different than relationship models
    Strategies
    Chained - Unchained


    Transactional - Non-Transactional
    CONTEXT A CONTEXT B
    CONTEXT C
    CONTEXT D

    View Slide

  46. CREATE USER
    NOTIFY CUSTOMER
    SAVE OUTBOX
    CREATE USER

    USE CASE
    USER CREATED

    MESSAGE
    USER CREATED

    MESSAGE
    OUTBOX SAVED

    MESSAGE
    CUSTOMER NOTIFIED

    MESSAGE
    CHAINED
    SYNC
    SYNC

    View Slide

  47. CREATE USER
    NOTIFY CUSTOMER
    SAVE OUTBOX
    CREATE USER

    USE CASE
    USER CREATED

    MESSAGE
    USER CREATED

    MESSAGE
    OUTBOX SAVED

    MESSAGE
    CUSTOMER NOTIFIED

    MESSAGE
    UN-CHAINED
    ASYNC
    ASYNC

    View Slide

  48. CREATE USER NOTIFY CUSTOMER SAVE OUTBOX
    CREATE USER

    USE CASE
    USER CREATED

    MESSAGE
    OUTBOX SAVED

    MESSAGE
    CUSTOMER

    NOTIFIED

    MESSAGE
    TRANSACTIONAL
    SINGLE TRANSACTION
    One exception triggers rollbacking of all previous operations in multiple contexts

    View Slide

  49. CREATE USER NOTIFY CUSTOMER SAVE OUTBOX
    CREATE USER

    USE CASE
    USER CREATED

    MESSAGE
    OUTBOX SAVED

    MESSAGE
    CUSTOMER

    NOTIFIED

    MESSAGE
    NON-TRANSACTIONAL
    TRANSACTIONAL TRANSACTIONAL TRANSACTIONAL

    View Slide

  50. MANAGING THE LEVEL OF

    HOW DEPENDENT BOUNDED
    CONTEXTS TO EACH OTHER
    thinking in communicating objects?

    View Slide

  51. We want to avoid having to
    change the source code and
    then rebuild the system every
    time you make these shifts
    Two patterns closely related with each
    other to achieve this
    A. Configurable Connection Pattern


    B. Configurable Dependency Pattern
    https://alistaircockburn.com/Configurable%20Connection%20Configurable%20Dependency.pdf

    View Slide

  52. Configurable Connection Pattern
    Configurable Dependency Pattern
    Sender does not depend on any receiver.


    It’s like polymorphism from OOP.


    Sender has a source-code dependency

    on the receiver.
    SENDER
    RECEIVER 1 RECEIVER 2 RECEIVER3
    sender’s provided
    interface
    sender’s required
    interface
    RECEIVER

    CONTRACT
    Sender asks the configurator which receiver

    to use, like in dependency lookup.


    Sender contains a hard-coded reference to

    the configurator.


    We avoid hard-coding the names of classes

    on which we depend in our code.


    Sender has a source-code dependency on

    the configurator.
    SENDER
    RECEIVER 1 RECEIVER 2 RECEIVER3
    RECEIVER

    CONTRACT
    CONFIGURATOR
    CONFIGURATOR CONFIGURATOR
    B
    application

    context
    spring
    SENDER
    RECEIVER 1 RECEIVER 2 RECEIVER3
    sender’s provided
    interface
    sender’s required
    interface
    RECEIVER

    CONTRACT
    CONFIGURATOR
    Configurator tells the sender what receiver

    to use, like in dependency injection.


    Receiver is held in sender in a local variable.


    It is set the receiver during execution, or

    at initialization.


    Sender still has a source-code dependency

    on the receiver.
    A
    spring

    View Slide

  53. Configurable Connection Pattern
    C
    spring
    Only one configurator exists & sender calls

    it via static methods.


    Receivers register themselves at initialization

    to the configurator.


    Configurator knows which receiver to select.


    Sender has no source-code dependency.
    SENDER
    RECEIVER 1 RECEIVER 2 RECEIVER3
    RECEIVER

    CONTRACT
    CONFIGURATOR
    CONFIGURATOR CONFIGURATOR
    the new approach
    publisher
    Sender asks the configurator which receiver

    to use, like in dependency lookup.


    Sender contains a hard-coded reference to

    the configurator.


    We avoid hard-coding the names of classes

    on which we depend in our code.


    Sender has a source-code dependency on

    the configurator.
    SENDER
    RECEIVER 1 RECEIVER 2 RECEIVER3
    RECEIVER

    CONTRACT
    CONFIGURATOR
    CONFIGURATOR CONFIGURATOR
    B
    application

    context
    spring
    SENDER
    RECEIVER 1 RECEIVER 2 RECEIVER3
    sender’s provided
    interface
    sender’s required
    interface
    RECEIVER

    CONTRACT
    CONFIGURATOR
    Configurator tells the sender what receiver

    to use, like in dependency injection.


    Receiver is held in sender in a local variable.


    It is set the receiver during execution, or

    at initialization.


    Sender still has a source-code dependency

    on the receiver.
    A
    spring

    View Slide

  54. C
    spring
    Only one configurator exists & sender calls

    it via static methods.


    Receivers register themselves at initialization

    to the configurator.


    Configurator knows which receiver to select.


    Sender has no source-code dependency.
    SENDER
    RECEIVER 1 RECEIVER 2 RECEIVER3
    RECEIVER

    CONTRACT
    CONFIGURATOR
    CONFIGURATOR CONFIGURATOR
    the new approach
    publisher
    CONFIGURATOR: MESSAGE PUBLISHER
    SENDER
    RECEIVER: USE CASE HANDLER

    View Slide

  55. RECEIVER: USE CASE HANDLER
    Use case handlers are injected by Spring
    into the registry, which is accessed by
    Message Publisher to submit messages
    HANDLER REGISTRATION
    Receivers are extended by MessageHandlers
    not only for being detected by Spring, and
    also for providing base “handle method”
    infrastructure
    STEP 1

    View Slide

  56. RECEIVER: USE CASE HANDLER
    Use case handlers return Aggregate Roots
    (if required) since it is the gateway for data
    change
    USE CASE HANDLING
    Use case handlers trigger “publishMessages”
    after handling is completed
    STEP 2

    View Slide

  57. Bounded contexts do not know which
    bounded contexts are connected with
    which strategy. That makes every
    bounded context independent.
    DEFINING COMMUNICATION STRATEGIES
    STEP 3
    SYNC
    SINGLE

    TRANSACTION

    View Slide

  58. Java library for building loosely-coupled bounded contexts
    https://github.com/craftgate/craftgate-modulith-messaging
    Supports loosely coupled, real-time
    dependency strategy to bounded
    contexts


    Supports aggregate roots and
    domain events


    Supports use-case driven approach
    and hexagonal architecture


    Supports transactional flows among
    aggregates


    Supports sync and async call chains
    between bounded contexts


    Contains unit tests for the API and
    acceptance tests for checking
    possible flows


    Detailed documentation describing
    why we need that & how it works


    Actively being used in production
    craftgate-modulith-messaging

    View Slide

  59. HOW
    MODULARITY
    LOOKS LIKE?

    View Slide

  60. THE SHAPE OF MODULARITY
    Each module is self-contained, with well-
    defined input/output interfaces


    Each module hides implementation details and
    exposes a high-level contract


    Each module handles multiple use cases directly
    related to user needs


    Each module extends by one use case at a time,
    no bloating experienced


    Each module is loosely coupled and highly
    coherent

    View Slide

  61. WE ARE

    HIRING
    speakerdeck.com/lemiorhan


    twitter.com/lemiorhan
    lemi orhan ergin co-founder
    mail: [email protected]
    CRAFTGATE
    one-stop shop payment gateway


    payment hub for your virtual poses


    with dynamic pos routing, you always
    pay the lowest commission to banks
    craftgate.io @craftgateio

    View Slide