Coupling & Cohesion
Tags: mashcuylabchallenge, softwareengineering
Published: 2023-08-13 05:37:01

# Introduction

Coupling and Cohesion are two classic concepts in software engineering. In this article I explore ways to apply this concepts at multiple levels of any project.

# Unit

First of all, is important to define what a _Unit_ is in the context of this article:

- a unit is an element that compose a complex structure.
- a unit can be classified as:
    - methods
    - classes
    - packages
    - services

# Coupling

## what is?
- coupling is the degree of _interdependency_ between __units__.
- coupling is the degree of how a unit `uses` other units directly.

## how this affects our projects?

a __highly coupled project__ can lead to problems like: 

- when a unit needs to be modified you `must`, as an `obligation`, to change the other units.
- code that is hard to change and optimize.

## how can I deal with highly coupled units?

there will always be a coupling degree in any project, the objective is to reduce that number.

### in methods

in the example below `registry` methods are deeply coupled to the `register_vehicle` method.

```python
class Application:
    def register_vehicle(self, brand: string):
        registry = VehicleRegistry()
        vehicle_id = registry.generate_vehicle_id(12)
        license_plate = registry.generate_vehicle_license(vehicle_id)
        catalogue_price = registry.calculate_price(license_plate, brand, vehicle_id)
        ...
```

to reduce coupling I moved all that business logic inside the `registry service`. 

```python
class Application:
    def register_vehicle(self, brand: string):
        vehicle_repository = VehicleRepository()
        registry_service = VehicleRegistryService(vehicle_repository)
        vehicle = registry_service.register_vehicle(brand)
        ...
```

### in classes

the class `CourierController` has a concrete class `Whatsapp` as a dependency.

```typescript
export default class CourierController {
  constructor (private readonly courier: Whatsapp) {
  }
}
```

to reduce coupling I created an `interface` or `abstract` class to be used by `CourierController`.

```typescript
export default class CourierController {
  constructor (private readonly courier: Courier) {
  }
}
```

### in packages

many MVC frameworks use a coupled package structure. the controller package use views, and the views package use models.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/huo6at8ezs1bx8tp0514.png)

to reduce coupling I applied `vertical slicing` and structured the project based on business entities.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lbn8k8ilt2qe4k9j9ja9.png)

### in services

when a project grows is common to have somethings like the image below, services are consumed by other services and this can produce performance issues in the architecture.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dsqf8893zcegz8jgjvgp.png)

to reduce coupling between services, a `queue` can be used, this allows to process expensive operation asynchronously and decouple the architecture.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/997y3r6neitl9uigtjpu.png)

# Cohesion

## what is?

- cohesion is the degree of relation of the __elements__ inside a __unit__.
- cohesion is the degree is which every __element__ inside a __unit__ are directed towards perform a single task.

## how this affects our projects?

a poorly cohesive project can lead to problems like:

- having many responsibilities for the same unit.
- code that is hard to test and maintain.

## how can I deal with poorly cohesive units?

### in methods

in the code below the internal elements of `register_vehicle` have multiple responsibilities.

```python
class Application:
    def register_vehicle(self, brand: string):
        # First responsability
        registry = VehicleRegistry()
        vehicle_id = registry.generate_vehicle_id(12)
        license_plate = registry.generate_vehicle_license(vehicle_id)

        # Second responsability
        catalogue_price = 0
        if brand == "Tesla Model 3":
            catalogue_price = 60000
        elif brand == "Volkswagen ID3":
            catalogue_price = 35000
        elif brand == "BMW 5":
            catalogue_price = 45000

        tax_percentage = 0.05
        if brand == "Tesla Model 3" or brand == "Volkswagen ID3":
            tax_percentage = 0.02

        payable_tax = tax_percentage * catalogue_price

        # Third responsability
        print("Registration complete. Vehicle information:")
        print(f"Brand: {brand}")
        ...

```

to improve cohesion I moved all that business logic inside the `registry service`. Now the elements inside the `register_vehicle` method are towards the same goal.

```python
class Application:
    def register_vehicle(self, brand: string):
        vehicle_repository = VehicleRepository()
        registry_service = VehicleRegistryService(vehicle_repository)
        vehicle = registry_service.register_vehicle(brand)
        vehicle.print()

```

### in classes

in the example below the class `Utils` have multiple responsibilities. This class will grow without any business role or good practice and will became a `god class`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/60675lqnsfc095ly2ilx.png)

to address this and improve cohesion, I relocated each of those methods to their respective classes, aligning them with their corresponding responsibilities.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2r0ywkt3031qqz01t5z3.png)

### in packages

in the example below a basic hexagonal architecture structure is shown. However, the elements within each package lack cohesion as they serve different purposes.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/309tirem0j36atf1shqf.png)

to solve this, screaming architecture can be applied.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3cw0sy2vq4amk8im054h.png)

### in services

in the example below two services `Sales and Invoicing` are shown. Both services have multiple responsibilities apart from its main ones, such as Auth, Email sending, AWS access, etc. This indicates a lack of cohesion within the architecture.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/czo5hx43fd5l2zbb8cw7.png)

to solve this I grouped the admin services and the external services into their proper units and then I referenced those units from the Sales and Invoicing services.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2kg2lxzut80zqiejumx1.png)

# References

- [SOLID Principles? Nope just coupling and cohesion](https://www.youtube.com/watch?v=YDNR_gfBk0Q)
- [Cohesion and coupling](https://www.youtube.com/watch?v=eiDyK_ofPPM&t=3s)
- [Learn Hexagonal Architecture](https://www.youtube.com/watch?v=eNFAJbWCSww)