DEV Community

Cover image for SOLID Principles in NestJS
Geampiere Jaramillo
Geampiere Jaramillo

Posted on

SOLID Principles in NestJS

When building software, keeping your code organized and scalable is key. SOLID is a set of principles that help improve code quality. In this blog, we’ll explore how to apply SOLID in NestJS to write clean, maintainable, and extendable code.

1. Single Responsibility Principle (SRP)

Every class should have one responsibility. This makes your code easier to understand and modify.

Example in NestJS:

@Injectable()
export class UserService {
  constructor(private readonly userRepository: UserRepository) {}

  createUser(data: CreateUserDto) {
    this.userRepository.save(data); // Responsible only for user logic
  }
}
Enter fullscreen mode Exit fullscreen mode

Here, UserService only handles user logic. Validation and other concerns should be separated.

2. Open/Closed Principle (OCP)

Classes should be open for extension, but closed for modification. You can add new features without changing existing code.

Example in NestJS:

interface PaymentStrategy {
  processPayment(amount: number): void;
}

class CreditCardPayment implements PaymentStrategy {
  processPayment(amount: number) {
    console.log(`Processing $${amount} via Credit Card`);
  }
}

@Injectable()
export class PaymentService {
  constructor(private paymentStrategy: PaymentStrategy) {}

  executePayment(amount: number) {
    this.paymentStrategy.processPayment(amount);
  }
}
Enter fullscreen mode Exit fullscreen mode

You can add more payment methods by extending PaymentStrategy without changing PaymentService.

3. Liskov Substitution Principle (LSP)

Derived classes should be substitutable for their base classes without breaking the application.

Example in NestJS:

class Animal {
  makeSound() {}
}

class Dog extends Animal {
  makeSound() {
    console.log('Bark');
  }
}
Enter fullscreen mode Exit fullscreen mode

You can use a Dog wherever an Animal is expected, without issues.

  1. Interface Segregation Principle (ISP)

Clients should not be forced to depend on interfaces they don’t use. Split large interfaces into smaller, specific ones.

Example in NestJS:

interface PaymentProcessor {
  processPayment(amount: number): void;
}

class CreditCardProcessor implements PaymentProcessor {
  processPayment(amount: number) {
    console.log(`Processing $${amount} via Credit Card`);
  }
}

class PaypalProcessor implements PaymentProcessor {
  processPayment(amount: number) {
    console.log(`Processing $${amount} via PayPal`);
  }
}
Enter fullscreen mode Exit fullscreen mode

Each class only implements what it needs, making the code cleaner and easier to manage.

5. Dependency Inversion Principle (DIP)

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

Example in NestJS:

interface Logger {
  log(message: string): void;
}

@Injectable()
export class MyService {
  constructor(private readonly logger: Logger) {}

  executeAction() {
    this.logger.log('Action executed');
  }
}
Enter fullscreen mode Exit fullscreen mode

Here, MyService depends on the Logger interface, not a specific implementation. This improves flexibility and testability.


Conclusion

Applying SOLID principles in NestJS helps you write clean, flexible, and maintainable code. These principles make it easier to test, scale, and evolve your applications without introducing bugs.

Top comments (1)

Collapse
 
nathan_tarbert profile image
Nathan Tarbert

Nice, I always need posts like this when I'm trying to keep my code from turning into a mess.