Skip to content

Service Container - Dependency Inversion (DIP)

When to Use

The service container is Drupal's DIP foundation. Depend on abstractions (interfaces) defined in core.services.yml, not concrete implementations.

Decision

Dependency pattern Good/Bad Why
Constructor inject interface type-hint Good Follows DIP, testable, swappable
Type-hint concrete class Bad Tight coupling, can't swap implementation
Use \Drupal::service() Bad Static coupling, violates DIP, untestable
Define services in *.services.yml Good Container manages lifecycle
new ClassName() in business logic Bad Can't inject dependencies, hard to test

Pattern

GOOD: Depending on abstraction (DIP)

# mymodule.services.yml
services:
  _defaults:
    autoconfigure: true

  mymodule.article_manager:
    class: Drupal\mymodule\ArticleManager
    autowire: true  # Automatically injects dependencies

  # Service alias for interface -> implementation
  Drupal\mymodule\ArticleManagerInterface: '@mymodule.article_manager'
// Depend on interface, not implementation
class ArticleController extends ControllerBase {
  public function __construct(
    private ArticleManagerInterface $articleManager,  // Interface, not class
  ) {}
}

BAD: Depending on concretions (violates DIP)

class ArticleController extends ControllerBase {
  public function build() {
    // Static call to container - violates DIP
    $manager = \Drupal::service('mymodule.article_manager');

    // Instantiating directly - violates DIP
    $helper = new ArticleHelper();

    // Type-hinting concrete class instead of interface
  }
}

Reference: /core/core.services.yml -- service definitions following DIP

Common Mistakes

  • Using \Drupal::service() static calls -- inject dependency. WHY: Can't swap implementation, can't mock in tests, hides dependencies
  • Type-hinting concrete classes in constructor -- type-hint interface. WHY: Tight coupling; can't use service decorators, can't swap implementations
  • Not defining service aliases for interfaces -- add alias in services.yml. WHY: Autowiring can't resolve interface to implementation without alias
  • Creating objects with new in services/controllers -- inject as dependency. WHY: Hard to test, can't inject dependencies into created object

See Also