Architecture
Dependency inversion, layered and Clean/Onion architecture, the Repository pattern over EF Core, and DTO versus domain entity mapping.
5 questions
MiddleTheoryVery commonHow does constructor injection support the Dependency Inversion Principle (DIP)?
How does constructor injection support the Dependency Inversion Principle (DIP)?
The class takes what it needs as an interface parameter and never news a concrete type, so it depends only on an abstraction it owns. The composition root picks the implementation, and the dependency inverts: infrastructure points at the domain.
Common mistakes
- ✗Thinking any container use satisfies
DIP, including a service locator resolved inside a method - ✗Letting infrastructure define the interface, so the domain still depends on infrastructure
- ✗Treating
DIPas a testability trick rather than a rule about dependency direction
Follow-up questions
- →Who should own the interface — the consumer or the implementation — and why?
- →What goes wrong when a singleton service takes a scoped dependency in its constructor?
JuniorTheoryCommonHow does a Data Transfer Object (DTO) differ from a domain entity?
How does a Data Transfer Object (DTO) differ from a domain entity?
A DTO is a flat, behavior-free shape that carries data across a boundary — it is the API contract. A domain entity has identity, invariants, and behavior. Mapping between them stops the storage schema leaking into the contract.
Common mistakes
- ✗Returning Entity Framework entities from controllers, leaking the database schema into the API contract
- ✗Putting business rules and invariants on the
DTOinstead of on the domain entity - ✗Assuming a
DTOneeds identity and equality semantics the way an entity does
Follow-up questions
- →When is a separate read
DTOper endpoint better than one sharedDTO? - →What breaks when the same
DTOis reused as both request and response shape?
JuniorTheoryCommonWhat layers does a typical layered ASP.NET Core solution have?
What layers does a typical layered ASP.NET Core solution have?
Presentation (controllers, DTOs), application (use cases), domain (entities, business rules), and infrastructure (Entity Framework, HTTP clients). Each layer calls only the one beneath it, and the domain stays free of framework and database types.
Common mistakes
- ✗Letting the domain layer reference
DbContextor ASP.NET Core types - ✗Putting business rules in controllers, leaving the domain as anemic data holders
- ✗Treating the layer split as folder organisation rather than a dependency rule
Follow-up questions
- →Which way must the dependency between domain and infrastructure point, and why?
- →What does an anemic domain model cost you as the business rules grow?
SeniorTheoryCommonHow does Clean/Onion architecture map onto the projects of an ASP.NET Core solution?
How does Clean/Onion architecture map onto the projects of an ASP.NET Core solution?
Domain holds entities and its interfaces and references nothing; Application holds use cases; Infrastructure implements those interfaces; Web is the composition root. Dependencies point inward: Infrastructure references Domain, not vice versa.
Common mistakes
- ✗Referencing
DbContextor ASP.NET Core types from the domain project - ✗Defining the repository interface in Infrastructure instead of in Domain or Application
- ✗Treating the ring split as project naming rather than an enforced dependency direction
Follow-up questions
- →Which project should own the repository interface, and why does that placement matter?
- →What does the composition root do that no other project is allowed to do?
MiddleTheoryOccasionalIs the data-access pattern Repository still worth adding on top of Entity Framework Core?
Is the data-access pattern Repository still worth adding on top of Entity Framework Core?
Usually not: DbSet<T> is already a repository and DbContext is already a Unit of Work, so a pass-through wrapper only hides IQueryable. It earns its keep when you must abstract the persistence technology or constrain callers' queries.
Common mistakes
- ✗Wrapping
DbSet<T>in a pass-through repository that adds neither constraint nor abstraction - ✗Returning
IQueryablefrom the repository, leaking the very technology it was meant to hide - ✗Believing Entity Framework Core has no Unit of Work of its own
Follow-up questions
- →What does returning
IQueryablefrom a repository leak to the caller? - →How would you test a service that depends on
DbContextdirectly?