Design Heuristics for Decomposing Monoliths

Design Heuristics for Decomposing Monoliths

"When you design a system…, then if the features can be broken into … loosely bound groups of relatively closely bound features, then that division is a good thing to be made a part of the design. This is just good engineering." -- Tim Berners-Lee

Decomposing monoliths is a trendy topic in software architecture these days.

I saw a great talk at #SACON by Vladik Khrononov that had some important caveats and design heuristics for approaching decomposition that I thought I’d share.

As we decompose software services from a monolithic big ball of mud, we tend to apply Domain Driven Development towards defining Bounded Contexts based on business domains. From there we can continue the decomposition further towards microservices, and then even further still. As you tell from this graphical depiction, there is a clear tradeoff between service size and system complexity & maintenance, as with a proliferation of nano-services you gain the benefit of high modularity but with the cost of high communication & coordination between services.

No alt text provided for this image

A service API is the interface through which you exchange information in and out of a given service. A microservice presents a micro-interface, reducing the surface area of that API which helps to reduce coupling between services, making that service easier to understand, more fault tolerant and less likely to require modification as the system changes.

”Global complexity …is the complexity of the overall structure of a program or system. i.e., the degree of association or interdependence among the major pieces of a program” -- Glenford J. Myers

As companies migrate their systems towards microservices it’s tempting to go overboard, and in doing so create a distributed monolith which actually increases system complexity. Whereby now you have to modify many small services to get a new feature shipped instead of making changes in a single monolithic code base.

But how do you know how far you should take your decomposition?

No alt text provided for this image

Vladik proposed several design heuristic to help guide architects making these types of engineering tradeoffs, and here’s a summary:

1. Decompose to Bounded Contexts

  • Don't implement logically conflicting models in the same service, instead try to decompose to the bounded context level

2. Don’t First Law of Distributed Object Design

  • Don’t distribute your objects” -- Martin Fowler
  • Unless there’s a clear business goal try not to decompose bounded contexts further at this point
  • Remember that you may not actually need microservices, so don’t decompose unnecessarily

3. Logically segment your domains into Core, Generic and Supporting

  • Buy or adopt Generic subdomains as these are mundane and complex problems everybody has and are already solved by other software vendors
  • Core subdomains represent your business’s competitive advantage and what you hope to deepen and grow
  • Supporting subdomains are not complex or interesting, offer no competitive advantage but are necessary to support the Core subdomains

4. Don’t Rush

  • Extract your Core subdomains and strictly adhere to their boundaries
  • Decompose further only when you acquire deep domain expertise
  • The best designs require learning and iteration, almost no one gets this right the first time

5. Supporting Subdomains

  • Because these change less frequently it’s often safe to decompose subdomain boundaries early for supporting services

6. Evaluate Consistency Requirements

  • What are the dependencies between your services?
  • Does one service need to read the last write of the other?

? Could use two services with synchronous communication

  • Is eventual consistency ok?

? Could use 2+ services with asynchronous communication

  • Do you need to control concurrency?

? Perhaps put these into the same service

  • A good heuristic is that if your microservice design requires distributed atomic transactions, just don’t do it and combine those into a single service.

7. Public / Private Events

  • Categorize events as public or private, where public events represent your “service door” and remember that you can collapse multiple private events into public event(s) too.
  • Keep domain knowledge inside of services and expose only relevant state-altering events

8. Make Events Explicit

  • Eliminate ambiguity so clients don’t have to make assumptions about your events or interfaces

9. Evaluate Reasons for Change

  • If two services regularly need to change together that’s a design smell that perhaps they should be the same service

10. Evaluate Service Doors

  • Microservices should have a micro “service door” or interface

要查看或添加评论,请登录

社区洞察

其他会员也浏览了