Single Responsibility: a module should have one reason to change. This means it serves one actor (one group of users or one business role). Example: invoice calculation and invoice PDF rendering live in separate classes.
Open/Closed: code should be open for extension but closed for modification. Example: to add a PayPal provider, you add a class that implements PaymentProvider. You do not edit a switch in CheckoutService.
Liskov Substitution: you must be able to use a subtype anywhere the base type is expected. Example: every UserRepository implementation, in-memory or Postgres, must behave the same for findById.
Interface Segregation: clients should not depend on methods they don't use. Example: a report job depends on OrderReader. It does not depend on a big interface that also has delete and bulkImport.
Dependency Inversion: high-level modules depend on abstractions (interfaces), not on details (concrete classes). Example: OrderService depends on a Notifier interface, and SesEmailNotifier implements it.
- SRP: a module has one reason to change, because it serves one actor.
- OCP: you extend behaviour by adding code, not by editing working code.
- LSP: every implementation must keep the base type's contract.
- ISP: use small interfaces made for each client. DIP: depend on abstractions, not concrete classes.