C
C#2mo ago
Gustavo Cruz

Overkill on the use of interfaces for Dependency Injection in Web APIs?

I'm developing a Web API that uses Controllers, Services and DAO classes, and every single controller consumes a corresponding service through Dependency Injection. Reading about Dependency Inversion on modern-web-apps-azure I've come to realize that I'm not leveraging the benefits of the principle, i.e., to be able to use multiple implementations based on a single interface. I'm building interfaces just to make use of Dependency Injection. The current state of my API is 1:1:1 (one controller to one service to one DAO). It's overkill to use interfaces in this case? It's actually quite annoying to change services implementation details and have to update my interfaces, as well as the possible performance implications of using Injection for every service.
8 Replies
PixxelKick
PixxelKick2mo ago
My rule of thumb is to only use DI for services that are stateful, or are involved in statefulness. Examples include: - Object pooling - HttpClientFactory stuff - Entity Franework DbContext - Configuration - Logging - Caching Etc, these various services actually are stateful in some way. For the vast majority of my logic I just use static classes and static methods. There's zero reason to use DI on a stateless singleton service, you just re-invented a static class when you do that. Some people don't like this but it's served me very well. It reduces clutter, is less crud to maintain, and reduces inversion of control. 1. I don't make a service stateful unless I must, and even then I abstract out the stateful part to its own service and keep as much stateless logic in the static class as possible. 2. I don't add abstraction on top of the stateful service unless I need to mock it because it is "on the edge" Which then brings up the "but then you cant moq how do you unit test?" Question, to which I've made this handy infographic:
PixxelKick
PixxelKick2mo ago
No description
Anton
Anton2mo ago
In addition to what @PixxelKick said, services from DI can be used for bundling dependencies. You don't have to inject interfaces FYI, you can inject concrete implementations. Interfaces are for unit testing and aren't even useful most of the time. It seems I'm not the only one who's doing static classes
zuzaki
zuzaki2mo ago
Pixel, can you show a real example because I don't think I understand your infograph. I'm currently working in a code base with a lot of static classes and my impression is that it's very hard to follow code flow, since it jumps around a lot, and it's very hard to test services when it uses 3 or more different static classes. I can't really figure out of it's due to bad class design or if it's all the static classes or both.
PixxelKick
PixxelKick2mo ago
public static class FooRepository
{
public static void RecalculateFoo(AppContext ctx, int fooId)
{
var existingFoo = ctx.Db.Foos.SingleOrDefault(f = f.FooId == fooId);
var recalculation = FooCalculationService.RecalculateFoo(existingFoo);
existingFoo.Value = recalculation;
ctx.Db.SaveChanges();
}
}

public static class FooCalculationService
{
public static decimal RecalculateFoo(Foo existingFoo)
{
...
}
}
public static class FooRepository
{
public static void RecalculateFoo(AppContext ctx, int fooId)
{
var existingFoo = ctx.Db.Foos.SingleOrDefault(f = f.FooId == fooId);
var recalculation = FooCalculationService.RecalculateFoo(existingFoo);
existingFoo.Value = recalculation;
ctx.Db.SaveChanges();
}
}

public static class FooCalculationService
{
public static decimal RecalculateFoo(Foo existingFoo)
{
...
}
}
Note how FooCalculationService has no dependencies and is atomic. It merely takes in a plain ole Foo and does its logic. This is now very easy to unit test. No mocking needed, no dependencies, pure behavior, no side effects. It does it's atomic job and returns a value you can easily assert on. Meanwhile FooRepository depends on your db context and mostly just holds the logic for doing database stuff abd connecting to the Egress (ef core sql stuff) Which means you would want to Integration Test FooRepository, but you can Unit Test FooCalculationService
zuzaki
zuzaki2mo ago
Thanks that helps. I'm thinking of pros and cons. With your rules it seems like a fine way to code, but I would except that some problems can arise. Static classes are not garbage collected, so would there be potential for memory leaks? I think you need to be very disciplined in your design to not introduce state, or too much, into the static classes. And the api or class design is something I see being skipped in a lot of projects.
Anton
Anton2mo ago
there's nothing to garbage collect you can't introduce state into a static class and procedural programming is literally the most straightforward way to program
PixxelKick
PixxelKick2mo ago
Only way that can happen is if you put a static field or static prop on the class for some reason. If you keep them readonly to just serve as effectively constant values though, it's still stateless really. Only if you did something weird like put a static list on the class that you kept adding stuff to would it be a problem
Want results from more Discord servers?
Add your server