Understanding Services and the Service Container in Drupal

When learning Drupal backend development through Acquia Academy, one topic appears again and again and gradually becomes unavoidable:

Services and the Service Container

For many developers, this topic feels abstract at first. The terminology can sound complex and the architecture may seem distant from day-to-day coding. However, once services are connected to real Drupal code, their purpose becomes clear and practical.

This article is written to help anyone learning Drupal backend development. It explains the same concepts in a simple, structured, and engaging way so readers can continue learning without feeling overwhelmed.

This article covers:

  • What services are in Drupal
  • Why Drupal uses a service container
  • How dependency injection works
  • How these concepts fit into real Drupal development

What Is a Service in Drupal?

In Drupal, a service is:

A reusable PHP class that performs a specific task and is managed by Drupal.

Drupal core itself is built on services. Many everyday tasks in Drupal are handled by services behind the scenes.

Common examples of core services

  • Database connection
  • Entity type manager
  • Cache backend
  • Logger
  • Messenger

Instead of creating new objects every time a feature needs them, Drupal creates services once and reuses them wherever they are required.

This design helps reduce:

  • Duplicate code
  • Memory usage
  • Tight coupling between classes

What Is the Service Container?

The service container is Drupal’s internal system responsible for managing services.

It:

  • Knows which services exist
  • Knows how to create each service
  • Injects services into classes that need them

The service container works by:

  • Reading *.services.yml files from core and contributed modules
  • Building service objects
  • Resolving and injecting dependencies automatically

In simple terms, developers do not manually create services. Drupal creates and manages them through the service container.


Why Drupal Uses Services

Drupal uses services to follow modern PHP and Symfony-based architecture practices. This approach makes Drupal suitable for large and complex applications.

Services help make Drupal code:

Modular

Each service focuses on a single responsibility, which keeps code organized and easier to maintain.

Testable

Services can be mocked or replaced during unit and kernel testing.

Replaceable

One service implementation can be swapped with another without changing the code that depends on it.

Scalable

Large systems, including enterprise and government Drupal sites, rely on this pattern to manage complexity.

This architecture goes beyond basic CMS functionality and reflects how modern backend frameworks are designed.


Dependency Injection

Dependency Injection means that a class receives the objects it depends on rather than creating them internally.

Instead of a class deciding how to get its dependencies, Drupal provides them when the class is constructed.

A discouraged approach

Using global static calls:

$logger = \Drupal::service('logger.factory');

This approach causes several issues:

  • Dependencies are hidden
  • Code becomes harder to test
  • Classes become tightly coupled to global state

The recommended Drupal approach

Inject dependencies through the constructor:

public function __construct(LoggerChannelFactoryInterface $logger_factory) {
  $this->logger = $logger_factory->get('custom_module');
}

Drupal automatically injects the required service when the object is created.


Registering a Custom Service

Custom services are registered using a MODULE.services.yml file.

Example: my_module.services.yml

services:
  my_module.example_service:
    class: Drupal\my_module\Service\ExampleService
    arguments: ['@logger.factory']

This configuration tells Drupal:

  • The name of the service
  • Which PHP class implements the service
  • Which dependencies should be injected

Once registered, the service becomes available throughout the Drupal application.


Using a Service in a Controller

Controllers should focus on handling requests and responses, not business logic. Services are commonly injected into controllers to keep them lightweight.

class ExampleController {

  protected $logger;

  public function __construct(LoggerChannelFactoryInterface $logger_factory) {
    $this->logger = $logger_factory->get('my_module');
  }

}

This approach avoids global calls and keeps code clean and testable.


How Services Improve Drupal Development

Understanding services changes how developers structure Drupal code.

With services:

  • Controllers become smaller and easier to read
  • Business logic moves into reusable classes
  • Code reviews become clearer
  • Debugging becomes more predictable

Drupal begins to feel like a structured backend framework rather than a simple CMS.

These concepts are foundational for:

  • Custom module development
  • API integrations
  • Cron jobs
  • Queue workers
  • Event subscribers
  • Enterprise Drupal projects

Final Takeaway

For anyone aiming to grow as a Drupal backend developer, understanding services and the service container is essential.

Acquia Academy emphasizes this topic because it sits at the core of how modern Drupal applications are built and maintained.