Sponsor: Do you build complex software systems? See how NServiceBus makes it easier to design, build, and manage software systems that use message queues to achieve loose coupling. Get started for free.
What is cohesion and why should you care? Highly cohesive software design can reduce complexity and coupling. But what is cohesion? It’s the degree to which the elements inside a module belong together. How you group operations together can have a widely different outcome on Cohesion. Informational Cohesion is grouped by operations on data. Functional Cohesion is grouped by operations of a task. It’s directly related to the Single Responsibility Principle, which you might also have a different definition of.
Check out my YouTube channel where I post all kinds of content that accompanies my posts including this video showing everything that is in this post.
Most people generally hear about Cohesion in relation to Coupling. I’ve done a post about how to Write Stable Code using Coupling Metrics, but I haven’t yet touched directly on Cohesion. To me, Cohesion and Coupling are like the yin and yang of software design.
To give it a simple definition:
“degree to which the elements inside a module belong together”Structured Design: Fundamentals of a Discipline of Computer Program and Systems Design
However, most people are likely more familiar with or think of the Single Responsibility Principle, which is directly related to Cohesion. Single Responsibility Principle states:
A class should have one, and only one, reason to changeAgile Software Development, Principles, Patterns, and Practices
What what does that really mean? What is “one reason”? Does that mean that a class/module should only have one job? What about dependencies, what if they change? Doesn’t that affect other classes (coupling) and they have to change?
Robert C. Martin, the author wrote about this years ago on his blog to clarify.
When you write a software module, you want to make sure that when changes are requested, those changes can only originate from a single person, or rather, a single tightly coupled group of people representing a single narrowly defined business function. You want to isolate your modules from the complexities of the organization as a whole, and design your systems such that each module is responsible (responds to) the needs of just that one business function.https://blog.cleancoder.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html
What’s funny about this is that most classes/modules are not organized by business function, but rather they are organized by data.
If you read any of my other posts, you know that I advocate thinking about business capabilities and not technical concerns, which I describe more in my post AVOID Entity Services by Focusing on Capabilities.
I think the common practice is to organize operations (methods) in a class/module using an informational cohesion approach. Information cohesion is about grouping related to operations on data.
For example, a ProductService class that did data access for a Product Entity (Data Model).
These operations are grouped in this Interface/Class because they operate on the same information/data.
Functional Cohesion is about grouping related to the operations of a task. Single Responsibility mentions are about a narrowly defined business function(s).
This means that you don’t group by Entity/Data/Information, but rather the boundary in which users perform actions within your system. In other words, more based on roles. Cohesive software design, to me, is focusing on functional cohesion.
To illustrate this more, here’s an example of a class that depends on a IProductService that I’ve defined above.
The problem with Informational Cohesion that is occurring in IProductService, is that we don’t require anything more than one single method (GetProductBySku).
To test this class, we have a couple of options. We can create a stub of the interface or we can mock it. Most would probably choose to use a mocking library so we only have to mock the one method.
However, if we change the actual implementation to use a different method on our IProductService interface, this test is going to fail because we haven’t mocked every method on the interface.
Do we really want a dependency on that interface so we can use one method? When you need a class for one method, you don’t really the interface, you want a function.
In C#, Delegates are… functions!
Instead of an interface, I’ve defined delegates within a static class that are specific for the Catalog.
Now instead of depending on the interface, we depend on the delegate.
Now our test becomes much more explicit in how we create this Handler to test. We need to stub the delegate. There is no mock. There is no mocking framework.
If we change the implementation to use a different delegate, if the signature of the delegate is the same, then the test will still pass. If the signature changes, then our code won’t even compile. We’re being as explicit as we can about what we depend on.
Cohesive Software Design
When I’m thinking about Cohesive Software Design, I’m thinking about grouping by business functions and capabilities. Grouping by tasks, by roles, but what the business process and workflows actually are.
By increasing cohesion you can reduce coupling.
Developer-level members of my CodeOpinion YouTube channel get access to the full source for any working demo application that I post on my blog or YouTube. Check out the membership for more info.