React Architectural Design: A focus on cohesion and coupling.
Tags: webdev, programming, react, frontend
Published: 2023-03-20 09:42:13

One of the major design principles of Software Architectural design is to 

> increase cohesion and reduce coupling anywhere possible.

You might've stumbled upon this rule somewhere but in this article, I hope to explain it in the context of developing a react application.

Let us start by first defining what cohesion and coupling both mean in an engineering context.


![Image of several modular connections](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/22ilt4sbzx57ew8j8zwq.jpg)
Photo by <a href="https://unsplash.com/@guerrillabuzz?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">GuerrillaBuzz</a> on <a href="https://unsplash.com/photos/RIvSJTiGwLc?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
  

> ##Cohesion
It is defined as the degree of relationship between elements of the same module. It focuses on how a single class is designed. The higher the cohesiveness of the class, the better the design. Some of the benefits of higher cohesion is that highly cohesive classes are much easier to maintain and less frequently changed.

> ##Coupling
It is defined as the degree of interdependence between the modules. It is about how much one module depends on or interacts with other modules.


Okay, now that we have a basic understanding of what cohesion and coupling both mean, let's move on to their effects and how best to approach reducing coupling and increasing cohesion to get a truly efficient react application.


## 1. Structure is key.

It is critical to programming to always create a good structure for your files in order to write higher level readable codes. When we talk about software architecture, in this context we are basically talking about its structure. When designing the structure of your react application it is preferable to either group modules by features/routes or by file type. You can also break down modules and group them in folders based on their category or function.


```
 src
├── components
│   ├── navbar.js
│   ├── component.js
│   └── footer.js
├── utils
│   ├── test-utils
│   │   ├── test-utils.js
│   │   └── index.js
│   |       
├── layout
│   ├── about.js
│   ├── contact.js
│   |
├── index.js
```


In the example above you can see that we're grouping our modules by file type. Take note of how we arrange the folder and file structure. 


## 2. Make your modules self-reliant.

You do not want your modules to heavily rely on each other, it is counter-intuitive and defeats the purpose we are trying to achieve. If elements are loosely coupled, changing one element does not affect or stir on changes in other elements hence driving up the levels of cohesiveness.


```
(Bad example)

import Button from "../button";

const App = () => {
  return (
    <div>
     <form action="">
       <input type="text" />
       <Button />
     </form>     
    </div>
  );
};
```


Any changes made to the `button` component will affect all modules that make use of it.


```
(Good example)

const App = () => {
  return (
    <div>
     <form action="">
       <input type="text" />
       <button type="submit">Get Started<button/>
     </form>     
    </div>
  );
};
```

In the example above, the button can be changed and styled as you please without causing ripples in the fabric of your application.


## 3. Be careful when passing down data between components.

We already know by now that in a highly cohesive application, a change in one module will not affect other modules adjacent to it but we need to make sure of this by being careful when passing down data. 


```
Header.js (good)

const Header = () => {
  return (
    <div className=''>
        <div className=''>
          <Link to="" className='self-center'>
            <p className=''>{"Title"}</p>
          </Link>
        </div>
        <hr className=''/>
    </div>
  )
}
```


In the code block above we have a component that is static and has no outside data constraints to it at all. This is always good as you can change contents of `p` tag as much as you want without causing a chain of changes.


```
(good and bad)

const Header = (props) => {
  return (
    <div className='font-poppins mb-12'>
        <div className=''>
          <Link to="" className='self-center'>
            <p className=''>{props.title}</p>
          </Link>
        </div>
        <hr className=''/>
    </div>
  )
}
```


In the example above you can see that we are passing a `prop` into our `Header` component and this component will be reused (coupled) through our application. This is a good practice because it makes the component more reusable. You can make changes to the title of the header without having to manually alter the Header.js file. But on the negative side if there are too many props in one component, a simple change will have a widespread effect that may not be easily manageable.


## 4. Elements in a module must have distinct and coordinated roles.

What roles do your intended function/component play in a page. Is it needed, will changing it affect another component in the module, have you structured them semantically to fit the flow of the page, is it independent, and with a single, well-defined purpose? These are some questions you must ask yourself during your developmental process.

Looking at the example below we can see that
roles is an external json file which has been imported containing hard-coded information about user roles.

Any changes made to the values inside roles.json will have no breaking effect on your file.


```
Dropmenu.js

import React from "react";
import { roles } from "../data";

const Dropmenu = () => {
  return (
    <>
      {roles.map((role, key) => (
        <div key={key} >
            {role.title}
        </key>
      ))}
    </>
  );
};

export default Dropmenu;
```


## NB: Using CSS frameworks

You need to be conscious in a situation you're using a CSS framework like Tailwind where your styles will advertently have to be inline. Tailwind follows a cohesive approach because most, if not all, your style pertaining to a particular module will be written in that module so changing its styles will not have a widespread consequence on the application.
Take for an example the code block below;


```
const Header = (props) => {
  return (
    <div className='font-poppins mb-12'>
        <div className='text-lg lg:text-2xl my-4'>
          <Link to="" className='self-center flex'>
            <p className='text-[#4E4A5A] text-[28px] leading-9 
             px-20'>{props.title}</p>
          </Link>
        </div>
        <hr className='h-0.5 border-none bg-neutral-200 
         shadow-sm'/>
    </div>
  )
}
```


You can see the styles have been written inline, if this component is reused in your application, no further changes to the CSS will be needed because it hardcoded.

## Outro

So far, we have gone over some of the steps to effectively reduce cohesion and increase coupling. It is worth noting that they are not independent terms and as so they cannot exist without each other. A system can't be 100% totally cohesive because there will always be a degree of coupling at play. 
It is worthy to also note the context in which the application is being built; the levels of cohesion might depend on the level of the project or it's requirements.

I hope by now you've understood a little bit better how to write highly scalable code. Thank you for reading and please if you have any questions, feedback or more useful information to add, please leave a comment down below.