Skip to content

SOLID Principles in Drupal

I need to... Guide Summary
Understand how Drupal embodies SOLID Overview Understanding how Drupal's architecture embodies SOLID principles guides you to write code that aligns with core patterns rather than fighting them.
Keep services focused on single responsibilities Services & SRP Every service should have one reason to change. If a service handles node access grants AND email notifications, split it.
Split fat controllers into services Controllers & SRP Controllers should only handle HTTP request/response. Business logic belongs in services.
Organize Drupal 11 OOP hook classes Hook Classes & SRP Drupal 11.1+ supports OOP hooks using class methods with #[Hook] attributes. Organize hook classes by domain concern, not by hook type.
Use plugins to extend without modification Plugin System & OCP Plugins are Drupal's primary OCP mechanism -- extend functionality without modifying existing code. Use plugins for blocks, field formatters/widgets, views plugins, etc.
Use hooks and events for extension Hooks & Events & OCP Hooks and event subscribers let you extend behavior without modifying existing code. Use hook_alter() for data modification, events for architectural extension points.
Override config safely Config Overrides & OCP Config overrides let you change configuration values without modifying stored config. Use for environment-specific settings (dev/staging/prod).
Understand entity hierarchy substitutability Entity Hierarchy & LSP Drupal's entity system follows LSP -- any ContentEntityBase subclass (Node, User, Term) can be used wherever ContentEntityBase is expected. Behavioral contracts must be preserved.
Choose the right form base class Form Hierarchy & LSP Drupal form base classes follow LSP -- extend the right base for your use case. ConfigFormBase adds config-specific behavior; SettingsForm adds protected config behavior.
Combine access results correctly Access Results & LSP AccessResult follows LSP via three immutable subclasses: Allowed, Forbidden, Neutral. They can be combined with orIf()/andIf() and always produce valid AccessResultInterface.
Design role-based interfaces Entity Interfaces & ISP Drupal provides role-specific entity interfaces. Implement only the interfaces your entity needs -- don't implement EntityPublishedInterface if your entity can't be published.
Use correct injection interfaces Injection Interfaces & ISP Drupal provides focused interfaces for dependency injection. Use ContainerInjectionInterface for static::create() factory method, or AutowireTrait for automatic dependency resolution.
Depend on abstractions not implementations Service Container & DIP The service container is Drupal's DIP foundation. Depend on abstractions (interfaces) defined in core.services.yml, not concrete implementations.
Inject dependencies correctly Dependency Injection Patterns & DIP Inject all dependencies through constructor. Use autowiring in Drupal 10.2+ to reduce boilerplate.
Avoid static service calls Anti-Patterns & DIP Recognize these patterns as violations of DIP. They create tight coupling, prevent testability, and make code fragile.
Structure a module following SOLID Module Architecture Structure your custom module following SOLID principles. This section shows recommended directory layout and organization patterns.
Review code for SOLID compliance Best Practices Checklist Use this checklist during code review to verify SOLID compliance.
Identify SOLID violations Common Mistakes Recognize these patterns as SOLID violations. Understanding WHY they're bad helps you avoid them and spot them in code review.
Find core examples of each principle Code Reference Map