Annotated Container, Why Attributes?

August 20th, 2022

I've recently reached a point with Annotated Container where I'm starting to build my own apps with it and want to find users who might be interested in using the project. Along with trying to find those users has come a lot of useful feedback. Quite a bit of it has been positive, others have questioned why would you use Attributes to configure dependency injection? In this article I'm going to talk about exactly that, why Annotated Container chose Attributes to configure your Container.

I Stole This Idea

Like most of my good ideas1, I didn't come up with Annotated Container on my own. Before Roave tricked me into working for them, I did a fair amount of work in a Java Spring Boot application. Dependency injection was handled with very similar patterns using Annotations. I found this system to be very powerful and I preferred working in it over trying to define a configuration file in some arbitrary format or learning how to use a programmatic API. Much of the inspiration for this project was, "I wonder if we could do dependency injection in PHP like Spring Boot?"

Dependency Injection is Configuring Code

Regardless of how we might try to slice it, configuring a dependency injection container is configuring code. Putting that configuration close to the code, as code itself, can have some side benefits that aren't immediately apparent. Below I'll talk about 2 of those I believe are important.

Tooling Support

Since Attributes are pieces of code just like your codebase there's some tooling support built-in to Annotated Container right out of the box, and I didn't have to do anything for it. Want to know what services you have in your codebase? A modern IDE, like PhpStorm, will be able to find all usages and give you a list, right there in your IDE. That'll work for all Attributes. Additionally, purpose-built static analysis tools can be created on the backs of libraries like Psalm or PHPStan to provide further functionality.

A part of Annotated Container's roadmap includes better support for tooling. That includes a PhpStorm plugin to review the information about your container more easily and a static analysis tool for finding potential problems before your code is run.

Less Susceptible to Code Changes

Classes, methods, and method arguments change over time; what they're named, what types they expect, and where they're located in the codebase. As the business logic evolves so will your codebase, hopefully. When you have to define those constructs outside your codebase it can create a lot of "and work". I need to rename this code construct to match up with new business knowledge and ensure some boilerplate dependency injection file is updated properly. Because Attributes are code and lives with your code it is implicitly aware of this change. Cutting out this type of "and work" is a large goal for Annotated Container.

Configuring Dependency Injection Sucks

Having worked a fair amount on configuring dependency injection I can safely say whatever system you choose, they all suck. Whether you're writing a configuration file, or using a programmatic API at runtime, or putting Attributes on code constructs they all have positives and drawbacks. You need to understand and agree with whatever system you decide to use. For some people that will be writing configuration files. For others, it'll be using a fluent API or set of functions. And, for others still, it'll be using Attributes.

Annotated Container is not meant to be "The Way" to configure your dependency injection, just "a way". :)

Before You Get Going

I'd like to point out that there are multiple ways to configure dependency injection with Annotated Container! In addition to Attributes there's also a set of functions for defining your Container. Ultimately, both the Attributes and the functions use the same code to construct a series of definition objects. You could decide to implement your own system to create these definitions, using the same constructs this library does or your own. Ultimately the ContainerDefinition, and the other interfaces it provides, can be generated by whatever configuration format you choose. However, what Annotated Container is intended to provide, and will support out-of-the-box, is dependency injection through Attributes.

Footnotes

1 It is still to be determined whether Annotated Container is a good idea.