Your Domain Layer Is Lying to You If It Knows About EF Core — Here’s the Rule That Fixes It
Part 2: The Dependency Rule — Why the Arrows Point Inward, and Why Domain Must Never Touch EF Core
In the previous article, we talked about the structure of Clean Architecture, which splits a project into four main layers. These four layers are governed by a rule that organizes how they work together — this rule is called the Dependency Rule.
What Is The Dependency Rule?
In short: the Dependency Rule states that the direction of dependency in your code must always point inward, toward the core business rules. In other words, dependencies always flow from outer layers to inner layers — and inner layers are never allowed to import or even know about anything in the outer layers.
I know that might not fully click yet, so let’s break it down simply.
We said the layers depend on each other — meaning they need references between them for everything to work correctly. Here’s an example:
Say we have a project made up of the four core layers: API/Presentation — Infrastructure — Application — Domain. Each layer has a specific dependency on another. Here’s the order:
- Presentation layer — needs a reference to both the Application and Infrastructure layers.
- Infrastructure layer — needs a reference to the Application layer.
- Application layer — needs a reference to the Domain layer.
- Domain layer — has no references at all.
This means an outer layer knows the inner layer exists and can import namespaces from it — but never the other way around.
Why Is Adding EF Core or ASP.NET Core Inside Domain Forbidden?
The Domain layer is the heart of the business. Adding NuGet packages like Entity Framework Core or ASP.NET Core inside the Domain project causes the following problems:
- Coupling business logic to a specific storage technology: A future move to a NoSQL database or a different ORM like Dapper would force you to modify your Domain objects.
- Complicating testing: You can’t write fast, isolated unit tests for your entities and business rules without dragging in external frameworks.
At this point, a question probably comes to mind: “If an inner layer needs to communicate with an outer layer, how is that even possible?” Here’s the detailed answer:
Get Marwan Abu-Ghazaleh’s stories in your inbox
Join Medium for free to get updates from this writer.
This is achieved through the Dependency Inversion Principle (DIP) and the use of interfaces, as follows:
- Define the contract (Interface): The inner layer defines an interface that specifies only what it needs (e.g.,
IEmailServiceorITaskRepository). - Implementation: The outer layer implements this interface (e.g.,
SmtpEmailServiceusing external libraries). - Dependency Injection: The outer layer’s code is wired into the inner layer at runtime via the IoC Container in the API project.
And What Exactly Is the Dependency Inversion Principle?
It’s one of the foundational principles of Object-Oriented Design — the last of the five SOLID principles, coined by Robert C. Martin himself:
- S — Single Responsibility Principle: a class should have only one reason to change.
- O — Open/Closed Principle: code should be open for extension, but closed for modification.
- L — Liskov Substitution Principle: any object of a derived type should be replaceable with an object of its base type without breaking the program.
- I — Interface Segregation Principle: many small, specific interfaces are preferred over one large, general-purpose interface.
- D — Dependency Inversion Principle: the one we care about here.
The Dependency Inversion Principle rests on two connected rules:
- High-level modules should not depend on low-level modules; both should depend on abstractions.
- Abstractions should not depend on details; details should depend on abstractions.
In short: instead of having the Application layer depend directly on a specific technical detail like Entity Framework Core, we place an abstraction layer between them — and that abstraction is the Interface. It describes what needs to happen, not how it happens. It contains no actual implementation — just method signatures.
In the next post, we’ll walk through practical examples and real code from the TaskFlow project to put these ideas into action.
🔥 Don’t miss Part 3
We’re about to get hands-on with real code from TaskFlow. If this breakdown of the Dependency Rule made things click for you, follow now so you don’t miss it, drop a 👍 like to help this reach more .NET devs, and tell me in the comments: have you ever been burned by a Domain layer that knew too much? Let’s talk about it. 👇










