dependency rule in clean architecture
The dependency rule is the guiding principle for clean architecture. It suggests how the modules or layers should depend on and interacts with each-others. The dependency rule is an extension of the dependency inversion principle, Which is one of the SOLID principles. Let's explore the SOLID principles:
The SOLID principles:
S : Single Responsibility principle : This principle states that a class should have one and only one responsibility. Having multiple responsibilities on a single class makes it prone to unpredictable side effects.
O : Open-Closed principle : states that a software entity should be open for extension but closed for modification. This means, allow extension for the details but keep the source impacted.
L : Liskov Substitution: state that any type in a software should be substitutable by it's sub-type without any undesirable side-effect on the software.
I: Interface Segregation: its exclusively for interfaces. It says that any client should not be forced to depend on any methods that doesn't uses.
D: Dependency inversion principle: this principle states that high level modules should not depend on low level modules, both should depend on abstraction, and abstraction should not depend on concretion or details. Instead concretion or details should depend on abstractions.
To understand the dependency inversion principle let's examine an example:
says there are a high level module called "A", this module incorporate high level business rules and policies, it depend on modules "B". Module "B" is a low level detail module, having a high level module depend on low level module is against dependency inversion principle. There are side effects for ever, tight coupling between components and low test-ability. any change in module "B" can directly affect high level module "A".
according to the dependency inversion principle, high level modules should not depend on low level modules. both should depends on abstraction.
If the high level module "A" sets for the contracts(interface) for the low level module and uses this contract internally, it will be completely decoupled from module "B". Module "B" can fulfill this contract by implementing it and providing required implementations. now this components have loose coupling, high test-ability and no dependency of high level modules on low level modules.
in the previous case module "A" was directly dependent on module "B", now module "B" is dependent on module "A".
Implementation in the clean architecture:
Use cases was a part of the domain layer which is a high level layer. Uses cases needs to communicate with the data layer to fulfill their goals.
The data layer is a low level layer compared to the domain layer. the domain layer is unaware about the data layer. similarly the data layer needs to communicate with the local and remote data-sources, but it is not allowed as local and remote data-sources layers are outer to the data layer. so how to prevision the communication of inner layer with the outer ones. we will use the dependency inversion principle. inner layers can set for the contracts for the outer layers.
The domain layer can put up for a contract for the data layer and the data layer can put up local and remote data-sources contracts respectively for the local and remote data-sources modules. Uses cases can use the data contract as it belong to the same layer.
This contract is fulfilled by the data layer. data layer can use the local and remote data-source contracts which is fulfilled by the data-source layer. the contracts here being abstractions which usually implemented using interfaces.
Conclusion:
By adhering to the dependency inversion principle, we have achieved loose coupling, high test-ability and maintainability.
The dependency rule acts as a guiding for placement and communication between different layers. according to this rule, dependencies can only points inwards. No layer is allowed to communicate with it's respective outer layer directly.
Inner layers are business rules and high levels polices and hence are more stable in comparison to the outer layers.
outer layers are mechanisms and tools, these layers tends to changes very often. UI, data-sources, frameworks etc, are parts of this layers.
Inner layers are oblivious to the outer layers as the changes to the outer layer can propagate inwards. all dependencies must point towards stability and abstraction.