Think of Drupal's service container as a hotel concierge desk. You don't personally know how to book a taxi, reserve a restaurant table, or arrange a wake-up call — you just ask the concierge, and they hand you exactly what you need, already configured and ready to use. A service is exactly that: a reusable object registered once, in one place, that any other part of your code can request by name instead of constructing by hand.
What you'll learn in this lesson
- What a service actually is, and why Drupal core is built almost entirely out of them
- How to register a brand-new service in your module's
.services.ymlfile - What the
classandargumentskeys really do - Where to find a real, working example in Drupal's own Examples project
Why services exist
Without services, every class that needed to run a database query would have to know how to open its own database connection; every class that needed to send an email would duplicate mail-sending logic. Drupal instead builds one shared object for each responsibility — database, current_user, entity_type.manager, and hundreds more — and hands it out to whoever asks. Your own custom module can add its own services the exact same way core does.
Registering a service: the real pattern
Here's a real service definition from Drupal's own Examples module, in dbtng_example.services.yml:
services:
dbtng_example.repository:
class: Drupal\dbtng_example\DbtngExampleRepository
arguments: ['@database', '@string_translation', '@messenger']
The service ID
dbtng_example.repository is the service's unique name — by convention, {module_name}.{descriptive_name}. This is the string you'll use everywhere else to ask the container for this exact object.
class
The fully-qualified PHP class that gets instantiated when something requests this service. Drupal never calls new on this class directly in your code — the container does it once, then hands the same instance to everyone who asks (services are singletons by default).
arguments
A list of other services (each prefixed with @) to pass into this service's constructor, in order. Here, @database, @string_translation, and @messenger are three of Drupal core's own built-in services — the container resolves each one and passes it straight into DbtngExampleRepository's constructor. You never write the wiring code yourself; you just declare what you need.
drush cache:rebuild (or clear caches from the admin UI).Quick check: if two different classes both need to send a custom-formatted notification email, and you write that logic once as a service, how many times does the container actually instantiate that service class across a single page request? (Answer: once — services are singletons per request. Both classes are handed the same shared instance, not separate copies.)
Key takeaways
- A service is a reusable PHP object registered once in
{module_name}.services.ymland shared by everything that requests it. - The service ID convention is
{module_name}.{descriptive_name}— always namespaced to your module to avoid collisions. argumentslists other services (prefixed@) that the container injects into the constructor automatically — you never wire this by hand.- Services are singletons: the same instance is reused everywhere it's requested within a request, not recreated each time.
- Registering a new service always requires a cache rebuild before Drupal will recognize it.
Coming up next
Declaring a service's dependencies in YAML is only half the story — next we'll look at exactly how those @service.name arguments get matched up with your PHP class's constructor parameters, and what happens when a dependency you ask for doesn't exist.