Containerless Dependency Injection for Services

While I’m a big fan of dependency injection, I find dependency injection containers less alluring. I’ve found that liberal usage of containers can make application code harder to follow, and often the container definitions go untested, creating a spawning pool for bugs. I’ve seen a number of frameworks/applications which effectively treat the container as a very fancy global variable that classes inevitably pull objects out of. These issues are a symptom of the tool being used incorrectly, but containers encourage this behaviour because they make it so easy. Containers sometimes encourage ‘shared’ services, which are effectively global variables. Putting this shared mutable state in a container does not fix the problems related to global state, but it lets developers pretend they’re not exposed to the risks globals present. Every application I have worked on has some shared global state. Database connections and environment variables are two examples that come to mind. Using singletons and global functions continuously reminds you that you’re playing with fire. Whereas containers make you feel safe. In smaller or simpler applications, I think that the complexity incurred by a dependency injection container is not offset by its benefits. In these situations I’ve found myself using a simpler solution that I’d like to share.

Service Dependencies

When building applications, I like to use ‘service’ classes. These classes are generally stateless objects that present top level domain actions in the application. For example, the UsersService might expose a ensureUserExists or getUser method. The service methods encapsulate the various interations that are related to these domain actions. A register method might do the following:

  • Update the users and profiles database table.
  • Track some metrics in statsd.
  • Fire off a verification email.

An example service in an application I’ve been working on looks a little bit like this:

Show Plain Text
  1. <?php
  2. namespace App\Service;
  4. class UsersService
  5. {
  6.     use \Cake\Core\InstanceConfigTrait;
  7.     use \Cake\Datasource\LocatorAwareTrait;
  8.     use \Cake\Datasource\ModelAwareTrait;
  10.     private $http, $stats, $Users;
  12.     public function __construct($http, $stats, $usersTable = null)
  13.     {
  14.         $this->http = $http;
  15.         $this->stats = $stats;
  16.         $this->Users = $usersTable;
  17.         if ($this->Users === null) {
  18.             $this->modelFactory('Table', [$this->tableLocator(), 'get']);
  19.             $this->loadModel('Users');
  20.         }
  21.     }
  23.     /**
  24.      * Get the user data that relates to an access token.
  25.      *
  26.      * Having the user data lets us persist it to the database, and do useful work
  27.      * later.
  28.      */
  29.     public function getUser($accessToken)
  30.     {
  31.         $res = $this->http->get('/user', ['access_token' => $accessToken]);
  32.         $this->stats->increment('user.fetched');
  33.         return $res->json;
  34.     }
  36.     /**
  37.      * Get the user and ensure it exists in the database.
  38.      */
  39.     public function ensureUser($accessToken)
  40.     {
  41.         $this->stats->increment('user.ensured');
  42.         $data = $this->getUser($accessToken);
  43.         return $this->Users->ensureExists($data, $accessToken);
  44.     }
  45. }

This service interacts with both the Github API, statsd and the UsersTable. All of its dependencies can be injected through the constructor, and have sensible defaults where possible. This allows me to easily leverage mocks in my tests, and not lean on global state inside the service.

Getting Dependencies

My service has a few dependencies, and I’d prefer to not duplicate code when constructing instances. Because I don’t want the complexity that a container adds, and don’t want to have shared state, or inheritance I can use a trait. Traits allow horizontal code reuse and fit my requirements well:

  • Traits are simple. My editor can easily jump to a method definition.
  • No code duplication. I can re-use the trait in both HTTP and CLI contexts.
  • No shared state. The trait methods can return new instances each time they are called.

In this application my ServiceTrait looks something like the following:

Show Plain Text
  1. namespace App\Controllers;
  3. use Cake\Core\Configure;
  4. use Cake\Network\Http\Client as HttpClient;
  5. use App\Services\UsersService;
  6. use League\StatsD\Client as StatsClient;
  8. trait ServicesTrait {
  10.     protected function stats()
  11.     {
  12.         $client = new StatsClient();
  13.         $client->configure(Configure::read('Statsd'));
  14.         return $client;
  15.     }
  17.     protected function githubClient()
  18.     {
  19.         $config = Configure::read('Github');
  20.         return new HttpClient([
  21.             'host' => $config['apiHost'],
  22.             'scheme' => 'https',
  23.             'redirect' => 3
  24.         ]);
  25.     }
  27.     protected function userService()
  28.     {
  29.         return new UsersService($this->githubClient(), $this->stats());
  30.     }
  32.     // More services
  33. }

We can use this trait in both our HTTP controllers, and our CLI tools. The code is easy to follow, and an IDE or editor can easily resolve what $this->stats() does in any context. I avoid the complexity and ambiguity that containers can introduce. Furthemore, my gloal state is explictly contained in Configure. In tests, I can also easily mock out a service, by stubbing one of the service factory methods. I find this solution is a simple way to get dependency injection without a container, hopefully you can use it in one of your projects.


