CHAPTER 09
Intermediate
SOLID Principles for Clean Architecture
Updated: May 16, 2026
35 min read
# CHAPTER 9
SOLID Principles for Clean Architecture
1. Introduction
If DRY, KISS, and YAGNI are the street-smarts of programming, the SOLID principles are the academic foundation of enterprise architecture. Introduced by Robert C. Martin, SOLID is an acronym representing five design principles intended to make software designs more understandable, flexible, and maintainable. When code is tightly coupled, a single change in one file breaks ten other files. SOLID principles act as the architectural blueprint to decouple your code, allowing a massive system to scale infinitely without collapsing under its own weight. In this chapter, we will dissect each of the five principles, translating dense academic theory into practical, real-world engineering.2. Learning Objectives
By the end of this chapter, you will be able to:- Define and apply all five SOLID principles.
- Understand how SRP prevents "God Classes."
- Utilize the Open/Closed Principle (OCP) to extend features without editing old code.
- Ensure polymorphism safety using Liskov Substitution (LSP).
- Decouple systems using Interface Segregation (ISP) and Dependency Inversion (DIP).
3. S - Single Responsibility Principle (SRP)
*A class should have one, and only one, reason to change.*-
The Problem: A
Userclass holds user data, formats the data into HTML, and saves the data to the SQL database. If the HTML styling changes, the class changes. If the database schema changes, the class changes. This is a fragile "God Class."
- The Solution: Split it up.
-
1.
User(Holds data).
-
2.
UserView(Renders HTML).
-
3.
UserRepository(Saves to SQL).
4. O - Open/Closed Principle (OCP)
*Software entities should be open for extension, but closed for modification.*-
The Problem: You have a
DiscountCalculatorthat checksif ($type == 'VIP')andelse if ($type == 'HOLIDAY'). Tomorrow, marketing adds a 'SUMMER' discount. You have to open this existing file and modify the core logic, risking breaking the VIP logic.
-
The Solution: Use polymorphism (Interfaces). Create a
DiscountInterface. Create separate classes:VipDiscount,HolidayDiscount,SummerDiscount. TheDiscountCalculatoraccepts the interface. To add a new discount, you just write a *new* class. The old classes are "closed" to modification, but the system is "open" to extension.
5. L - Liskov Substitution Principle (LSP)
*Objects of a superclass shall be replaceable with objects of its subclasses without breaking the application.*-
The Problem: You have a
Birdclass with afly()method. You create a subclassPenguin extends Bird. If another part of the system loops through an array ofBirdsand callsfly(), thePenguinwill throw a fatal error.
-
The Solution: A Penguin is not a flying bird. Fix the abstraction. Have a generic
Birdclass, and an interfaceIFlyable. Only birds that actually fly implementIFlyable.
6. I - Interface Segregation Principle (ISP)
*No client should be forced to depend on methods it does not use.*-
The Problem: You create an
IWorkerinterface withwork()andeat()methods. AHumanimplements both. ARobotimplements it, but robots don't eat. TheRobotclass is forced to have a useless, emptyeat()method.
-
The Solution: Segregate (split) the massive interface into smaller, specific ones. Create
IWorkableandIFeedable. The Human implements both; the Robot only implementsIWorkable.
7. D - Dependency Inversion Principle (DIP)
*High-level modules should not depend on low-level modules. Both should depend on abstractions (interfaces).*-
The Problem: A
PaymentProcessorclass creates a newStripeAPI()directly inside its constructor. The processor is tightly coupled to Stripe. If the business switches to PayPal, you have to rewrite the PaymentProcessor.
-
The Solution: The
PaymentProcessorshould ask for a genericIPaymentGatewayinterface in its constructor. At runtime, you inject either theStripeAdapteror thePayPalAdapter. The high-level logic now has zero dependency on the low-level API details. (This is often achieved via Dependency Injection).
8. Diagrams/Visual Suggestions
*Dependency Inversion Flow*
txt
9. Best Practices
- Design for Change: SOLID principles are not just academic theories; they are a direct response to the reality that business requirements change constantly. Applying OCP and DIP ensures that when a manager asks for a change, you write *new* files instead of surgically altering *old*, working files.
10. Common Mistakes
- Over-Engineering (Violating KISS): Do not apply SOLID principles preemptively to a 10-line script. If an application is tiny and will never change, massive interfaces and dependency injection containers are a waste of time. Apply SOLID when the codebase begins to grow and feel painful to maintain.
11. Practice Exercises
- 1. Provide a real-world example of how violating the Single Responsibility Principle (SRP) makes code harder to test.
- 2. Explain the Liskov Substitution Principle (LSP) using a Square and Rectangle example.
12. MCQs with Answers
Question 1
A developer needs to add a new "Bitcoin" payment method to an app. Instead of modifying the massive PaymentManager class, they create a new BitcoinProcessor class that implements an existing PaymentInterface. Which SOLID principle did they just follow?
Question 2
What is the primary architectural goal of the Dependency Inversion Principle (DIP)?
13. Interview Questions
- Q: Explain the Single Responsibility Principle (SRP). How do you know when a class has taken on too many responsibilities? (Hint: Explain the concept of "Reasons to change").
- Q: Walk me through the Interface Segregation Principle (ISP). Why are "fat interfaces" dangerous for clients that consume them?
- Q: You are building an email notification system. How would you use the Dependency Inversion Principle (DIP) to ensure the system can switch between SendGrid and Mailchimp without rewriting the core business logic?