Designing Modular Systems

From NovaOrdis Knowledge Base
Jump to navigation Jump to search

External

Internal

Overview

The larger and tightly coupled a system is, the harder it is to change, and the easier it is to break. The alternative is to compose a system from small, simpler pieces. Each piece has a clearly defined interface and it is easy to understand. The component can be tested and deployed in isolation. Microservices fall into this category.

The systems should be designed for modularity. The goal of modularity is to make it easier and safer to make changes to the system. The hope is that, for a well-designed modular system, you can make a change to a component without needing to change other parts of the system. A well-designed modular system performs well on these operational performance metrics.

Designing components is the art of deciding which elements of your system to group together and which to separate. Two important characteristics of a component are coupling and cohesion. The goal of good design is to create low coupling and high cohesion.

Coupling

Coupling describes how often a change to one component requires a change to another component. Zero coupling isn't a realistic goal for two parts of a system, zero coupling means they aren't part of the same system at all. Instead we aim for low, or loose coupling. In software system engineering, especially in systems based on microservices, we aim for loose coupling. In a microservices context, we want to avoid coupling the microservice and its consumers such that a small change to the microservice itself can cause unnecessary changes to the consumer. It should be easy to replace one side of the dependency relationship without disturbing the other.

DRY and Coupling

Duplication Considered Useful

Cohesion

Cohesion describes the relationship between the elements within a component. As with coupling, the concept of cohesion relates to patterns of change. Components with high cohesion are easier to change because they are smaller, simpler and cleaner with a lower blast radius.

Cohesion is the quality of code base to have related code - code that describes a certain logical behavior - grouped together. The concept of cohesion is reinforced by the Single Responsibility Principle espoused by Robert C. Martin. Any given component should have responsibility for one thing, so it is focused, so its contents are cohesive. The component "should fit in your head".

In software system engineering, especially in systems based on microservices, we aim for high cohesion. High cohesion allows us, if we want to change behavior, to change it in only one place, and then deliver the change via a simpler release process than required if the behavior were to be changed in a lot of places. Making changes in many places is slower, and the consequent release process is riskier.

High cohesion software systems are designed by finding the right boundaries of our problem domain. Right boundaries ensure that related behavior resides in one place, and the communication with other boundaries is as loose as possible. All decisions about what changes are allowed on the state maintained within the bounded context must be taken inside the bounded context. If the decision about what changes are allowed leak out, we are losing cohesion.

Kent Beck's Rules of Simple Design

Beck Design Rules by Martin Fowler

A well designed system should:

  • Pass its tests (do what is supposed to do).
  • Reveal its intentions (be clear and easy to understand).
  • Have no duplication. Don't Repeat Yourself.
  • Include the fewest elements.

Design Components around Domain Concepts, not Technical Ones

Shared components create coupling between their dependents, and components based on technical concepts introduce artificial coupling.

Define Interfaces between Components

This is a direct consequence of the Law of Demeter.

Use Testing to Drive Design Decisions