| 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 |
|