by Zoran Horvat
In previous article we have seen the definition and usefulness of coupling and instability metrics when applied to modules of the application (see How to Use Module Coupling and Instability Metrics to Guide Refactoring ). In this article, we will use NDepend to construct a query which calculates coupling metrics and instability for all modules in an application.
When talking about module coupling, we recognize two kinds of couplings. Efferent coupling is the number of types from other modules that types from this module know about. Afferent coupling is the number of types in this module that types from other modules know about.
We define instability metric as the ratio between efferent coupling and total coupling: I = Ce / (Ce + Ca).
Now that we have defined the metrics we are interested in, we can write a new CQLinq query that calculates them.
The first step towards calculating coupling metrics is to find which types are using which other types in our application. We are still not interested to know which type belongs to which application module. In terms of NDepend and CQLinq , We are only going to use the TypesUsingMe property defined on IType, which tells which other types are using this type. Here is the query:
let dependencies =
JustMyCode.Types
.SelectMany(t => t.TypesUsingMe.Select(usingT => new { Type = t, UsedBy = usingT }))
.Where(pair =>
pair.Type.ParentNamespace.Name.Contains(".") &&
pair.UsedBy.ParentNamespace.Name.Contains(".")) // Remove app. entry point
This query forms pairs with two properties. Type property tells which type is used. UsedBy property tells which type is using it. Note that we are removing all types that have only one segment in the full namespace path. This is by convention, because in this application we assume that only the application entry point, including Inversion of Control setup, is located at the root namespace. All other types are supposed to be defined in nested namespaces.
If you want to test this partial query, just add a simple line that will construct the query output as expected by NDepend :
from dep in dependencies select dep
This query will produce all pairs of types that know about each other, as shown in the following picture.
This picture shows the result of the query defined above when run on a sample application. In this small application we have 80 pairs of types that know about each other, excluding types defined in the application root namespace.
Now we are ready to make the next step, and that is to enrich these pairs with direction of the dependency - it is not the same whether one type is using the other, or it is used by the other. The prior case contributes to efferent coupling, while the latter contributes to afferent coupling.
Now that we have pairs of types, where the first type in pair is used by the second type in pair, we can add the efferent/afferent flag to pairs. Basically, each pair we have defines one afferent coupling, because the first type in the pair is used by the other type. Now we can define one additional pair for each of the pair, only this time the elements will switch places. Code is telling more than words:
let afferentDependencies =
from dep in dependencies
select new { Type1 = dep.Type, Type2 = dep.UsedBy, Direction = "Afferent" }
let efferentDependencies =
from dep in dependencies
select new { Type1 = dep.UsedBy, Type2 = dep.Type, Direction = "Efferent" }
Now we have defined two collections, one telling about afferent couples, the other telling about efferent couples. We can simply query the union of these two to get all the couplings in the application:
from dep in efferentDependencies.Union(afferentDependencies) select dep
The result of running this query is shown in the following picture. You can see that some pairs are defined as efferent dependencies, while others are defined as afferent dependencies.
We are gradually moving forward. Now that we know the direction of each dependency, we are ready to put boundaries around types. We want to identify the module each of the types belongs to, because our ultimate goal is to calculate coupling metrics for modules, not for types.
The definition of module varies from application to application. Sometimes, primarily in larger applications, we want one assembly to be one module. In smaller applications it often happens that all modules are wrapped in one assembly.
As a consequence, this step in our process of building the query which calculates coupling and instability of modules will not be universal. You have to adapt this step to your particular application. As an example, we can assume that all classes in the application are following a naming scheme for namespaces. We can see all the namespaces in the application by running this query:
from ns in JustMyCode.Namespaces select new { ns, ns.Name }
For the demo application we are querying in this article, this query returns total of 10 namespaces:
DemoApp
DemoApp.Presentation.Interfaces
DemoApp.Presentation.PurchaseReports
DemoApp.Presentation.Implementation
DemoApp.Presentation.Implementation.Commands
DemoApp.Application.Implementation
DemoApp.Application.Interfaces
DemoApp.Domain.Interfaces
DemoApp.Domain.Implementation
DemoApp.Infrastructure.Implementation
We have already ruled out root namespace (DemoApp) because we assume that only the application entry point and related code is placed there. This leaves us with total of nine namespaces containing types we want to analyze.
Now comes the critical decision - which namespaces form which modules. We can decide that the second segment in the namespace identifies the module. Hence, we have four modules: Presentation, Application, Domain and Infrastructure. It will be very simple to extract module name from each type name. Here is the continued query which starts with the efferentDependency and afferentDependency collections calculated earlier and uses them to build a single collection which identifies both the modules and the direction of each dependency:
let allModuleDependencies =
from dep in efferentDependencies.Union(afferentDependencies)
select new
{
Type1 = dep.Type1,
Module1 = dep.Type1.ParentNamespace.Name.Split('.').ElementAt(1),
Type2 = dep.Type2,
Module2 = dep.Type2.ParentNamespace.Name.Split('.').ElementAt(1),
Direction = dep.Direction
}
As you can see, we are taking the second segment in the type's namespace name as the name of the module that type belongs to. This was very easy and it supports all types currently existing in the application, as well as types that are going to be added in the future, as long as new types conform to the same naming convention.
We can see the results of this query by selecting data from it:
from dep in allModuleDependencies select dep
This query produces the result as shown in the following picture.
But as you can see, the results of the query are not perfect. We can see that the output contains dependencies that are internal to a module. IApplicationServices interface is used by the LoginCommand concrete class, for example, but both the interface and the class belong to the Presentation layer. We certainly don't want to have these pairs of types affect module coupling. Module coupling should only be affected by couplings to and from classes defined in other modules. That will be the next step in our process - to clean up the couplings that are internal to modules.
The next step in the process of building this elaborate query will be to remove internal coupling, i.e. coupling in which both ends of the dependency belong to the same module. Now that we have isolated module names, it will be easy to filter out internal coupling:
let moduleDependencies =
from dep in allModuleDependencies
where dep.Module1 != dep.Module2
select dep
This query simply removes records that have the same module name on both ends of the relation. We can test the query by selecting data from moduleDependencies collection:
from dep in moduleDependencies select dep
This query produces much shorter result set than the previous one. As you can see from the picture below, the result set does not contain internal coupling anymore.
We are advancing with the query. The next step will be to group the couplings by module name and then see how many types are known across the module boundary. Types from other modules that this module knows about will define its efferent coupling. Types inside this module that other modules know about will define its afferent coupling.
This is the only complicated step in the whole process. We have to group couplings by modules and then see how many types there are inside and outside the module that are known about. Here is the query, and then we will discuss it:
let rawCoupling =
from dep in moduleDependencies
group dep by dep.Module1 into g
select new
{
SeedType = g.First().Type1,
Module = g.Key,
EfferentCoupling =
g.Where(tuple => tuple.Direction == "Efferent")
.Select(tuple => tuple.Type2).Distinct().Count(),
AfferentCoupling =
g.Where(tuple => tuple.Direction == "Afferent")
.Select(tuple => tuple.Type1).Distinct().Count()
}
The first step in this query is to group the records by the originating module name. After that, we can construct the output record. The first field in output (SeedType) is there to satisfy NDepend's requirement that output record must start with either type, namespace or assembly. We are just taking the first type from each group to satisfy this constraint. This particular field has no other purpose in the query.
After that, we are picking the module name. This is the key to the record. Each module name will have one record in the output.
Next field is EfferentCoupling of the module. We calculate efferent coupling by counting types from other modules that types from this module are connected with through the efferent dependency. Note that we are picking all such types and then calling the Distinct method before counting them. This is crucial, because we don't want to count any particular class more than once.
The last field is AfferentCoupling and it is calculated in much the same way as the previous one, only this time we are counting types defined in this module that are taking part in afferent coupling relations. Once again, we are counting distinct types. We don't want any type to be counted more than once.
We can see what this query is producing by selecting data from its output:
from coupling in rawCoupling select coupling
The result of this query is shown in the following picture.
We can see that there is one row per module, as expected. For each module we can see how many types from other modules it knows about (EfferentCoupling column), as well as how many types there are in it that other modules know about (AfferentCoupling). Now that we have these values, we can finally calculate instability metric for all modules.
At this stage, we have all input data we need to calculate the Instability metric. Remember, if we have efferent coupling (Ce) and afferent coupling (Ca) counts, then instability metric is calculated as: I = Ce / (Ce + Ca). Hence the following query which does precisely that:
let coupling =
from couple in rawCoupling
select new
{
SeedType = couple.SeedType,
Module = couple.Module,
EfferentCoupling = couple.EfferentCoupling,
AfferentCoupling = couple.AfferentCoupling,
Instability =
couple.EfferentCoupling / (double)(couple.EfferentCoupling + couple.AfferentCoupling)
}
In the end, it only remains to query data produced by the query:
from c in coupling select c
When this query is run, it produces output shown in the picture below.
Finally, we have seen the overall results for the Instability metric for our sample application. What can we learn from the results?
From high-level towards low-level modules, we see that Presentation module is absolutely stable, with instability metric equal to zero. This is important result because it says that all other parts of the application are adapting to the presentation layer. This is generally considered a good thing, because we want the application to be fully subdued to the user's needs.
One step below we find the Application layer. With instability metric value 0.8 it is pretty much instable. This sounds reasonable if we know that Application layer is adapting its implementation to the needs of the Presentation layer. That is where all the efferent dependencies are coming from, so we can accept this result as reasonable.
One more step below we find the Domain layer, which has instability close to 0.5. This means that Domain layer is dependent on higher level modules, which are defining the requirements it must meet, while at the same time lower-level modules are dependent on the Domain. This is because lower-level modules have to provide data and services the domain needs to implement domain logic. The result for the Domain layer is more or less expected and it also depicts why it is so hard to design Domain layer well.
Finally, at the very bottom of the application stack, we find the Infrastructure layer. With Instability metric value 1, this layer is absolutely instable. This makes sense and it tells us that infrastructure must adapt whenever a change is made in higher-level modules. We want to see the Infrastructure layer be absolutely instable. We don't want any other part of the application to have to change when infrastructure changes.
Bottom line is that coupling and instability metrics on the sample application have shown that we have met some of our design goals. Coupling between modules is just right and we cannot detect any design issues when looking at these metrics.
In this article you have seen the process of building a query which calculates coupling and instability metrics for modules in an application. One of the steps was application-specific. It was the part where we were determining which type belongs to which module. All other elements in the query are generic and can be applied to any application.
And here is the final query which can be used to calculate coupling and instability of all modules in an application.
let dependencies =
JustMyCode.Types
.SelectMany(t => t.TypesUsingMe.Select(usingT => new { Type = t, UsedBy = usingT }))
.Where(pair =>
pair.Type.ParentNamespace.Name.Contains(".") &&
pair.UsedBy.ParentNamespace.Name.Contains(".")) // Remove app. entry point
let afferentDependencies =
from dep in dependencies
select new { Type1 = dep.Type, Type2 = dep.UsedBy, Direction = "Afferent" }
let efferentDependencies =
from dep in dependencies
select new { Type1 = dep.UsedBy, Type2 = dep.Type, Direction = "Efferent" }
let allModuleDependencies =
from dep in efferentDependencies.Union(afferentDependencies)
select new
{
Type1 = dep.Type1,
Module1 = dep.Type1.ParentNamespace.Name.Split('.').ElementAt(1),
Type2 = dep.Type2,
Module2 = dep.Type2.ParentNamespace.Name.Split('.').ElementAt(1),
Direction = dep.Direction
}
let moduleDependencies =
from dep in allModuleDependencies
where dep.Module1 != dep.Module2
select dep
let rawCoupling =
from dep in moduleDependencies
group dep by dep.Module1 into g
select new
{
SeedType = g.First().Type1,
Module = g.Key,
EfferentCoupling =
g.Where(tuple => tuple.Direction == "Efferent")
.Select(tuple => tuple.Type2).Distinct().Count(),
AfferentCoupling =
g.Where(tuple => tuple.Direction == "Afferent")
.Select(tuple => tuple.Type1).Distinct().Count()
}
let coupling =
from couple in rawCoupling
select new
{
SeedType = couple.SeedType,
Module = couple.Module,
EfferentCoupling = couple.EfferentCoupling,
AfferentCoupling = couple.AfferentCoupling,
Instability =
couple.EfferentCoupling / (double)(couple.EfferentCoupling + couple.AfferentCoupling)
}
from c in coupling select c
If you wish to learn more, please watch my latest video courses
In this course, you will learn the basic principles of object-oriented programming, and then learn how to apply those principles to construct an operational and correct code using the C# programming language and .NET.
As the course progresses, you will learn such programming concepts as objects, method resolution, polymorphism, object composition, class inheritance, object substitution, etc., but also the basic principles of object-oriented design and even project management, such as abstraction, dependency injection, open-closed principle, tell don't ask principle, the principles of agile software development and many more.
More...
In this course, you will learn how design patterns can be applied to make code better: flexible, short, readable.
You will learn how to decide when and which pattern to apply by formally analyzing the need to flex around specific axis.
More...
This course begins with examination of a realistic application, which is poorly factored and doesn't incorporate design patterns. It is nearly impossible to maintain and develop this application further, due to its poor structure and design.
As demonstration after demonstration will unfold, we will refactor this entire application, fitting many design patterns into place almost without effort. By the end of the course, you will know how code refactoring and design patterns can operate together, and help each other create great design.
More...
In four and a half hours of this course, you will learn how to control design of classes, design of complex algorithms, and how to recognize and implement data structures.
After completing this course, you will know how to develop a large and complex domain model, which you will be able to maintain and extend further. And, not to forget, the model you develop in this way will be correct and free of bugs.
More...
Zoran Horvat is the Principal Consultant at Coding Helmet, speaker and author of 100+ articles, and independent trainer on .NET technology stack. He can often be found speaking at conferences and user groups, promoting object-oriented and functional development style and clean coding practices and techniques that improve longevity of complex business applications.