Unwanted dependency party in your code (Stairway Pattern)
Abstraction is a key point to understanding and keeping our focus even if the project is huge. However, it gets complicated if the solution has a dependency “party” that degrades code to be easily-understood and organized. A way to organize dependencies is an adaptation of the Stairway pattern. I have chosen Entourage and Stairway pattern/anti-pattern to dive a little bit into the topic while reading Adaptive Code via C# by Gary McLean Hall.
More often the entourage antipattern is connected to a situation where you will invite one friend to the party. However, he will come with another ten people that you don’t know. But now it’s party time and you are part of it :).
Let’s imagine this situation in code. You want one dependency that you trust. But you will be surprised after you start using it. Your dependency will “invite” numbers of other dependencies that you don’t know, don’t trust or even are dangerous. You are lucky if you notice such situations, but it will be better to avoid such situations.Thankfully we can use SCA tools to help us.
Storage library scenario
Let’s have a look at a simple storage scenario.You are going to create a library to store and read data from a storage. You will pack together service classes, some libraries and some contract classes. Your logic for storing/reading is now packed together with the storage assembly. Then you will use this library in your other project with all its dependencies. After you use it in multiple projects you will notice that you do not want to have a fixed storage provider that is going to make your application quickly obsolete and insecure.
Let’s define the problem and propose a solution based on this scenario.
Let assume that the problem is the implementation and interfaces are in the same project.
Solution can be the Stairway pattern as many books suggest. Probably you will instantiate a coffee meeting with your friend friend based on a separated interface and you will receive some control on what depencesies the friend is going to take with. Let’s continue with this proposed solution. I agree, it might be strange with a real coffee/party problem.
Short definition of this pattern is keeping implementation in separate assemblies. The result is better management of dependencies in your solution.
Let’s have a look at our problem. Our storage library is an one assembly. One assembly causes that by using it we might start the dependency party that was described in the scenario above. So we will split the storage library into two assemblies. One for interfaces and another one for the implementation. Clients then need to reference interface assembly.
We have to be careful what we put into the interface library. We should avoid bringing external dependencies to the interface assembly. On one side, this might lead us to guess that we can now control what kind of external dependencies the implementation can use. However on the other side the implementation can create a wrapper and use any external dependencies. In general we should not have external dependencies and any of their classes or data objects in the interface assembly. This will avoid complications with wrapping and keep coupling lower. We will keep dependencies on implementation and any client referencing the interface assembly will not directly have to reference other dependencies, because the interface does not order them.
Another benefit of having splitted interface and implementation is that clients will reference an interface and direct reference to implementation will not be possible. More loosely coupled approach with each dependent assemblies.
The flexibility should be better, because the interface is not at the same place as implementation; the implementation assembly can be more easily replaced. We can benefit from looser coupling.
Let’s imagine a little bit of a strange situation: we have a storage library that uses a storage events notification library. We can not easily replace the notification library, because it references the interfaces in the notification library. That way the replacement of the notification assembly leads to bigger effort than using the separated interface assembly in the new notification implementation.
Disadvantage of this pattern is that the number of projects is bigger. But the maintenance side is lower thanks to the looser coupling and avoiding dependency parties in solution.
If I separate interface and implementation, how can I control that my coffee does not change to a dependency party. Can I specify third party libraries that I trust?
With separating interfaces you can “pre-specify” dependencies that the implementation will use. That way you can order an implementation with a specific third party library that you trust. However, there is still a place for implementation to make the dependency party.
We can find a lot of pattern and anti pattern definitions. Many articles explain when to use and when to not use such patterns. However the pragmatic side of using and not using patterns should be taken into count. World which is ideal and follows strict rules might miss a space for creativity and quick changes. Therefore pragmatic implementation should be taken into account and still follow rules that will keep organization and management of code in a good way. The dependency party might be a critical issue and should be handled at the start.
Code is a place where a developer works. Therefore it is important to keep dependencies under control and still be practical to use and improve the code. Having manageable dependencies can lead at least to manageable dependency parties.