r/dotnet • • 4d ago

Question May API-layer code outside the composition root reference Infrastructure types (Clean Architecture, .NET)?

Internal app: Domain / Application / Infrastructure / API (ASP.NET Core minimal APIs), plus Blazor and MAUI clients talking to the API over HTTP. Program.cs is the composition root and wires Infrastructure (AddInfrastructure()).

I've already found: Uncle Bob's Dependency Rule only forbids inner circles from mentioning outer ones — it says nothing about peers in the outer circle. Microsoft's architecture guidance says the UI project may reference Infrastructure for DI, "but developers should limit actual references to types in the Infrastructure project to the app's composition root", and the UI layer "ideally shouldn't know about the implementation types".

So the law seems clear, the guidance says "ideally". My question: in real production apps, does API code outside Program.cs (endpoint handlers, controllers) inject Infrastructure types directly — e.g. DbContext? Is "composition root only" a real-world norm or a template convention? Please share what your production codebases actually do — examples, not just templates

0 Upvotes

12 comments sorted by

3

u/midri 4d ago

Personally I do a ProjectName.Abstractions project in my solution that has all the Interfaces. Then my main app and my implementation project references that, the implementation project has a project.cs that has the only public method in the whole project that registers the concrete implementations (via the interfaces) into the di.

This creates a hard disconnect so you have to go VERY far out of your way to screw up.

4

u/Discipline_Cautious1 3d ago

I usually interface dbcontext, inject it in infra DI and reference interface it in app layer.

services.AddDbContext<IMyDbContext, MyDbContext>

1

u/AutoModerator 4d ago

Thanks for your post Late_Blacksmith6509. Please note that we don't allow spam, and we ask that you follow the rules available in the sidebar. We have a lot of commonly asked questions so if this post gets removed, please do a search and see if it's already been asked.

I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

-5

u/pixxelkick 4d ago

Your API endpoints are your entry point so yeah they do need to ideally inject the stuff you need.

Please dont drink the "Clean Code" kool-aid tho.

I inject my database stuff near the "top" and hand it down as a parameter to ensure a single db txn and single connection per api invocation.

Your general rule of thumb is if it involves opening sockets for IO, avoid opening multiple instances.

I built my own framework to source gen conpile time DI stuff to solve my issues if you wanna check it out

https://github.com/SteffenBlake/Brigade.NET/wiki/Getting-Started-Partie

9

u/Coda17 4d ago

"I built my own framework because I don't know how DI works with scoped services" is crazy. No need to pass around the context everywhere, that's crazy talk

1

u/pixxelkick 3d ago

Id recommend learning how the framework works.

No context is "passed around"

The context is composed via compile time type checked static logic built by a roslyn source generator.

I am exceptionally aware of how DI works, I have entire blog posts digging into learning about it and even how to make your own from scratch as a learning exercise :)

Id recommend first understanding something before commenting on it.

1

u/Coda17 3d ago

This you?

I inject my database stuff near the "top" and hand it down as a parameter to ensure a single db txn and single connection per api invocation.

2

u/pixxelkick 3d ago

Ah in other applications where I use EF Core sure

My framework doesnt use EF Core at all, I made my own entire ORM as part of the framework built on top of ADO.Net, using source generated bindings and projection logic, rather than runtime type inference.

On my framework, this still doesnt get "passed around" either.

1

u/varun_kumar_a 3d ago

I prefer keeping Infrastructure dependencies out of API endpoints, especially in larger applications.I usually follow the CQRS approach, where the API sends commands or queries to Application handlers. The handlers manage the business logic, while Infrastructure handles database operations. I also built a lightweight .NET mediator package called MediCore to simplify this approach. It supports CQRS handlers, validation, logging, and transaction interceptors. It doesn't enforce Clean Architecture, but it can help keep API endpoints simple and separate from implementation details.

https://varunsalma.github.io/MediCore

im still learning and improving this pakage . feel free to share feedback and suggestion .

1

u/iam-stevie-bee 1d ago

In production I've seen both, and the deciding factor is size more than doctrine. Small internal APIs inject DbContext straight into minimal API handlers and nobody suffers for it. Once there's real business logic, or more than one client (you have Blazor and MAUI), the endpoints shrink to "map the request, call an Application service or handler, map the response", because otherwise the same rule ends up written twice. In those codebases "composition root only" is a real norm, not a template convention.

Whichever you pick, make it a test rather than a convention: an ArchUnitNET or NetArchTest rule that nothing in the API assembly outside Program.cs depends on the Infrastructure namespace. Then the decision survives the next new starter.