In our previous post, we discussed The Acyclic Dependency Principle, in this chapter we will go over next principle called SDP (Stable Dependencies Principle)
Depend in the direction of stability
Robert Martin
We need to ensure modules that are easy to change should not be depended on by modules that are harder to change.
What is a stable component?
Let’s say we have module X, and we have three modules that depend on X. Then X is referred to as Independent or Stable component as it has no external reason to change.
Let’s say, we have module Y which depends on three modules, so any change in any of those three modules would cause Y to change and so Y is referred to as Dependent.
Stability Metrics
Fan-in – Number of incoming dependencies, i.e., number of classes outside component that depend on classes within the component.
Fan-out – Number of outgoing dependencies, i.e., number of classes inside component that depends on classes outside the component.
Instability –
I = Fan-out / (Fan-in + Fan-out)
The range of this metric is between 0 and 1.
I = 0 => maximally stable component, component X from above example
I = 1 => maximally unstable component, component Y from above example
As per SDP, I of a component should be larger than I of the components that it depend on. I should decrease in direction of dependency.
Not all components should be maximally stable as that would create a very rigid system. We need a balance of stable and unstable components. Ideally in our system, we should have unstable components on top and then should be depending on stable components at bottom.
Thanks for stopping by! Hope this gives you a brief overview in to Stable Dependencies Principle. Eager to hear your thoughts and chat, please leave comments below and we can discuss.
So far we have gone through defining component and understanding what makes a component in previous chapters. In this post we start looking into principle that talk about the relationships between component. Let’s start with the acyclic dependency principle.
Allow no cycles in the component dependency graph
Robert Martin
A very common scenario that we developers face – We are working on a project, make some code changes, verify it’s working, so log out for day/commit the changes. Next day or few days later, we find that our integration tests are failing! Root cause comes out to be a change in one of our dependencies. This leads to either reverting our change or working on fix. The author calls this “morning after syndrome”.
Solution to this problem of running into frequent changes caused by changes in dependencies – The Weekly Build
The Weekly Build
As per this, developers work for 4 days in a week on their code and on fifth day they integrate their changes and build the system.
Now this could work when the team is small/the system is small so there are fewer teams. But when the system would grow, components and teams owning those components would grow. Eventually we would find that it takes more time to integrate and so pushing the integration days one day back. Ultimately a build per week won’t be sufficient and we would starting doing a build every two weeks. This could increase to month!
Clearly this solution cannot work for large systems.
Dependency Cycles elimination
The solution is to partition the development environment into releasable components. Each component could have its release versions with each change. The components consuming these changed components can then decide to move to new version or keep using the previous ones. This reduces the dependencies between teams and they can work independently.
The key for this approach to work is – When components depend on other components, there should be no cycles.
Say we have three components A, B, C. A depends on B, B depends on C. There will be a cycle if C would depend on A. If there is no cycle, any changes in A would force changes in B and C. Any change in B would only force C to change, A is safe. Any change in C would not force any change in A or B.
However if there is a cycle, then any change in A, B or C would need changes in all components.
So if there are cycles in the way our component depends on each other, we need to break it –
Use DIP (Dependency Inversion Principle), invert dependency by using interfaces.
Add the common classes between two components to a third component and then depend on that component.
Top Down Design
When we start building the system, we could be grouping classes based on usability. Few iterations later, we might worry about reusability of components and start moving around classes by creating new components and new dependencies between them. We can see the component structure keeps on evolving based on the requirements at each stage of our development. And so we cannot design this structure at the beginning in top down manner.
Thanks for stopping by! I hope this gives a good preview into acyclic dependency principle. Eager to hear your thoughts and chat, please leave comments below and we can discuss.
In our previous post, we went through what a component is? Next question that comes is – Which classes go in which component? This chapter defines three principles to keep into mind while grouping classes into components –
REP: The Reuse/Release Equivalence Principle
CCP: The Common Closure Principle
CRP: The Common Reuse Principle
The Reuse/Release Equivalence Principle
The granule of reuse is the granule of release.
Robert Martin
All of us are familiar with module management softwares like maven, gradle, etc. These software helps us reuse classes from other dependencies in our projects. We have seen that every module that is part of these systems comes with a release process and release numbers.
The release numbers and the release documentation helps the consumer in deciding whether to move to new version or remain on an old one.
REP, on architectural level means that classes which form a component must belong to a cohesive group. They should not be a random group of classes, but should have some theme/purpose which they all share. In other words, classes in a component should be releasable together.
What would be the outcome if we group random classes together? The consumers would know about it, they would know that they depend on our package and it has classes which don’t make any sense to be in our package. They would know that any change in those classes would also unnecessary force them to change. This would show our weak architectural skills.
This principle, although very important, gives a weak advice. It says similar classes should be grouped together, but, doesn’t have a strong criteria that architects can follow. The weakness of this principle is compensated by the other two principles.
The Common Closure Principle
Gather into components those classes that change for the same reasons at the same time. Separate into different components those classes that change at different times and for different reasons.
Robert Martin
This is similar to Single Responsibility Principle. SRP stated to keep methods that change for different reasons in separate classes. CCP states to keep classes that change for different reasons in separate components.
Following this principle ensures that if something needs to change then only one component is affected as it has all classes related to that change. We would need to only redploy that component. The other components who depend on that component would not change.
This principle is also similar to Open Closed Principle. OCP states that classes should be closed for modification, but open for extension. CCP augments this principle by keeping the classes which are closed to same type of changes together in one component. So, if change comes in requirements, only few components change.
The Common Reuse Principle
Don’t force users of a component to depend on things they don’t need.
Robert Martin
This principle keeps the classes that can be reused together into same component.
When component uses even one class from another component, a dependency is created between them. If the used component has other classes which the using component doesn’t depend on. Then any change in those classes would force using component to have changes as well.
So, when we depend on a component we need to be sure we are using all the classes in that component. It means, we want to keep together the classes that are inseparable (impossible to depend on one and not on others) in to one component.
Tension diagram for component cohesion
The above diagram shows the tension diagram for component cohesion. The edges of the diagram define the cost of abandoning principle on the opposite vertex.
If an architect focuses on REP and CRP then he will find too many components which change when a new requirement comes. If an architect focuses on CRP and CCP, then he will find he has too many components which are hard to reuse. If an architect focuses on REP and CCP then he will find he has components which have very frequent releases.
A good architect will need to find a position in the triangle which meets the concerns of the current development team, but would also be aware that those concerns will change over time.
For example, when I started working recently on a new project which is rearchitecting current system, our focus initially has been on CCP. We have many components which have classes that change together. At the same time, we have many classes which are reusable together, but currently they are in different components. But as the project matures, things have been changing and we are moving more towards REP.
In conclusion, we need to start grouping classes together into components, we must consider opposing forces involved in reusability and develop-ability. And also acknowledge, that partitioning that is relevant today might not be relevant in future, but would keep on evolving.
Thanks for stopping by! I hope this gives a good preview into component cohesion. Eager to hear your thoughts and chat, please leave comments below and we can discuss.
Components are smallest units of deployment. In java, they are jar files. In ruby, they are gem files and so on.
This chapter mainly focuses on history of components, how they came into existence in the software world.
Starting from the earliest –
At first, programmers used to write their programs starting the the physical address where they would be stored in memory. Programs were not relocatable. Programmers would include the source code of library functions with the application code. This resulted in longer load time if programs were big. And eventually this approach failed for very big programs.
Relocatable binaries came next to solve this problem. Compiler was changed to output binaries which included function names as metadata. These binaries also, included the address to be loaded and other flags. All this information was then given to loader. Loader would link the function names to place where it has loaded them. Now, programmers can load only the required functions using loader
The above approach worked well for small systems. As the number of libraries increase, the time taken by these linking loaders increased. And so linking and loading were separated. Programmers put the linking part into linker. The output of linker was link relocatable file that a relocating loader can quickly load. But still the loading time couldn’t be reduced.
As storage optimized, disk became short and fast, RAM became fast and efficient, time spent linking and loading application began to shrink too fast. Next came .jar files, and applications where multiple .jar files can be loaded and linked in few seconds. This was era of component plugin architecture. Here, say you want to pin photos on google chrome, just create an extension/plugin and add it to chrome and have it working.
These dynamically linked files are called the components of our architecture.
Thanks for stopping by! I hope this gives a good preview into components. Eager to hear your thoughts and chat, please leave comments below and we can discuss.
DIP says that the most stable systems are those in which source code dependencies refer to abstractions and not to concretions.
Dependency inversion principle cannot be followed as rule because in reality we have to depend on concrete implementations like operating systems, platforms, String in Java, etc. The dependencies like these which are stable and very unlikely to change are excluded from this principle.
The dependencies that are volatile, that are still developing, that can change, we should be depending on their interfaces and not on concrete implementations.
Stable Abstractions
Stable architectures are those that avoid depending on volatile concretions and that favor the use of stable abstract interfaces. They follow following rules –
Don’t refer to volatile concrete classes
Don’t derive from volatile concrete classes
Don’t override concrete functions – concrete functions have dependencies, when you override them, you inherit these dependencies. So ideally we should make functions abstract and create multiple functions.
Never mention the name of anything concrete and volatile
Thanks for stopping by! I hope this gives a good preview into DIP. Eager to hear your thoughts and chat, please leave comments below and we can discuss.
Let’s go through an example to understand this principle.
Let’s say OPS class has three operations op1, op2, and op3. And there are three users, User1 uses op1, User2 uses op2, User3 uses op3 . If there is change in op3, both User1 and User2 will have to recompile and redeploy their code.
Now instead if each of these operations are segregated into three different interfaces and each user just depends on their interface, then if there is change in op3, only user3 will need to recompile and redeploy their code. This is called Interface Segregation Principle.
ISP and Languages
In statically typed languages like Java, with statements like import, include, use, etc, create source code dependencies, that force recompilation and redeployment and so ISP becomes necessary.
In dynamically typed languages, where declarations does not exists, but inferred at runtime, there are no source code dependencies, that force recompilation and redeployment. This is primary reason systems with dynamically typed languages are more flexible and less tightly coupled.
ISP and Architecture
ISP is just not language level restriction. But it is also relevant at architecture level. It is harmful to depend on a module that contains more than you need.
For example, you are architecting a system, and you decide to use a framework that depends on a database. If there is change in the database, both the framework and your system would need to recompile and redeploy. Or if any feature in the database fails, it will call failure in both the framework and your system.
The lesson is depending on something that carries extra baggage that we don’t need can cause us trouble that we don’t expect.
Thanks for stopping by! I hope this gives a good preview into ISP. Eager to hear your thoughts and chat, please leave comments below and we can discuss.