0

At the end of this discussion about coupling and cohesion https://www.youtube.com/watch?v=hd0v72pD1MI some renowned programmers agreed in the general thought that to achieve high cohesion you have to join things, and to achieve low coupling you have to split them.

This seems to be a common thought but I think is totally the opposite. In my opinion the most cohesive module is the one that doesn't exist or have just one small feature, and in the other hand the less coupled module is the one that only depends on itself

In a small example, the following module:

(feature A, feature B, feature C)

has low cohesion and low coupling (no coupling at all).

Trying to achieve high cohesion I will try to split that module:

(feature A)-(feature B)-(feature C)

now I have three high cohesive modules but now the system could become highly coupled.

I see this issue in the Microservices world, where splitting things seems to be equal to "decoupling" your system. Then you have to touch 4 different services and 3 layers in each one to add a simple property, but no one seems to care about that.

I would like to hear your thoughts about this

1
  • This isn’t a question with a clear definitive answer, so isn’t a good fit for this site.
    – Telastyn
    Aug 11, 2021 at 3:07

2 Answers 2

3

I see this issue in the Microservices world, where splitting things seems to be equal to "decoupling" your system. Then you have to touch 4 different services and 3 layers in each one to add a simple property, but no one seems to care about that.

Excellent insight!

This is not just microservices, as you say. Even in monoliths a layered architecture results in you modifying all "layers" most of the time, even for simple things.

"No one seems to care" is also a great insight. Although, I think it has more to do with a lot of bad, and no good examples being widely available.

If you care about maintainability, i.e. if you want to have (business-relevant) changes mostly localized (such as adding a simple property somewhere), you'll find that you need to do things differently to what most projects do.

In short, and this has been years worth of learning (/ unlearning) for me, you'll have to make sure that objects don't publish their data. Having a "getter" in an object basically means that data is used elsewhere, i.e. this object will probably change if any of the usage sites want something differently. This is what cohesion is supposed to prevent. You'll have to include all functionality in the object that is cohesive, including presentation, persistence, etc.

Coming back to your example about adding a property. You can imagine the only way you will have a single place of modification, if you include both UI, "logic" and persistence in the same object. Ironically this is what object-orientation was supposed to do, include function with data.

Regarding microservices, those can be done this way too. If your microservices are not CRUD-based (as most are), but implement a closed, self-contained function, including UI, you will not have this problem of modifying many on a single change.

As you probably realize, all this is dogmatically opposed by most current design / architectural patterns, which always try to split on technical merits. Like always splitting everything UI from everything that is "pure" calculation.

So this is where we're at. You seem to be asking the right questions, so keep asking, don't take anything on authority!

HTH

2
  • 2
    "a lot of bad, and no good examples being widely available" - very true, it's certainly a big part of it. Also, people tend to gravitate towards prescribed solutions, so you end up with these cookie cutter approaches to architecture. These may sometimes work or help get the project off the ground, but only up to a point; they can't do the thinking for you. We may have our disagreements when it comes to certain details (based on our exchanges in comments elsewhere), but I don't think I fundamentally disagree with your views expressed here. Definitely a good answer +1 Aug 11, 2021 at 11:24
  • Thanks Robert! Your answer is very refreshing, at least I don't feel alone in this. As you, I have been learning and unlearning a lot of things to came to this conclusion. Sadly, all current design patterns seems to be following that path. Aug 11, 2021 at 17:15
7

In my opinion the most cohesive module is the one that doesn't exist or have just one small feature, and in the other hand the less coupled module is the one that only depends on itself

That's not really a useful way to look at things. Design is about managing change; if the software didn't change, you wouldn't be too interested in coupling and cohesion. It's not very useful to look at an isolated module from an outside, treating it as a single thing, a black box, and then say that it's highly cohesive and has no coupling. You care about interactions between things.

So, what you're interested in is how cohesive/coupled are different aspects within it (submodules, components, functions, logical groupings of code), with respect to certain types of changes. The word "cohesive" means "it sticks together". If you need to make a change, and you need to edit in three different places, then your code is not cohesive (a related thing is scattered in three places), and is coupled as a result of that (the three places are interdependent). It can be hard to bring these pieces together because of other accidental coupling - each of these pieces can be inadvertently coupled to something else. And the overall code will likely be coupled in other ways, too. So while you have a single module, if the module is not very small and if you didn't take steps to actively control the coupling, within it you'll have a tangled web of interconnections that will get in your way.

Making code more cohesive by restructuring it so that things that are interdependent but scattered come together eliminates some of the coupling between subelements, by confining it into a small, more focused cohesive component with an explicit boundary interface. You then work further to refine component interfaces to minimize inter-component coupling. The goal is to strategically control the coupling, and find a design (or arrive at a design) that'll mostly be conducive for the kinds of changes you can typically expect for your system. You have to study the domain over time to be able to do this.

enter image description here
(You can mentally replace app1, app2 with the more generic component1, component2. Note that going from (b) to (a) may require, among other things, moving parts from one box to the other.)

If you are successful in establishing such a structure, then that allows you to more easily choose a deployment strategy - you can deploy as a monolith, or you can divide it up into a number of libraries, or you can work toward making the system distributed over a network. It's easier to make a physical split if the coupling is minimized.

I see this issue in the Microservices world, where splitting things seems to be equal to "decoupling" your system. Then you have to touch 4 different services and 3 layers in each one to add a simple property

A split based on some arbitrary criterion does not equal decoupling. What you're describing here is exactly the low cohesion/high coupling scenario. It's not at all as simple as splitting things. People have noted before that a team has to be good at design, good at decoupling a monolith internally, before they can be effective at creating decoupled microservices. Because there's always a tradeoff; the distributed nature of microservices makes design problems much, much harder.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.