“Separation of concerns” is a software design principle that advises us to divide our application into distinct modules or sections, each of which addresses a separate “concern” or “feature”. The goal is to have as little overlap in functionality and scope between the divided concerns. A concern is typically considered to be separated if it is localized in a single component of a system, whether that is a file, class, container, or function. A concern is typically considered to be separated if it is localized in a single component of a system, whether that is a file, class, container, or function.
The goal of applying separation of concerns to our software design is to have as little overlap in functionality and scope as possible among our divided concerns. We typically say that a concern is separated if it is localized within a single module/component/file/class/function (henceforth simplified to “component” for readability).
History
The term “separation of concerns” originated in Djikstra’s essay “On the role of scientific thought“. Dijkstra states that you must be willing and able to study pieces of your subject matter (whatever you define that to be) on their own while still knowing that you’re occupying yourself with just one of the aspects of the overall subject. Were you to tackle all parts of your subject matter at once, you would simply be overwhelmed. Separating concerns (that is – focusing your attention on a single aspect) and analyzing them on their own, while not perfectly possible, allows you to effectively order your thoughts.
Collectively, we have since extended this idea to how we structure source code. You will have an easier time reasoning about your software if you are able to reason about distinct “concerns” separately, rather than being forced to reason about the entire system (or multiple concerns) all at once. Of course, as Dijkstra points out, this is not perfectly possibly: we still have to contend with interacting and dependent concerns as well as the behavior of the system-as-a-whole.
What are concerns?
A “concern” is commonly defined along the lines of “any particular interest in the system” or “a set of information that affects the code of a computer program”. This is vague, so it helps to consider examples. Concerns may be general, such as “the details of the hardware for this application” or “the details of how databases interact”. They may also be specific, such as “specify the name of a class to instantiate” or “perform calculation X”. We may divide up concerns based on any number of criteria, including:
- purpose
- functionality
- structure
- behavior
- cost
- supportability
- safety
- interoperability
We might also separate interactions at the system’s boundary from those at the application core, use domain analysis to suggest abstractions and separations, and separate concerns based on system responsibilities.
Benefits
In one sense, separation of concerns is an organizational principle that can help us evaluate the structure of a given piece of code. It helps produce a program that is modular. “Good” separation of concerns also allows each component to be simpler and more easily understood. The ideal is to achieve high cohesion and low coupling in our separations. Separating concerns can also enable independent development of program modules.
Separation of concerns is also viewed as a form of abstraction. Separation of concerns is achieved by encapsulating information inside of a section of code or a module that has a well-defined interface. By hiding implementation details within a module, we can modify a single concern’s implementation without knowing the details of or modifying other concerns. This provides us with an improved ability to modify, test, and reuse our modules.
Tradeoffs
- How we decompose our systems matters. Separating concerns at the wrong boundaries can result in high coupling and complexity between features/concerns, even if the contained functionality within a feature does not cause significant overlaps with another concern
- As with most form of abstractions, there is a cost in the addition of code, both in terms of interfaces and in machinery to support them. This introduces a potential execution penalty, although in many cases this is negligible.
Examples
- You can often identify poor separation of concerns by looking for mixed metaphors.
- Layered architecture involves separating a system into layers, each which usually represents a significant concern of the system (e.g., a hardware abstraction layer or board support package in embedded software encapsulates the hardware-specific concerns of the system)
- A hexagonal architecture also models separation of concerns. This architectural style separates the application core from the components and services that interact with the outside world.
- Event-Driven and Publish-Subscribe architectures often result in high separation of concerns due to the interaction through events and messages. Collaborators only know about the exchanged events, not the implementations sending or receiving them.
Related Concepts
- Avoid Mixed Metaphors
- Single Responsibility Principle is separation of concerns taken to its extreme
- Interface Segregation Principle is an application of Separation of Concerns to interfaces, advocating for creating smaller, focused interfaces that don’t expose functionality the client code doesn’t require
References
- Wikipedia: Separation of Concerns
- Wikipedia: Concern (computer science)
- Real-Time Software Design for Embedded Systems by Hassan Gomaa
Structuring a concurrent system into tasks results in a separation of concerns about what each task does from when it does it. This usually makes the system easier to understand, manage, and develop.
- Microsoft Application Architecture Guide, Chapter 2: Key Principles of Software Architecture
Separation of concerns. Divide your application into distinct features with as little overlap in functionality as possible. The important factor is minimization of interaction points to achieve high cohesion and low coupling. However, separating functionality at the wrong boundaries can result in high coupling and complexity between features even though the contained functionality within a feature does not significantly overlap.
- Use of Abstract Interfaces in the Development of Software for Embedded Computer Systems
There are additional benefits. This process leads toward what is sometimes referred to as a cleaner structure of the software – one in which there is a good separation of concerns, allowing each component to be simpler and more easily understood. Further, those components that are not cognizant of the real-world details of the interface can be more elegant and subject to a more mathematical analysis. Elegance is not a property of systems that must deal with ugly real world facts, but it can be obtained in those components that are separated from the real world b y the use of well-defined interfaces.
- On the role of scientific thought by E.W. Djikstra
Let me try to explain to you, what to my taste is characteristic for all intelligent thinking. It is, that one is willing to study in depth an aspect of one’s subject matter in isolation for the sake of its own consistency, all the time knowing that one is occupying oneself only with one of the aspects. We know that a program must be correct and we can study it from that viewpoint only; we also know that it should be efficient and we can study its efficiency on another day, so to speak. In another mood we may ask ourselves whether, and if so: why, the program is desirable. But nothing is gained —on the contrary!— by tackling these various aspects simultaneously. It is what I sometimes have called “the separation of concerns”, which, even if not perfectly possible, is yet the only available technique for effective ordering of one’s thoughts, that I know of. This is what I mean by “focusing one’s attention upon some aspect”: it does not mean ignoring the other aspects, it is just doing justice to the fact that from this aspect’s point of view, the other is irrelevant. It is being one- and multiple-track minded simultaneously.
- Revisiting Information Hiding – Reflections on Classical and Nonclassical Modularity
A concern is separated if its code is localized in a single component of a system, such as a file, a class, or a container.
- The Art of Readable Code by Dustin Boswell and Trevor Foucher
Key Idea Code should be organized so that it’s doing only one task at a time.
- From Software to Systems: The Newsletter – Communication Patterns: Decoupling edition
There are many reasons to create a separation of concerns. Perhaps the front end is constrained by the technology selected for the back end. Building with front end frameworks, like Angular or React, can generate more-performant software with its own logic. The front end team can design their own software and push changes quicker, without getting entangled in a monolith.
Meanwhile, back end software and services can focus on creating, structuring and publishing content that will be consumed by other software.
Design one part so that it does not need to know anything about the other part, except when it does . Usually, this is done by events. Caution: it’s very easy to build a distributed monolith in the cloud, rather than actually decoupling. If your parts aren’t independently deployable, you might still have tight coupling.
« Back to Glossary Index