Hi! This approach looks interesting.
What do you do if you need to return in one of your service bitbucketClient with the same interface like githubClient? Will you make a new method “bitbucketClient” in your trait? Or let’s say githubClient but with another parameter- will you make a new method “githubClientWithHTTPParam”?
How do you solve these type of issues?

swegey on 2/2/16

@swegey I would add a new method in the trait and have the two services share a common interface.

mark story on 2/4/16

How can we test class with this aproach? How can we remplace realisation in ServicesTrait?

Londeren on 2/15/16

let’s say githubClient but with another parameter- will you make a new method “githubClientWithHTTPParam”? How do you solve these type of issues?

saraybosna turu on 2/24/16

Londeren: Generally I only have to replace the implementations in test cases. In those situations I can stub out the factory method and have the mocked method return an alternate implementation.

Saraybosna turu: I would probably define a new factory method, or change the original method definition. In an application with a container, you’d either need to add a new service, or mutate a shared service. I think mutating a shared service is not a good idea, and would prefer to add more services. In this approach service names translate into factory methods.

mark story on 3/6/16

Thank you very much. i had worked with laravel. I saw this facilities i didn’t think it is in cakephp 3.x .

mehdi fathi on 10/24/16

It’s a pity you don’t have a donate button! I’d certainly donate to
this fantastic blog! I guess for now i’ll
settle foor bookmarkin and adding youur RSS feed
to my Google account. I look forward to new updates
aand will talk about this website with my Facebook group.

Talk soon!

Tony on 4/15/17

H? t?ere, just wante? t? mention, I enjoyedd th?s post.
?t was helpful. ?eep ?n posting!

Deana 3 weeks, 6 days ago

W?at a inform?tion of un-ambiguity and preserveness ?f precious
excperience ?egarding unpredicted feelings.

Stormy 3 weeks, 5 days ago

Thanks t? my father ??o ttold me rega?ding thi? webpage,
this web site i? genuiney amazing.

Milagros 3 weeks, 5 days ago

Hurrah! ?fter a?l I gott a weblog from w?ere I be ?ble too tru?y
get u?eful dwta rewgarding m? study and knowledge.

rylantupl384blog.Onesmablog.com 3 weeks, 3 days ago

Howdy exceptional blog! ?oes running ? blog such
as this tae a ?reat deal of work? I ?ave virtually no understanding of comp?ter proghramming
howe??r I w?? hoping t? start m? own blog in the near future.
Any?ays,if you have any ideas or techniques for ne? blog owners ?lease share.
? kno? thks is offf topic h?wever I simply ?anted t? a?k.

Renee 3 weeks, 3 days ago

?ood day! ?h?s ?s my first visjt t? yo?r blog! We ar? a collection of volunteers and starting a new initiative ?n a community ?n t?e same niche.
Yo?r blog pro?ided uss beneficial ?nformation t? work on. Y?u have done a outstanding job!

taylorbzsk555drainfieldsblog.onesmablog.com 3 weeks, 3 days ago

?’m amazed, ? must ?ay. Seldom d? I come ?cross a blkog
that’? equially educative andd amusing, ?nd w?thout a
doubt, you’ve hit the nail onn t?e head. ?h? iissue is an issue that t?? feww folks ?r? speaking
intelligently ab?ut. Iam v?ry ?appy that I stumbled ?cross this
durung myy hunt f?r s?mething rega?ding this.

Melinda 3 weeks, 3 days ago

I enjoy ??at yyou guys are usual?y up too. Such clever wok and coverage!
K?ep uup t?e good w?rks guys ?’?? incorporated yyou guys t? blogroll.

ziondzsk555blog.blogocial.com 3 weeks, 3 days ago

You actu?lly make ?t ap?ear really easy ?l?ng with
your presentation howsever ? f?nd ?is topic to be really something wh?ch I fee I’d never understand.
It sort of feels t?o complicated and ver? vaast for me.
I’m having a look ahead on ?our next submit, I will try too get thee dangle ?f it!

charliefvfo047blog.qowap.com 3 weeks, 3 days ago

It’s an remarkable article ?n favor of all the internet u?ers; t?ey wijll obtan advantage
f?om itt I am s?re.

Timmy 3 weeks, 3 days ago

Fantastic beat ! I wish to apprentice whilst you
amend your website, how could i subscribe for a weblog site?
The account aided me a applicable deal. I have
been tiny bit familiar of this your broadcast provided vivid clear idea

Lunaluxe 2 weeks, 6 days ago

I must thank you forr the efforts you have put in writing
this website. I’m hoping to check out the same high-grade
content by you later on as well. In truth, your creative writing abilities has
inspired me to get my own site now ;)

Eliza 1 week, 6 days ago

Have your say: