Chapter 5: Applying SOLID Principles to Clean Architecture

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):

  • A class should have only one reason to change. It means a class should have only one responsibility.

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):

  • Software entities should be open for extension but closed for modification.

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):

  • Subtypes must be substitutable for their base types without altering the correctness of the program.

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):

  • Clients should not be forced to depend on interfaces they do not use.

Example:

public interface IWorker {
    void Work();
    void Manage();
}

// Fixed:
public interface IWorker {
    void Work();
}
public interface IManager {
    void Manage();
}        

5️⃣ Dependency Inversion Principle (DIP):

  • High-level modules should not depend on low-level modules. Both should depend on abstractions.

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:

  • Extract Method: Extract blocks of code into smaller, descriptive methods for better readability.

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:

  • Constantly refactor to keep the code clean and understandable.
  • Apply TDD (Test-Driven Development) to enhance code quality and detect faults early.
  • Ensure each class has a clear responsibility to make testing and modifications easier.


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

To view or add a comment, sign in

Insights from the community

Others also viewed

Explore topics