No history yet

Core Dependency Inversion

The Dependency Rule

In a traditional layered architecture, dependencies flow downwards. The user interface depends on the business logic, which in turn depends on the data access layer. This seems logical, but it creates a fragile system. A change in the database schema could ripple upwards and force changes in the business logic, even if the business rules themselves haven't changed.

This structure makes the high-level policy (the business logic, which is the heart of your application) dependent on low-level details (how you store data or show it on a screen). It's like building a skyscraper where the blueprint for the penthouse is determined by the brand of pipes used in the basement. If you change the pipes, you have to redesign the penthouse. This is backward.

The most important principle is dependency inversion, which means that outer layers depend on inner layers — never the other way around.

Inverting the Flow

To fix this, we apply the (DIP). The principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions, like interfaces. This means our core business logic should not depend on the database or the web framework. Instead, the database and web framework should depend on the business logic.

This flips the dependency arrow. Instead of pointing from the high-level business logic down to the low-level data layer, the arrow now points from the data layer inward to an abstraction defined by the business logic. It's crucial not to confuse this architectural principle with Dependency Injection, which is a technique for supplying dependencies to an object.

The diagram shows the shift. In the inverted model, the Data Layer now depends on an interface defined within the Business Layer. The Business Layer dictates the contract, and the Data Layer provides an implementation. The flow of control at runtime might still go from business logic to the database, but the flow of source code dependency has been flipped to point inward.

Defining Interfaces in the Core

The key to making this work is defining abstractions (interfaces) in your or application logic. These interfaces describe the capabilities your business logic needs, such as IGetUserData or ISaveOrder. They are simple contracts that define methods, inputs, and outputs, without any implementation details.

Outer layers, like a database repository or a third-party API client, then provide the concrete implementations. Your core business logic only ever communicates with the interface. It doesn't know or care if the data is coming from a SQL database, a file, or a web service. This isolates your most valuable code from the churn of technology choices and implementation details.

// Inside your Core/Domain Layer
// This layer has NO dependencies on any frameworks.
public interface IUserRepository
{
    User GetById(int userId);
}

// Inside your Core/Application Layer
// This layer depends ONLY on the Domain Layer.
public class UserService
{
    private readonly IUserRepository _userRepository;

    public UserService(IUserRepository userRepository)
    {
        // The dependency is injected here.
        _userRepository = userRepository;
    }

    public User GetUser(int id)
    {
        return _userRepository.GetById(id);
    }
}

// Inside your Infrastructure Layer
// This layer depends on the Core and external frameworks (e.g., a database driver).
public class SqlUserRepository : IUserRepository
{
    public User GetById(int userId)
    {
        // ... code to fetch a user from a SQL database ...
        return new User(); // Simplified for example
    }
}

In this example, the UserService is part of our high-level business logic. It needs to get a user, but it doesn't know how. It only knows about the IUserRepository interface, which is defined right there in the core.

The SqlUserRepository is a low-level detail. It lives in the outer infrastructure layer and contains the specific code to talk to a database. It implements the interface from the core, making the infrastructure layer dependent on the core, not the other way around. We have successfully inverted the dependency.

This principle is the mechanical foundation of Clean Architecture. It ensures that the core business rules can evolve independently of the surrounding technology. Now, let's test your understanding.

Quiz Questions 1/5

What is the primary problem with a traditional layered architecture where dependencies flow downwards?

Quiz Questions 2/5

According to the Dependency Inversion Principle, what should high-level and low-level modules both depend on?

By consistently applying the Dependency Inversion Principle, you create a system that is more flexible, maintainable, and testable.