Dependency Injection Anti-Patterns - DIP
When to Use
Recognize these patterns as violations of DIP. They create tight coupling, prevent testability, and make code fragile.
Decision
| Anti-pattern | Why it's bad | Fix |
|---|---|---|
\Drupal::service() in classes |
Untestable, hides dependencies | Constructor injection |
\Drupal::entityTypeManager() |
Static coupling to global | Inject EntityTypeManagerInterface |
\Drupal::database() |
Bypasses entity API, tight coupling | Use entity storage or inject connection |
new ClassName() in business logic |
Can't inject dependencies into created object | Factory service or dependency |
| Global $user | Untestable, deprecated | Inject CurrentUserInterface |
Pattern
BAD: Static service calls (violates DIP)
class ArticleManager {
public function publish($nid) {
// All static calls - untestable, tightly coupled
$storage = \Drupal::entityTypeManager()->getStorage('node');
$node = $storage->load($nid);
$node->setPublished(TRUE);
$node->save();
\Drupal::logger('mymodule')->notice('Published');
\Drupal::cache('data')->set('last_published', $nid);
\Drupal::messenger()->addStatus('Published');
}
}
GOOD: Proper dependency injection (follows DIP)
class ArticleManager {
public function __construct(
private EntityTypeManagerInterface $entityTypeManager,
private LoggerInterface $logger,
private CacheBackendInterface $cache,
private MessengerInterface $messenger,
) {}
public function publish($nid) {
// All dependencies injected - testable, swappable
$storage = $this->entityTypeManager->getStorage('node');
$node = $storage->load($nid);
$node->setPublished(TRUE);
$node->save();
$this->logger->notice('Published');
$this->cache->set('last_published', $nid);
$this->messenger->addStatus('Published');
}
}
Reference: Drupal API Best Practices
Common Mistakes
- Using
\Drupal::in services "because it's easier" -- invest in proper DI. WHY: "Easy" now = impossible to test, impossible to reuse in CLI/batch/API contexts - Mixing injected dependencies and static calls in same class -- be consistent. WHY: Inconsistency confuses developers; partial testing gives false confidence
- Using static calls in .module files -- extract to service. WHY: .module files always load; services use PSR-4 autoloading (performance)
- Not mocking dependencies in tests -- mock injected services. WHY: Tests without mocks are slow, fragile integration tests; not unit tests
- Creating "hybrid" classes with both patterns -- choose one: static OR injection. WHY: Hybrid pattern is worst of both worlds; can't fully test, can't fully refactor
See Also
- Dependency Injection Patterns for correct patterns
- Service Container for DI architecture
- Common Mistakes for more anti-patterns