.NET Core  

🍁Mastering SOLID Principles in Real-World .NET Projects

✅ Single Responsibility Principle (SRP)

// One class = One job

Every class should have only one reason to change. This means no mixing database logic with business logic or API response formatting with validation rules.

💡 Real-World .NET Example

// Bad: Doing too much
public class UserManager
{
    public void Register(User user) { /* logic */ }
    public void SaveToDb(User user) { /* DB logic */ }
    public void SendWelcomeEmail(User user) { /* Email logic */ }
}
// Good: SRP Applied
public class UserService
{
    public void Register(UserDto dto) { /* business rules */ }
}
public class UserRepository
{
    public void Save(User user) { /* DB only */ }
}
public class EmailSender
{
    public void SendWelcomeEmail(User user) { /* Email only */ }
}

✅ Open/Closed Principle (OCP)

Open for extension, closed for modification

Your code should allow new behavior without changing existing code. This avoids regressions and makes it easier to add future features.

💡 Real-World Example

public interface IDiscountStrategy
{
    decimal ApplyDiscount(decimal price);
}
public class HolidayDiscount : IDiscountStrategy
{
    public decimal ApplyDiscount(decimal price) => price * 0.9m;
}
public class NoDiscount : IDiscountStrategy
{
    public decimal ApplyDiscount(decimal price) => price;
}

By injecting IDiscountStrategy, you can add new discounts without changing your OrderService.

✅ Liskov Substitution Principle (LSP)

Subtypes must be substitutable for their base types

Any derived class should be replaceable for its parent without breaking functionality.

Anti-Pattern

public class Rectangle
{
    public virtual void SetWidth(int width) { }
}
public class Square : Rectangle
{
    public override void SetWidth(int width)
    {
        throw new NotImplementedException(); // LSP Violation
    }
}

Instead, design with interfaces or composition when inheritance breaks behavior.

✅ Interface Segregation Principle (ISP)

Keep interfaces small and focused

Clients shouldn't be forced to implement methods they don’t use. This avoids bloated and confusing code.

💡 Better Design

public interface ILoginService
{
    void Login(string user, string password);
}
public interface IRegistrationService
{
    void Register(UserDto dto);
}

Instead of forcing everything into a single IUserService, split interfaces by responsibility.

✅ Dependency Inversion Principle (DIP)

Depend on abstractions, not concrete classes

High-level modules (like OrderService) should depend on interfaces, not low-level classes like SqlOrderRepository.

💡 Real-World Example

public interface IEmailService
{
    void SendEmail(string to, string message);
}
public class EmailService : IEmailService
{
    public void SendEmail(string to, string message)
    {
        // SMTP logic
    }
}
public class NotificationManager
{
    private readonly IEmailService _emailService;

    public NotificationManager(IEmailService emailService)
    {
        _emailService = emailService;
    }

    public void Notify(string msg)
    {
        _emailService.SendEmail("[email protected]", msg);
    }
}

In Program.cs

services.AddScoped<IEmailService, EmailService>();

🚀 How to Apply SOLID Principles Efficiently While Working?

  • Start with interfaces, then build implementations.
  • Don’t mix responsibilities: Separate service, repository, controller.
  • Write unit tests: If a class is hard to test, it’s probably violating SOLID.
  • Utilize design patterns:such as Strategy, Factory, and Mediator, to simplify SOLID principles.
  • Think like Lego: Compose functionality from small, testable bricks.

🌟 Final Thoughts

Mastering SOLID is like upgrading your dev brain. You’ll not only write clean and testable code but also scale faster, debug smarter, and survive enterprise-level chaos like a pro.

Want feedback on how SOLID your current codebase is? Drop a sample, and let's refactor it together!

Happy coding, and stay SOLID ✌️