Learn / .NET API Engineering Path
Project Structure and Dependency Injection
Split API, application, and infrastructure responsibilities so dependency direction stays reviewable from the first feature.
Course: .NET API Engineering Path. Level: Advanced. Topic: Typed backend.
Stage: basic - Basic foundation - Project structure and dependency direction. Separate API, application, and infrastructure projects so composition stays explicit from the first feature.
Outcomes
- Separate endpoint, application, and infrastructure projects.
- Register services at the composition root.
- Keep framework types out of core use cases.
Concepts
- composition root
- service lifetime
- project reference
- interface boundary
Concept flow
Show how project structure and dependency direction moves from trigger to implementation outcome in .NET APIs.
- API project
- Application use case
- Infrastructure adapter
- Options
- Dependency container
Session flow
- Model composition root (concept, 10 min) — Name the decisions behind composition root before writing code.
- Separate endpoint, application, and infrastructure projects.
- Explain where composition root belongs in order management API.
- Build the vertical slice (walkthrough, 17 min) — Implement the smallest useful slice in Orders.Application/CreateOrderHandler.cs.
- Register services at the composition root.
- Connect service lifetime to a working example.
- Verify and harden (exercise, 11 min) — Document which project owns each dependency.
- Keep framework types out of core use cases.
- Record one risk or follow-up before moving on.
Code example
C# in Orders.Application/CreateOrderHandler.cs.
public sealed class CreateOrderHandler(IOrderRepository orders, TimeProvider clock)
{
public async Task<Guid> HandleAsync(CreateOrder command, CancellationToken ct)
{
var order = Order.Create(command.CustomerId, command.TotalCents, clock.GetUtcNow());
await orders.SaveAsync(order, ct);
return order.Id;
}
}
Walkthrough examples
- Project Structure and Dependency Injection in a order management API — A team is extending a production-grade ASP.NET Core order API and needs this lesson's pattern to be clear enough for review, testing, and future maintenance.
- File: Orders.Application/CreateOrderHandler.cs
- File: src/Orders.Application/project-structure-and-dependency-injection.cs
- File: tests/Orders.Api.Tests/project-structure-and-dependency-injection.Tests.cs
- File: docs/dotnet-advanced/project-structure-and-dependency-injection.md
- Start from the provided C# snippet and make the intent visible in names and boundaries.
- Apply the checklist item "API project references application" before adding extra behavior.
- Write down how the implementation changes when service lifetime fails or becomes slow.
Practice
- Move one use case out of Program.cs.
- Register one interface and implementation with a deliberate lifetime.
- Document which project owns each dependency.
Checklist
- API project references application
- Application avoids ASP.NET Core types
- Infrastructure implements ports
- Service lifetimes are named in review
Quiz prompts
- Where should framework-specific ASP.NET Core endpoint types usually live? — Keeping framework types at the edge protects application logic from HTTP and hosting churn.
- A teammate wants to hide composition root inside a convenient helper. What should you check first? — Place composition root at the boundary that keeps order management API behavior explicit, testable, and reviewable.
- Which artifact best proves this .NET APIs lesson is ready for review? — Production-ready learning needs evidence: a test, trace, command, screenshot, or log that catches the risk again.
- Basic foundation: a teammate says the happy path works, but "Composition root" is still implicit. What should you ask for before merging? — Composition root belongs in the basic stage only when the decision is visible, testable, and tied to a realistic failure mode.
- A reviewer has five minutes to evaluate this basic .NET APIs slice. Which evidence is strongest? — Build a small CreateOrder slice with API registration, an application handler, an infrastructure repository, and a test proving the use case runs without ASP.NET Core.
Flashcards
- Basic foundation: what decision does "Composition root" force you to make? Register concrete infrastructure at the API edge while application use cases depend on interfaces. Evidence prompt: Move one service registration into the API composition root and name the chosen lifetime.
- Basic foundation: what decision does "Application boundary" force you to make? Keep framework types out of core use cases so the business flow can be tested without ASP.NET hosting. Evidence prompt: Refactor one handler to accept a command and repository port instead of HttpContext.
- Basic foundation: what decision does "Dependency direction" force you to make? Make project references point inward from API and infrastructure toward the application contracts. Evidence prompt: Draw the project references and remove one dependency that points back toward the API project.
- In .NET APIs, what should you remember about composition root? composition root matters here because it supports "Separate endpoint, application, and infrastructure projects.".
- In .NET APIs, what should you remember about service lifetime? service lifetime matters here because it supports "Register services at the composition root.".
- In .NET APIs, what should you remember about project reference? project reference matters here because it supports "Keep framework types out of core use cases.".
- In .NET APIs, what should you remember about interface boundary? interface boundary matters here because it supports "Separate endpoint, application, and infrastructure projects.".
Labs
- Ship a project structure and dependency injection slice — Extend a production-grade ASP.NET Core order API with a small but reviewable feature that proves the lesson's architecture in code.
- Build a small CreateOrder slice with API registration, an application handler, an infrastructure repository, and a test proving the use case runs without ASP.NET Core.
- Move one service registration into the API composition root and name the chosen lifetime.
- Refactor one handler to accept a command and repository port instead of HttpContext.
- Draw the project references and remove one dependency that points back toward the API project.
- Move one use case out of Program.cs.
- Register one interface and implementation with a deliberate lifetime.
- The lab demonstrates the basic foundation outcome without skipping earlier contract evidence.
- Each subtopic has a concrete artifact: Composition root, Application boundary, Dependency direction.
- The review notes explain how this level changes ownership, verification, or operational risk.
Challenge
- Review-ready project structure and dependency injection (Stretch) — Build a small CreateOrder slice with API registration, an application handler, an infrastructure repository, and a test proving the use case runs without ASP.NET Core.
- All checklist items are either implemented or documented with a reason.
- The change can be understood from Orders.Application/CreateOrderHandler.cs plus one short note.
- The concept diagram names ownership, failure handling, and verification points.
- A teammate could run the verification steps without asking for hidden context.
Canonical lesson URL