The article was initially published https://www.linkedin.com/pulse/rep-ccp-crp-software-architecture-principles-after-solid-jahid-hasan-ksn5f/
You have probably seen tons of articles and lectures on SOLID principle which is really great for software engineers but have you ever asked yourself that what comes after SOLID principle? What should be the next approaches should we learn and implement? Let's talk about what comes after SOLID principle in software engineering.
In Software Engineering, we often ask ourselves : which files should be grouped together in a single directory (component), how can we architect modules properly, what are the rules can guide us these decision?
Unfortunately many of software engineers make this crucial decision based on an ad hoc manner and almost entirely on context. And finally we come out with a solution that must has a `common module` when we cannot decide the codes / classes should belongs to here. And it shows us how poorly the software is architected.
So in this article, I will try to explain the Three most important principles of Component Cohesion along with proper examples.
1. Reuse/Release Equivalence Principle (REP)
The granule of reuse is the granule of release.
It says : If you want someone to reuse your codes, you cannot just throw it to them randomly. You have to package it as a release with proper versioning and release notes.
So let's say you have built a module called auth which gives a reusable authentication service with multiple providers. Now you want to let the people reuse it, to make it reusable you have to follow Reuse/Release Equivalence Principle (REP). This principle says that you must have to package it with proper versioning and release notes. The bellows two examples showing the proper versioning with release notes.
auth@1.0.0 (Initial Version)
|- auth/
| |-- index.js
| |-- googleSSO.js
| |-- customSSO.js
| |-- package.json
| |-- README.md
| |-- CHANGELOG.md {
"name": "auth",
"version": "1.0.0",
"description": "A auth module with multiple SSO providers",
"main": "index.js",
"scripts": {
"lint": "eslint ."
},
"keywords": ["auth", "SSO"],
"author": "Author's name",
"license": "MIT"
} # Changelog
## [1.0.0] - Current Date
### Features
- Auth module which support multiple SSO providers:
- `googleSSO` for Google login
- `customSSO` for custom authentication
- Exported all functions through `index.ts ` for easy reuse Now let's say we have updated the auth module by adding more features and fixes some issues, hence we need to update the versioning as well as release notes. Here is the next example for next versions of auth module.
auth@1.1.0 (next release)
- auth/
|-- index.js
|-- googleSSO.js
|-- facebookSSO.js // New file
|-- customSSO.js
|-- package.json
|-- README.md
|-- CHANGELOG.md {
"name": "auth",
"version": "1.1.0",
"description": "A auth module with multiple SSO providers",
"main": "index.js",
"types": "index.d.ts",
"scripts": {
"lint": "eslint ."
},
"keywords": ["auth", "SSO"],
"author": "Author's name",
"license": "MIT",
}, # Changelog
## [1.1.0] - Current Date
### Features
- Support for Facebook SSO (`facebookSSO.js`)
### Fixed
- Resolved id-token parsing issue in `googleSSO.js`
---------------------------------------------------------------
## [1.0.0] - Previous Date
### Features
- Initial release with support:
- Google SSO
- Custom SSO (Custom Auth Support) There is a rule to update the version number properly. This rule is called semver. You can see more about semver here https://semver.org/ .
2. Common Closure Principle (CCP)
Gather into components those classes that change for the same reasons and at the same times.Separate into different components those classes that change at different times and for different reasons.
This principle is one of my most favorite one. This principle helps me to decide
how can I organize modules, components, classes, files etc.
where they should belongs.
how can i group them all.
etc.
This principle is similar like SRP (Single Responsibility Principle). Now let's understand this principle with proper example.
Let's say we have two components like below. One is for authentication (auth/) other one is for payment (payment/) and both have their domain related files , classes. Now let's say there is a feature which will perform sending the email to users if user get signed-up or user make any payments. Here is our first design.
|- auth/
| |-- index.js
| |-- login.js
| |-- signup.js
| |-- signupEmail.js
|- payment/
| |-- index.js
| |-- saveCard.js
| |-- payment.js
| |-- paymentEmail.js Now let's have a first look, it seems that signupEmail.js and paymentEmail.js are doing there job independently as SRP suggests. However what happen if we need to change the email provider? In this scenario, we have to rewrite both component's (paymentEmail.js, signupEmail.js) file which actually breaks the Common Closure Principle (CCP).
So how can we implement the Common Closure Principle (CCP) here. Hence signupEmail.js and paymentEmail.js doing similar type of job like sending email, we can gather them into a single component like below.
//Good Design
|- auth/
| |-- index.js
| |-- login.js
| |-- signup.js
|- payment/
| |-- index.js
| |-- saveCard.js
| |-- payment.js
|-email/
| |-index.js
| |-templates/
| | |-signup.js
| | |-payment.js Now if we have to change anything that is related to email (e.g providers), we only have to change in email component and can be deployed it independently.
This approach actually groups the codes that changes together and it is known as Common Closure Principle (CCP)
3. Common Reuse Principle (CRP)
Don’ t force users of a component to depend on things they don’ t need.
This is the generic version of ISP (Interface Segregation Principle). The ISP told us not to depend on the modules / classes which have methods that we don't use. Here similarly Common Reuse Principle (CRP) says not to depend on Component that has classes / modules we do not use.
Now let's look at the following component as example. If we need to change stripe.js code, it does means we are changing whole payment component for stripe.js changes .
|-payments/
|-- index.js
|-- stripe.js
|-- paypal.js And it breaks Common Reuse Principle (CRP). The paypal.js is not dependent on stripe.js, but for the changes of stripe.js whole component along with paypal.js need to redeploy.
So how can we design the components in good way, here is an example.
// Good Design
|-payments/
|-- stripe/
| |-- index.js
| |-- processPayment.js
|-- paypal/
| |-- index.js
| |-- processPayment.js Now you can change stripe's component inside it, it doesn't depend on paypal anymore. both are segregated with different components and deployable independently.
Software Engineering is not just writing codes, now days code can be written through AI Agents, But architecting the Software is still a crucial parts in software engineering. To know how to architect a software, we must need to know about software engineering principles such as SOLID along with REP, CCP, CRP. There are limited articles on REP, CCP, CRP in the internet compared to SOLID principle, That why I find interest writing this articles on this specific topic.
