Skip to content

Services & DI Overview

When to Use

When you need to understand the foundation of modern Drupal architecture — services provide reusable functionality, dependency injection eliminates hard-coded dependencies.

Decision

If you need... Use... Why
Reusable functionality across modules Service definition in *.services.yml Single instance, shared state, testable
Access to Drupal APIs (database, cache, config) Inject service dependencies Decoupled, mockable in tests, follows SOLID principles
Procedural code (hooks, .module files) \Drupal::service() static helper Only option in procedural context, fallback pattern
Class-based code (controllers, forms, plugins) Constructor injection via create() Best practice, explicit dependencies, autowire-compatible

Pattern

Service Definition (my_module.services.yml):

services:
  my_module.example:
    class: Drupal\my_module\ExampleService
    arguments: ['@database', '@logger.factory']

Using the Service:

// In a controller with DI
$this->exampleService->doSomething();

// In procedural code
\Drupal::service('my_module.example')->doSomething();

Reference: /core/core.services.yml (2,035 service definitions), /core/lib/Drupal.php

Common Mistakes

  • Using \Drupal:: in classes — Inject services via constructor; only use \Drupal in hooks
  • Defining services for simple value objects — Services are for shared, stateful functionality, not DTOs
  • Injecting the entire container — Inject only what you need; container injection is an anti-pattern
  • Over-engineering with services — Not everything needs to be a service; static utility methods are fine for pure functions
  • Forgetting to rebuild cache after adding services — Run drush cr after adding/modifying *.services.yml files

See Also