One of the perks of being an MVP is to receive some tools for my own use, so I can evaluate them and if, I find them valuable, I can use them on a daily basis. On my work as an architect/consultant, one task that I often find is to analyze an application and suggest changes to make it more robust and maintainable. One tool that can help me in this task is NDepend (https://www.ndepend.com/). With this tool, you can analyze your code, verify its dependencies, set coding rules and verify if the how code changes are affecting the quality of your application. For this article, we will be analyzing eShopOnWeb (https://github.com/dotnet-architecture/eShopOnWeb), a sample application created by Microsoft to demonstrate architectural patterns on Dotnet Core. It's an Asp.NET MVC Core 2.2 app that shows a sample shopping app, that can be run on premises or in Azure or in containers, using a Microservices architecture. It has a companion book that describes the application architecture and can be downloaded at https://aka.ms/webappebook.
When you download, compile and run the app, you will see something like this:
You have a full featured Shopping app, with everything you would expect to have in this kind of app. The next step is to start analyzing it.
Download NDepend (you have a 14 day trial to use and evaluate it), install it in your machine, it will install as an Add-In to Visual Studio. The, in Visual Studio, select Extensions/NDepend/Attach new NDepend Project to current VS Solution. A window like this opens:
It has the projects in the solution selected, you can click on Analyze 3 .NET Assemblies. After it runs, it opens a web page with a report of its findings:
This report has an analysis of the project, where you can verify the problems NDepend found, the project dependencies, and drill down in the issues. At the same time, a window like this opens in Visual Studio:
If you want a more dynamic view, you can view the dashboard:
In the dashboard, you have a full analysis of your application: lines of code, methods, assemblies and so on. One interesting metric there is the technical debt, where you can see how much technical debt there is in this app and how long will it take to fix this debt (in this case, we have 3.72% of technical debt and 2 days to fix it. We have also the code metrics and violated coding rules. If you click in some item in the dashboard, like the # of Lines, you will see the detail in the properties window:
If we take a look at the dashboard, we'll see some issues that must be improved. In the Quality Gates, we have two issues that fail. By clicking on the number 2 there, we see this in the Quality Gates evolution window:
If we hover the mouse on one of the failed issues, we get a tooltip that explains the failure:
If we double-click in the failure we drill-down to what caused it:
If we click in the second issue, we see that there are two names used in different classes: Basket and IEmailSender:
Basket is the name of the class in Microsoft.eShopWeb.Web.Pages.Shared.Components.BasketComponent and in Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate
One other thing that you can see is the dependency graph:
With it, you can see how the assemblies relate to each other and give a first look on the architecture. If you filter the graph to show only the application assemblies, you have a good overview of what's happening:
The largest assembly is Web, followed by Infrastructure and ApplicationCore. The application is well layered, there are no cyclic calls between assemblies (Assembly A calling assembly B that calls A), there is a weak relation between Web and Infrastructure (given by the width of the line that joins them) and a strong one between Web and ApplicationCore. If we have a large solution with many assemblies, just with that diagram, we can take a look of what's happening and if we are doing the things right. The next step is go to the details and look at the assemblies dependencies. You can hover the assembly and get some info about it:
For example, the Web assembly has 58 issues detected and has an estimated time to fix them in 1 day. This is an estimate calculated by NDepend using the complexity of the methods that must be fixed, but you can set your own formula to calculate the technical debt if this isn't ok for your team.
Now that we got an overview of the project, we can start fixing the issues. Let's start with the easiest ones 😃. The Infrastructure assembly has only two issues and a debt of 20 minutes. In the diagram, we can right-click on the Infrastructure assembly and select Select Issues/On Me and on Child Code elements. This will open the issues in the Queries and Rules Edit window, at the right:
We can then open the tree and double click on the second issue. It will point you to a rule "Non-static classes should be instantiated or turned to static", pointing to the SpecificatorEvaluator<T&> class. This is a class that has only one static method and is referenced only one time, so there's no harm to make it static. Once you make it static, build the app and run the dependency check again, you will see this:
Oh-Oh. We fixed an issue and introduced another one - an API Breaking Change - when we made that class static, we removed the constructor. In this case, it wasn't really an API change, because nobody would instantiate a class with only static methods, so we should do a restart, here. Go to the Dashboard, in Choose Baseline and select define:
Then select the most recent analysis and click OK. That will open the NDepend settings, where you will see the new baseline. Save the settings and rerun the analysis and the error is gone.
We can then open the tree again and double click on another issue that remains in Infrastructure. That will open the source code, pointing to a readonly declaration for a DBContext. This is not a big issue, it's only telling us that we are declaring the variable as readonly, but the object it's storing is mutable, so it can be changed. There is a mention of this issue in the Framework Design Guidelines, by Microsoft - https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/. If you hover the mouse on the issue, there is a tooltip on how to fix it:
We have three ways to fix this issue:
- Remove the readonly from the field
- Make the field private and not protected
- Use an attribute to say "ok, I am aware of this, but I don't mind"
The first option will suppress the error, but will remove what we want to do - show that this field should not be entirely replaced with another dbcontext. The second option will remove the possibility to use the dbcontext in derived classes, so I'll choose the third option and add the attribute. If I right-click on the issue in the Rules and Queries window and select Suppress Issue, a window opens:
All I have to do is to copy the attribute to the clipboard and paste it into the source code. I also have to declare the symbol CODE_ANALYSIS in the project (Project/Properties/Build). That was easy! Let's go to the next one.
This is an obsolete method used. Fortunately, the description shows us to use the UseHiLo method. We change the method, run the app to see if there's nothing broken, and we're good. W can run the analysis again and see what happened:
We had a slight decrease in the technical debt, we solved one high issue and one violated rule. As you can see, NDepend not only analyzes your code, but it also gives you a position on what you are doing with your code changes. This is a very well architected code (as it should be - it's an architecture sample), so the issues are minor, but you can see what can be done with NDepend. When you have a messy project, this will be surely an invaluable tool!