Cohesion, Coupling and Composition
Definitions
Cohesion and coupling are two key software design concepts that measure the quality of a system design. Good system design aims for high cohesion and loose coupling. Further, the different components of a system must communicate with each other using clearly defined or industry standard interfaces.
Composition is the mechanism or ease with with different system modules are assembled together. Modules can be composed statically or dynamically.
Cohesion
Cohesion measures the degree to which elements within a single module or component are dependent upon each other and serve a single purpose or function.
Coupling
Coupling refers to the degree of interdependence between software modules. High coupling means that modules are closely connected and changes in one module may affect other modules. Loose coupling means that modules are independent and changes in one module have little impact on other modules.
# Why Loose Coupling is Good
โโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโ โ Limit change radius โ โ Higher agility โ โโโโโโโโโโโโโโ
โ โโโโโโโบโ (design-time) โโโโโโโบโ and veolcity โโโโโโโบโ โ
โ Loose โ โโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโ โ Cost and โ
โ Coupling โ โโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโ โ Complexity โ
โ โโโโโโโบโ Limit error radius โโโโโโโบโ Reliable, tolerable โโโโโโโบโ โ
โโโโโโโโโโโโโโโ โ (run-time) โ โ operations โ โโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโCoupling can occur over many dimensions and facets. A system can never be entirely decoupled, some degree of coupling at various levels must be tolerated. The appropriate level of (design-time) coupling depends on the level of control you have over the endpoints .However, the interfaces between the endpoints must be clearly defined.
| Coupling Facets | Examples |
|---|---|
| Technology dependency | Java vs C++ |
| Location dependency | IP address, DNS |
| Data format dependency | Binary, XML, JSON, Protobuf, etc |
| Data type dependency | int16/int32, string/UTF-8, Null/empty, etc |
| Semantic dependency | Name, Middlename, ZIP |
| Temporal dependency | sync, async, pub/sub |
| Interaction style dependency | messsaging, RPC, GraphQL |
| Conversation dependency | pagination, caching, retries |
Components of a Software Application
Software applications are typically composed of three different components or layers:
1. Application Environment
This includes all the dependencies for the actual application code - the user space environment, runtime and the external libraries required by the actual application code. I.e. all the support code required to run the actual application.
2. Application Code
This is the actual application itself, either in source or binary form. The runtime and external library dependencies are already provided by the previous component.
This also includes any configuration required to locate dependent services like DB, SMTP gateway, etc.
3. Application Data
This is the data managed by the application in databases, files or external storage services like an S3 object storage bucket.
Examples
| 1. App Environment | 2. App Code | 3. Data |
|---|---|---|
| Tomcat, Java JRE | app.jar | Oracle DB and local files |
| Python interpreter, python modules | app.py | Postgres DB and local files |
| Apache, PHP, PHP libraries | index.php | MySQL DB and uploaded files |
| Nginx, .NetCore runtime | app.exe | MS SQL Server and S3 object storage |
| Distroless | golangapp.exe | ClickHouse DB |
Cohesion and Coupling using Docker
Docker offers an excellent mechanism for architecting systems that follow the cohesion, coupling and composition best practices.
1. Application Environment
Package the application runtime and libraries into a docker container. Do NOT package the application code into the container, that will be bind mounted from the docker host.
PHP
- user space components (ubuntu-minimal)
- install apache, php and common php libraries (php-mysql, etc)
Java
- user space components (ubuntu-minimal)
- install tomcat, JRE
.NetCore
- user space components (ubuntu-minimal) or distroless
- install dotnetcore runtime
2. Application Code
Inject the application code into the runtime component by bind mounting the code from the docker host into the running container instance. This creates a isolated "VM like" environment for the code to run. Different runtime versions can be run on the same docker host with full isolation.
As a security best practice, the application code can be mounted into the container without write access. As docker containers are ephemeral and do not save state when removed, recovering a compromised container involves simply stopping/deleting the container and starting a new one in its place (docker-compose down; docker-compose up -d).
3. Application Data
Similar to application code, the data files required for the application can stored on the docker host and bind mounted inside the container at runtime. External services like databases can be run as separate containers within a single docker-compose file or on external systems.
Complete Example - Deploying WordPress
WordPress requires
- A minimal userland, Apache web server with mod-PHP and a list of PHP libraries. The php-apache-ubuntu container provides all these components.
- The actual WordPress application with modules and themes. These files are bind mounted from the docker host into the container in
/var/www/html. - A location to store file the media library either in an uploads folder, or external S3 compatible storage. The uploads folder
wp-contentis part of the/var/www/htmlmount. - Supported database server like MySQL/MariaDB. The DB can be provided as an additional service in the same docker-compose or run as a separate docker service on the host.
Example WordPress docker-compose.yaml
# Filename: /src/example.com/docker-compose.yml
# WordPress website for example.com
version: "2.4"
name: example.com
services:
example-com:
image: rsubr/php-apache-ubuntu:jammy
container_name: example.com
restart: always
environment:
- WORDPRESS_DB_HOST=${WORDPRESS_DB_HOST}
- WORDPRESS_DB_USER=${WORDPRESS_DB_USER}
- WORDPRESS_DB_PASSWORD=${WORDPRESS_DB_PASSWORD}
- WORDPRESS_DB_NAME=${WORDPRESS_DB_NAME}
volumes:
- ./www:/var/www/html
labels:
- com.centurylinklabs.watchtower.enable=true
- traefik.enable=true
- traefik.http.routers.example-com.rule=Host(`example.com`, `www.example.com`)
- traefik.http.routers.example-com.tls=true
- traefik.http.routers.example-com.tls.certresolver=lets-encrypt