DEV Community

Ujjwal Raj
Ujjwal Raj

Posted on

Template for System Design Using ‘The Method’

Welcome to another weekly article on System Design. In this article, I will give you a template for how to design systems using 'The Method'.

As I’ve already stated: we are going to learn the rules, follow the rules, and once you are an expert — bend the rules.

Let’s keep track of what we’ve learned so far, so everything stays at our fingertips even if you haven’t read the previous article:

  • Avoid functional decomposition (what we were doing in universities), and remember: a good system design speaks — through how components interact.
  • The client should not be the core business. Let the client be the client — not the system.
  • Decompose based on volatility — list the areas of volatility.
  • There is rarely a one-to-one mapping between a volatility area and a component.
  • List the requirements, then identify the volatilities using both axes: — What can change for existing customers over time? — Keeping time constant, what differs across customers? (Remember: these axes are independent.)
  • Verify whether a solution/component is masquerading as a requirement. Verify it is not variability. A volatility is not something that can be handled with if-else; that’s variability.

Let’s now start diving into the template.

The Use Case

A use case is an expression of required behavior — how the system is going to accomplish something to add business value. It describes the end-user interaction with the system or the system’s interaction with the user. It also illustrates system-to-system interactions and processing. It is always recommended to capture use cases graphically. Nothing communicates better and more simply than pictures.

An activity diagram showing all possibilities can also be drawn.

The two figures below show how a use case diagram (left) and an activity diagram (right) look:

Image

It should be noted that it is not feasible to draw activity diagrams for all possible cases. So, only important ones should be covered, leaving the simple ones aside — they will be understood naturally through the layered approach we’re about to cover. Get ready for the ultimate framework.

Layered Approach

A layered approach is a better representation of systems and services. The encapsulations are layered:

a) Each layer encapsulates the volatility of itself and of the layers below it, from the layers above.
b) Within a layer, the services encapsulate volatility from each other. (Reminder: by “services,” I do not mean microservices.)

The following image is the same one I used to define a good design decision tree in the very first article. Don’t worry if you haven’t read it — but if you have, you’ll relate to it:

Image

The image below is taken from the book Righting Software by Juval Löwy. I use the same color template while designing systems, and we’ll continue using it throughout the series:

Image

It should be obvious that the number of layers in a practical system is limited. These layers terminate at a resource layer — such as a database, file storage, or third-party API.

The Services

As I’ve already emphasized in previous articles: system design is not just tech — it is engineering.

With service flow diagrams, the entire data flow can be understood very well. This is one of the key advantages of volatility-based decomposition. Designing in this layered manner makes service calls visualizable, giving us insights even before deployment — and certainly after some traffic is observed.

Things like security, scalability, traffic, throughput, responsiveness, consistency concerns, and synchronization can all be examined more clearly.

The General Template Framework for System Design

Here is the general framework of 'The Method'. We will discuss each section.

Image

The Client

These are the entry points to the system. Make sure your client is only a client — not a system. The client should not encapsulate business logic. All clients should use the same entry point to the system. A phone app and a web app should not be calling different backends.

Volatility by client:

  • Axis 1 (What can change for existing customers?): Different users may demand different accessibility options, dark mode, etc.
  • Axis 2 (What differs across customers?): It can be a desktop app, Android app, or just an API interface. Technology may vary — React, .NET, etc.

The Business Logic Layer

This layer encapsulates the system’s use cases — i.e., what the system is supposed to do from a business perspective. The components here are Managers and Engines.

  • Manager: Encapsulates volatility in the sequence.
  • Engine: Encapsulates volatility in the activity.

A Manager can utilize zero or more Engines to complete a business task.

For example, a car starting system may look like:

MovementManager.Start();
Enter fullscreen mode Exit fullscreen mode

And within the Manager:

PreparingEngine.ConfirmAdjustSeat();
PreparingEngine.AdjustSideGlass();
PreparingEngine.CloseWindow();

DrivingEngine.AdjustBrake();
DrivingEngine.AdjustGear();
DrivingEngine.AdjustAccelerator();
DrivingEngine.AdjustClutch();
DrivingEngine.StartICEngine();
Enter fullscreen mode Exit fullscreen mode

Here, the Manager (MovementManager) is utilizing multiple Engines — PreparingEngine, DrivingEngine.

Ensure that two managers do not use two different engines to do the same job. That’s a symptom of functional decomposition.

Engines can be reused between Managers, as the same activity might appear in different use cases. Design Engines with reuse in mind.

The Resource Access Layer

Components here are called ResourceAccess.

Volatility by ResourceAccess:

This layer defines how to access a resource — but it should not expose contracts like Read(), Write(), Open(), Close().

Instead, it should use business verbs, such as:

  • CurrentFuelVolume()
  • GetMusicPlaylists()

Using database-style contracts creates tight coupling — any change in the resource's tech affects all upper layers. Avoid this.

ResourceAccess components may be reused across Engines or Managers needing access to the same resource.

The Resource Layer

This contains the actual physical resources — databases, file systems, caches, third-party APIs, etc. It encapsulates the tech being used. This can also include external systems like payment APIs.

Utility

The Utilities vertical bar (on the right of the diagram) includes common infrastructure services that most systems need:

  • Authentication
  • Logging
  • Messaging Queue
  • Pub/Sub, etc.

Rechecking the Layer Classification

Once you’ve classified your layers and components, ask these questions. If the answers are “yes,” you’ve done volatility-based decomposition — not functional decomposition.

  • Are the names descriptive? E.g., SomeManager, DoSomethingEngine, SomethingResourceAccess
  • For Managers, the prefix should be a noun tied to the encapsulated volatility (e.g., MovementManager)
  • For Engines, the prefix should be a noun describing the activity (e.g., DrivingEngine)
  • For ResourceAccess, the prefix should be a noun related to the resource (e.g., FuelResourceAccess)
  • Gerunds (nouns ending in -ing) should only be used with Engines. Their use elsewhere usually signals functional decomposition.
  • Atomic business verbs should not be used in service names — only as operation names in ResourceAccess contracts.

Learn these rules. They ensure we don’t fall into the trap of functional decomposition.

I’m Updating the Rules to Carry Forward

Let’s keep the rules on our fingertips for the next article:

Updated Rules

  • Avoid functional decomposition (what we were doing in universities), and remember: a good system design speaks — through how components interact.
  • The client should not be the core business. Let the client be the client — not the system.
  • Decompose based on volatility — list the areas of volatility.
  • There is rarely a one-to-one mapping between a volatility area and a component.
  • List the requirements, then identify the volatilities using both axes: — What can change for existing customers over time? — Keeping time constant, what differs across customers? (Remember: these axes are independent.)
  • Verify whether a solution/component is masquerading as a requirement. Verify it is not variability. A volatility is not something that can be handled with if-else; that’s variability.
  • Use the layered approach and proper naming convention:

    • Names should be descriptive and avoid atomic business verbs.
    • Use <NounOfVolatility>Manager, <Gerund>Engine, <NounOfResource>Access.
    • Atomic verbs should only be used for operation names, not service names.

Conclusion

See you next Sunday in another article where I will dive with examples. Stay Tuned!

Here are links to previous articles in case you missed them:

  1. Why Functional Decomposition Leads to Bad System Design
  2. System Design Basics: Why The Right Method Matters
  3. The Anti-Design Thinking in System Design
  4. Volatility-Based Decomposition: A System Design Example
  5. Principles of Volatility-Based Decomposition in System Design

Top comments (0)