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
}
}
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);
}
}
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');
}
}
You can use a Dog wherever an Animal is expected, without issues.
- 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`);
}
}
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');
}
}
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)
Nice, I always need posts like this when I'm trying to keep my code from turning into a mess.