Distribu-ready with the Modular Monolith - Layla Porter - NDC Oslo 2024

Learn how to build a modular monolith that's ready for future distribution. Discover key principles, module design, architecture enforcement & migration strategies for maintainable apps.

Key takeaways
  • A modular monolith is a monolithic application divided into distinct modules while maintaining a single executable, offering improved maintainability and scalability

  • Key principles of modular architecture:

    • Loose coupling between modules
    • High cohesion within modules
    • Modules should be self-contained and independently deployable
    • Clear boundaries and encapsulation between modules
    • Communication through contracts/interfaces
  • Module design guidelines:

    • Use internal access modifiers by default
    • Keep module APIs public but implementation details private
    • Modules should only reference contracts, not other modules directly
    • Web API acts as the “glue” binding modules together
    • Each module should have its own data store
  • Three main approaches to enforce architecture:

    • Code review and pair programming
    • Compile-time checks through project references
    • Automated architecture tests
  • Benefits of modular monolith approach:

    • Easier local development and debugging
    • Simpler deployment compared to microservices
    • Ability to incrementally extract modules into separate services
    • Lower operational complexity
    • Better suited for most applications vs immediate microservices
  • Consider the “KitKat pattern”:

    • Each module is like a KitKat finger - encapsulated but part of the whole
    • Modules can be “broken off” into separate services when needed
    • Start with larger modules and decompose only when necessary
  • Common pitfalls to avoid:

    • Distributed monolith (complexity without benefits)
    • Premature distribution of services
    • Sharing databases between modules
    • Tight coupling between modules
  • Migration strategy:

    • Start with data separation
    • Use messaging/events for inter-module communication
    • Extract high-volume components as “satellites” first
    • Only distribute components that truly need independent scaling