Chapter 5: Applying SOLID Principles to Clean Architecture
In this chapter, we explore the SOLID principles—a set of five guidelines that ensure well-structured and sustainable code. When applied alongside Clean Architecture, these principles can transform how we develop software, promoting maintainability, testability, and extensibility.
The SOLID Principles:
1️⃣ Single Responsibility Principle (SRP):
Practical Example:
// Violates SRP:
public class OrderProcessor {
public void ProcessOrder() { /* Processing */ }
public void SendOrderConfirmation() { /* Send Email */ }
}
// Fixed according to SRP:
public class OrderProcessor {
public void ProcessOrder() { /* Processing */ }
}
public class OrderNotifier {
public void SendOrderConfirmation() { /* Send Email */ }
}
2️⃣ Open-Closed Principle (OCP):
Practical Example:
public interface IShape {
double CalculateArea();
}
public class Circle : IShape { /* Implementation */ }
public class Rectangle : IShape { /* Implementation */ }
public class AreaCalculator {
public double Calculate(IShape shape) {
return shape.CalculateArea();
}
}
3️⃣ Liskov Substitution Principle (LSP):
Example:
public class Rectangle {
public virtual int Width { get; set; }
public virtual int Height { get; set; }
}
public class Square : Rectangle {
// Violates LSP: Square modifies expected behavior of Rectangle
public override int Width {
set { base.Width = base.Height = value; }
}
public override int Height {
set { base.Width = base.Height = value; }
}
}
4️⃣ Interface Segregation Principle (ISP):
Recommended by LinkedIn
Example:
public interface IWorker {
void Work();
void Manage();
}
// Fixed:
public interface IWorker {
void Work();
}
public interface IManager {
void Manage();
}
5️⃣ Dependency Inversion Principle (DIP):
Example:
public interface IEmailSender {
void SendEmail(string to, string subject, string body);
}
public class EmailSender : IEmailSender {
public void SendEmail(string to, string subject, string body) {
// SMTP Implementation
}
}
public class NotificationService {
private readonly IEmailSender _emailSender;
public NotificationService(IEmailSender emailSender) {
_emailSender = emailSender;
}
}
Refactoring Techniques and Testability:
public void CalculateOrderTotal() {
decimal total = 0;
foreach (var item in order.Items) {
total += item.Price * item.Quantity;
}
// Other calculations
}
// Refactored:
public decimal CalculateOrderSubtotal(Order order) {
decimal total = 0;
foreach (var item in order.Items) {
total += item.Price * item.Quantity;
}
return total;
}
Using DI (Dependency injection) and Mocking for Testing: Use Dependency Injection and mocking frameworks like Moq to test behavior in isolation.
var mockRepository = new Mock<IProductRepository>();
mockRepository.Setup(repo => repo.GetProductById(It.IsAny<int>())).Returns(new Product { Price = 100 });
var service = new ProductService(mockRepository.Object);
var product = service.GetProductWithDiscount(1, 0.2m);
Assert.Equal(80, product.Price);
Key Tips:
In the next chapter, we'll talk about essential .NET 8 libraries that can help implement Clean Architecture more efficiently. Stay tuned!
And you? Do you apply SOLID principles in your daily work? What challenges do you face when trying to keep your code clean and modular? Comment below!
#CleanArchitecture #SOLID #CSharp #DotNet #SoftwareArchitecture #DevCommunity #MVP #Microsoft