So, it's been a while since I've blogged. Mostly this has been due to other commitments (family, friends, side projects, etc), working more hours contracting than I would ideally like and generally not having any dead-time (such as when I used to commute). Since my last blog post (over a year ago!!) I've been maintaining an ever increasing list of things I'd like to blog about but haven't found the time. Given it's a new year, I've decided to pick a couple of the more challenging/contentious blog ideas and endeavour to get them written. This is the first and, while not necessarily the most contentious, might take some getting your head around. You have been warned...
There is a convention at the heart of enterprise development and many modern development frameworks which seems to have been blindly adopted by the development community at large. I hope to challenge this convention as I believe changing it can provide many useful benefits in day to day development.
The convention I'm referring to is the tendency to group classes by functional pattern rather than functional domain. What does this mean? Well, to illustrate, lets start a new webapi project:
dotnet new webapi
You will end up with a folder structure similar to this:
$:. │ appsettings.Development.json │ appsettings.json │ FluentNamespacing.csproj │ Program.cs │ Startup.cs │ ├───Controllers │ ValuesController.cs │ └───Properties launchSettings.json
There, you see it don't you? No? Well, that just shows how ingrained this convention is.
I'm referring to the
ValuesController class residing a
Controllers folder. What's the problem with that, you ask? Well, lets develop this project a little further and revisit.
Lets say, after a little conventional development, we're now here:
$:. │ appsettings.Development.json │ appsettings.json │ FluentNamespacing.csproj │ Program.cs │ Startup.cs │ ├───Controllers │ PersonController.cs │ ├───Factories │ PersonFactory.cs │ ├───Models │ PersonModel.cs │ ├───Properties │ launchSettings.json │ └───Repositories PersonRepository.cs
ValuesController has become a
PersonController as we want to serve instances of the
PersonModel. But wait, where is the
PersonModel class? Ah, of course, in the
Models folder. Ok, we'll add
using FluentNamespacing.Models to the top of the
PersonController.cs and continue.
Now as is common practice, we might want to abstract our datastore via the repository pattern so we dutifully create a
Repositories folder within which we add a
PersonRepository class. We need to use this in the
PersonController so we add another using statement to the file (
using FluentNamespacing.Repositories) bringing the
PersonRepository into scope.
Finally, when a user uses a POST call to create a new user, the
PersonController should again follow common practise and use the factory pattern to create a new
PersonModel which can then be saved via the repository. Again we go round the loop of creating a directory (
Factories), adding a class to it (
PersonFactory) and adding a using to the
PersonController.cs file (
using FluentNamespacing.Factories). Phew.
Now look at our project. We have almost as many folders as we do classes and the classes related to the "Person" entity are spread across the project. Furthermore, we've had to prefix each of our classes with the entity (i.e.
Person) it relates to in order to differentiate it from similar classes for other entities (i.e.
Group). That's a lot of typing and/or mouse navigation to get basic functionality in place.
Now consider this:
D:. │ appsettings.Development.json │ appsettings.json │ FluentlyNamespaced.csproj │ Program.cs │ Startup.cs │ ├───Person │ Controller.cs │ Factory.cs │ Model.cs │ Repository.cs │ └───Properties launchSettings.json
Here we have exactly the same functionality but grouped by functional domain not functional pattern; i.e. all classes related to a Person are in the same folder. This has resulted in far less structural noise, simplified class names and no need for additional using statements to be added to any of the classes.
I have been applying this approach in medium to large enterprise code-bases for some time now and find it provides simplified code, less structural overhead, easier navigation, faster comprehension and improved code architecture. I refer to it as "Fluent Namespacing" because, when you start referring to your classes via the [partial] namespace rather than long class names (requiring additional
using statements), the code tends to read like a fluent interface.
I believe this approach provides numerous additional benefits such as:
Want the repository for your entity? It's right along side it. As are all the other classes related to that entity. This means you can very quickly skip between these classes without needing to navigate the breadth of your project looking for a folder for the appropriate functional pattern.
When you write code which involves multiple entities, these entities need to be brought into scope. Rather than adding using statements, I have found that utilising the entity's namespace in a fluent fashion gives very elegant results.
For example, say we have introduce a
Role entity and the controller for this entity needs to be able to add
Person instances to a role. A (naïve) implementation of this class might look something akin to:
Note the lack of additional using statements and how bringing classes related to other entities into scope requires only a partial namespace which then reads like natural language (i.e. Person.Repository / Person.Model).
In general, it is desirable for classes in your project to be loosely coupled. Fluent namespacing can visually illustrate when a class might be tightly-coupled by requiring long namespace traversals. For example:
Should the Group controller really be using the Person entity's data source (
Person.Data.Source) and mapping provider (
Person.Mapping.Provider) to fetch a
Person.Model instances or would they be better using the Person entity's repository (
There are a couple of downsides to this approach such as:
When you use Visual Studio's built in refactoring methods, it will always endeavour add a
usingto your class for classes outside the current namespace, even if you've already referred to them via the namespace:
Field? Yes please. Using? No thanks!
In Visual Studio, when you need to search for a specific class, you may find you get many more hits than expected; afterall, if the
Modelclass for all entities is called
Modelsearching for 'Model' will return all the entity classes. Fortunately, Visual Studio's 'Go to All' functionality supports search by multiple key word matches so this works:
Note: The fact that these downsides are all related to tooling shows just how ingrained this convention is to modern development.
As shown, moving away from the "grouping by functional pattern" convention and instead using "Fluent Namespacing" can make your code much more readable and maintainable while requiring very little overhead. The side benefits of simplified navigation leading to easier comprehension and the potential for highlighting possible code-smells can really pay dividends in a large code base. I'd very much encourage you to try Fluent Namespacing in your next project, I don't think you'll regret it.