# Di; new dependency injection shard

**URL:** <https://forum.crystal-lang.org/t/di-new-dependency-injection-shard/8719>\
**Category:** Community\
**Tags:** announces, release\
**Created:** [February 19, 2026, 3:02am UTC](https://forum.crystal-lang.org/t/di-new-dependency-injection-shard/8719 "2026-02-19T03:02:54Z")\
**Posts on this page:** 18\
**Page:** 1

<div class="post-metadata">

**Author:** ![omarluq](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/omarluq/32/3074_2.png) [@omarluq](https://forum.crystal-lang.org/u/omarluq)\
**Post date:** [February 19, 2026, 3:02am UTC](https://forum.crystal-lang.org/t/di-new-dependency-injection-shard/8719/1 "2026-02-19T03:02:54Z")

</div>

Hello everyone,

I just released Di v0.1.0, a new dependency injection shard.

Di is macro first and type safe. It supports auto wire from constructor types, named services, transient services, fiber local scopes, health checks, and graceful shutdown.

```crystal
require "di"

Di.provide { Database.new(ENV["DATABASE_URL"]) }
Di.provide UserRepo
Di.provide UserService

svc = Di.invoke(UserService)
# or
svc = Di[UserService]

Di.scope(:request) do
  Di.provide { CurrentUser.from_token(token) }
  current = Di.invoke(CurrentUser)
  # or
  current = Di[CurrentUser]
end

```

Repo: [GitHub - omarluq/di: Dependency injection for the Crystal programming language](https://github.com/omarluq/di)  
Docs: [https://crystaldoc.info/github/omarluq/di](https://crystaldoc.info/github/omarluq/di)

Feedback are always welcome.

---

<div class="post-metadata">

**Author:** ![Xen](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/xen/32/2288_2.png) [@Xen](https://forum.crystal-lang.org/u/Xen)\
**Post date:** [February 19, 2026, 10:09pm UTC](https://forum.crystal-lang.org/t/di-new-dependency-injection-shard/8719/2 "2026-02-19T22:09:26Z")

</div>

Sure looks nice and elegant.

> **Lifecycle hooks** are duck-typed.

I like.

Though I’m curious to the thinking behind naming the getter `invoke`? I think `provide` is a nice alternative to the common `register`, but I think the mental model is “getting” something from the container not “invoking” the service (you do that afterwards when calling methods on the returned object). `Di[UserService]` would work well too, if that’s feasible.

---

<div class="post-metadata">

**Author:** ![omarluq](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/omarluq/32/3074_2.png) [@omarluq](https://forum.crystal-lang.org/u/omarluq)\
**Post date:** [February 20, 2026, 4:36am UTC](https://forum.crystal-lang.org/t/di-new-dependency-injection-shard/8719/3 "2026-02-20T04:36:43Z")

</div>

> [@Xen](#):
>
> Though I’m curious to the thinking behind naming the getter `invoke`? I think `provide` is a nice alternative to the common `register`, but I think the mental model is “getting” something from the container not “invoking” the service (you do that afterwards when calling methods on the returned object). `Di[UserService]` would work well too, if that’s feasible.

Great point, thanks for calling it out.

You are right that the user mental model is usually “get from container” rather than “invoke service”.

The `invoke` name came from two ideas:

- It matches naming from `samber/do` influence.
- The container is doing more than a simple read. It may run a factory, resolve dependencies, detect circular chains, and cache a singleton. So semantically it is invoking provider logic, not invoking service methods.

That said, I agree `Di[UserService]` feels like more idiomatic Crystal, and I like how it reads. I am going to work on implementing it tonight

---

<div class="post-metadata">

**Author:** ![lasso](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/lasso/32/2148_2.png) [@lasso](https://forum.crystal-lang.org/u/lasso)\
**Post date:** [February 20, 2026, 10:21am UTC](https://forum.crystal-lang.org/t/di-new-dependency-injection-shard/8719/4 "2026-02-20T10:21:36Z")

</div>

Coming from C#, I’m used to working against interfaces in the DI system. Now, Crystal doesn’t have interfaces, but I usually do the abstract module trick to get around this.

```crystal
require "di"

module Printable
    abstract def get_print_data() : String
end

class Square
    include Printable

    def get_print_data() : String
        "Square"
    end
end

class Circle
    include Printable

    def get_print_data() : String
        "Circle"
    end
end

```

Using this library, it was easy to get this to work when using the block form

```crystal
Di.provide { Square.new.as(Printable) }

p Di[Printable] # prints #<Square:0x1e3be056fb0>

```

but I’m wondering if it is possible to get this kind of behavior with the non-block form. ie

```crystal
Di.provide Printable, Square # Would return Square when asking for Printable 

```

`

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [February 20, 2026, 2:04pm UTC](https://forum.crystal-lang.org/t/di-new-dependency-injection-shard/8719/5 "2026-02-20T14:04:59Z")

</div>

> [@lasso](#):
>
> but I’m wondering if it is possible to get this kind of behavior with the non-block form. ie

In my implementation I handled this by giving each registered service an ID. E.g. `"square"`, or `"my_app_square"` if in a `MyApp` namespace. From here, there is an annotation that can be applied to a service that includes interface module(s) that’ll denote that service as the “default”. Within the autowiring logic, you can look at the constructor argument name/type to deduce which you should use. So by default if you have like `@printable : Printable` you get the default. If you have like `@circle : Printable` you get `Circle` since the parameter name would match its service ID. In these cases I found it’s also good to not use the `@circle` syntax and assign it like a normal variable in the constructor. This way you can change the service that gets injected w/o having to change all the internal usages of the ivar.

---

<div class="post-metadata">

**Author:** ![lasso](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/lasso/32/2148_2.png) [@lasso](https://forum.crystal-lang.org/u/lasso)\
**Post date:** [February 20, 2026, 2:36pm UTC](https://forum.crystal-lang.org/t/di-new-dependency-injection-shard/8719/6 "2026-02-20T14:36:41Z")

</div>

Even though it’s very nice to have a more flexible DI registration, in this case I’m quite happy to just use the type as a key, I merely want to be able to use another (module/interface) type as the key instead of the implementing class.

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [February 20, 2026, 2:51pm UTC](https://forum.crystal-lang.org/t/di-new-dependency-injection-shard/8719/7 "2026-02-20T14:51:12Z")

</div>

It’s possible to access the modules a type includes, so could possibly implicitly add them as aliases behind the scenes. But that might have some side-effects vs being explicit 👍.

---

<div class="post-metadata">

**Author:** ![Xen](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/xen/32/2288_2.png) [@Xen](https://forum.crystal-lang.org/u/Xen)\
**Post date:** [February 20, 2026, 7:51pm UTC](https://forum.crystal-lang.org/t/di-new-dependency-injection-shard/8719/8 "2026-02-20T19:51:35Z")

</div>

It’s almost as if we’re taking about Symfonys contra Laravels container implementation in PHP.

Symfonys container started out, AFAICT, with named services (though classed services are showing up now), while Laravels used classes (we’ll skip over the fact that named classes is basically just a string, not important here).

In Symfony land they’ve also been fan of using attributes on parameters to tell the container what to inject. Laravel on the other hand has a concept of telling the container that when `This` class needs `That` interface, it should use `ThatOtherImplementation`.

I find the latter concept much more compelling. It’s not the concern of any random controller class which `Logger` implementation it should use. That’s the job of the container, which knows that in general it should be the `FileLogger`, but _that_ class over _there_ needs to get a `NullLogger` instead.

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [February 20, 2026, 8:35pm UTC](https://forum.crystal-lang.org/t/di-new-dependency-injection-shard/8719/9 "2026-02-20T20:35:01Z")

</div>

You guessed correctly 😅.

> [@Xen](#):
>
> I find the latter concept much more compelling

Yea for sure, it’s a pretty slick feature being able to define a default implementation of an interface, but still provide a way to use others. Symfony handles this via [AsAlias](https://symfony.com/doc/current/service_container/alias_private.html#aliasing:~:text=When%20using%20%23%5BAsAlias%5D%20attribute), and does something similar with constructor parameter names as i mentioned, or can use `AutoWire` attribute as you mentioned, to get a specific implementation and not just the default.

Is interesting there being two new DI shards fairly recently after years of not many others :S. But always interesting to see what other people do.

---

<div class="post-metadata">

**Author:** ![Xen](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/xen/32/2288_2.png) [@Xen](https://forum.crystal-lang.org/u/Xen)\
**Post date:** [February 20, 2026, 9:27pm UTC](https://forum.crystal-lang.org/t/di-new-dependency-injection-shard/8719/10 "2026-02-20T21:27:17Z")

</div>

> [@Blacksmoke16](#):
>
> Symfony handles this via [AsAlias](https://symfony.com/doc/current/service_container/alias_private.html#aliasing:~:text=When%20using%20%23%5BAsAlias%5D%20attribute)

I’m not sure if that’s quite the same. I’m sure you can make the Symfony container use a default service and then pass another implementation for some specific service. You can always use a factory. Or perhaps `arguments` will do. But then we’re back at explicitly specifying constructor parameters, which is a tighter coupling than “when this needs a logger, it should get this implementation”.

I’ll spare you my “what’s wrong with annotation based wiring” rant unless someone asks for it.

> [@Blacksmoke16](#):
>
> Is interesting there being two new DI shards fairly recently after years of not many others :S.

Maybe they needed to grow unhappy with the existing offerings and figure out how to (try) and do better? It’s one thing to port an existing concept to a new language, it’s something else to make it feel like it was born there.

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [February 20, 2026, 10:00pm UTC](https://forum.crystal-lang.org/t/di-new-dependency-injection-shard/8719/11 "2026-02-20T22:00:07Z")

</div>

> [@Xen](#):
>
> I’m not sure if that’s quite the same.

> [@Xen](#):
>
> “when this needs a logger, it should get this implementation”

Then I’m confused. Isn’t the idea that you have multiple implementations of `LoggerInterface`, but `SysLogger` is the “default” one. You then have like many services injecting `LoggerInterface` and it just knows to provide it `SysLogger` without any other/extra configuration? Or? Because that’s what i was trying to convey.

> [@Xen](#):
>
> Maybe they needed to grow unhappy with the existing offerings and figure out how to (try) and do better?

If they did I haven’t heard any complains/feedback otherwise could have worked to improve things 😅. It’s a pretty awesome implementation these days. Having new use cases to solve for is how things improve. Happy to chat more about that if you’d like 🙂.

---

<div class="post-metadata">

**Author:** ![omarluq](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/omarluq/32/3074_2.png) [@omarluq](https://forum.crystal-lang.org/u/omarluq)\
**Post date:** [February 20, 2026, 10:35pm UTC](https://forum.crystal-lang.org/t/di-new-dependency-injection-shard/8719/12 "2026-02-20T22:35:27Z")

</div>

Thanks all, this is super helpful.

For the immediate use case, I think the most Crystal feeling path is explicit interface/module aliasing in non-block form, e.g.:

```crystal
Di.provide Printable, Square

```

With a compile-time check that `Square` actually includes `Printable`.

I like the implicit-alias idea in theory (auto register under included abstract modules), but I’m leaning against making that the default because of ambiguity/side effects once multiple implementations exist.

On the Symfony point: I think `AsAlias` maps closer to global alias/default wiring, while “when this specific service needs interface X, use Y” is contextual binding. Those feel like two separate concepts worth modeling separately.

So current direction I’m considering:

1. explicit global alias/default (`Printable -> Square`).
2. optional contextual overrides as a separate API (something like `Di.bind SomeService, Printable, Circle`), instead of overloading `provide` too much.
3. strict single resolve vs explicit multi resolve

```crystal
Di.provide Printable, Sv1
Di.provide Printable, Sv2

Di[Printable] # => raises Di::AmbiguousServiceError
Di[Array(Printable)] # => [Sv1, Sv2]

```

Named services can also resolve ambiguity when callers need a specific implementation:

```crystal
Di.provide Printable, Square, as: :square
Di.provide Printable, Circle, as: :circle

Di[:square] # => Square
Di[:circle] # => Circle
Di[Array(Printable)] # => [Square, Circle]

```

---

<div class="post-metadata">

**Author:** ![omarluq](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/omarluq/32/3074_2.png) [@omarluq](https://forum.crystal-lang.org/u/omarluq)\
**Post date:** [February 20, 2026, 11:10pm UTC](https://forum.crystal-lang.org/t/di-new-dependency-injection-shard/8719/13 "2026-02-20T23:10:03Z")

</div>

> [@Blacksmoke16](#):
>
> Is interesting there being two new DI shards fairly recently after years of not many others :S. But always interesting to see what other people do.

The `ADI` shard is fantastic I peaked at it for some inspiration, but decided to go a different route eventually. I had not heard about the other new shard until you mentioned it.

I am building `di` as part of a family of shards:

- `di` for Dependency Injection
- `re` for ReactiveX paradigms
- `mo` for Monads

These are meant to be the core building blocks for a TUI framework on top of Termisu.  
I also want control over the implementations so I can shape them to the framework’s needs.  
Keeping everything minimal and staying within `stdlib` constraints is a top priority for me.

---

<div class="post-metadata">

**Author:** ![Xen](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/xen/32/2288_2.png) [@Xen](https://forum.crystal-lang.org/u/Xen)\
**Post date:** [February 21, 2026, 12:01am UTC](https://forum.crystal-lang.org/t/di-new-dependency-injection-shard/8719/14 "2026-02-21T00:01:52Z")

</div>

> [@Blacksmoke16](#):
>
> Then I’m confused. Isn’t the idea that you have multiple implementations of `LoggerInterface`, but `SysLogger` is the “default” one. You then have like many services injecting `LoggerInterface` and it just knows to provide it `SysLogger` without any other/extra configuration? Or? Because that’s what i was trying to convey.

Yeah, but with the added wrinkle that I can say “but when _that_ class asks for a `LoggerInterface` it shouldn’t get a `SysLogger`, but this `FileLogger` instead”.

Which the Symfony container can do, of course, but when I see it happen, I usually see them using an annotation on the constructor parameter of the class asking for a `LoggerInterface`. But then it’s the class deciding what logger to use and not the container.

> [@Blacksmoke16](#):
>
> If they did I haven’t heard any complains/feedback otherwise could have worked to improve things 😅.

Well, every piece of code can’t be everything for everybody, so rather than complain it’s more productive to see if one can come up with something more to one’s taste. 😁 PHP has at least three widely used DI implementations. It’s also nice with different options for the rest of us. I’ve always been a fan of “there’s more than one way to do it”, I don’t think I’d have formed as many opinions about what I like in a container if I hadn’t been exposed to a selection.

---

<div class="post-metadata">

**Author:** ![omarluq](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/omarluq/32/3074_2.png) [@omarluq](https://forum.crystal-lang.org/u/omarluq)\
**Post date:** [February 21, 2026, 4:46am UTC](https://forum.crystal-lang.org/t/di-new-dependency-injection-shard/8719/15 "2026-02-21T04:46:23Z")

</div>

Update: just released 0.1.2 with Interface binding for non-block form and multi-resolutions

---

<div class="post-metadata">

**Author:** ![Sija](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/sija/32/737_2.png) [@Sija](https://forum.crystal-lang.org/u/Sija)\
**Post date:** [February 21, 2026, 10:00am UTC](https://forum.crystal-lang.org/t/di-new-dependency-injection-shard/8719/16 "2026-02-21T10:00:12Z")

</div>

Regarding the naming, perhaps `resolve` would be a better fit?

---

<div class="post-metadata">

**Author:** ![lasso](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/lasso/32/2148_2.png) [@lasso](https://forum.crystal-lang.org/u/lasso)\
**Post date:** [February 21, 2026, 4:53pm UTC](https://forum.crystal-lang.org/t/di-new-dependency-injection-shard/8719/17 "2026-02-21T16:53:54Z")

</div>

Awesome! Thank you!

---

<div class="post-metadata">

**Author:** ![omarluq](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/omarluq/32/3074_2.png) [@omarluq](https://forum.crystal-lang.org/u/omarluq)\
**Post date:** [February 21, 2026, 10:20pm UTC](https://forum.crystal-lang.org/t/di-new-dependency-injection-shard/8719/18 "2026-02-21T22:20:39Z")

</div>

> [@Sija](#):
>
> Regarding the naming, perhaps `resolve` would be a better fit?

~~You mean instead of `provide`?~~  
nvm I’m stupid lol! `resolve` does sound nice! I wanna avoid having many aliases so probably will drop `invoke` in favor of `resolve` and keep `[]` as well
