It is well-known that separation-of-concerns and proper application layering lead to a clean design, and improves application maintenance. In my July 2010 MSDN article, “Problems and Solutions with Model-View-ViewModel” I provided a standardized and layered high-level component diagram for virtually every software application I have had the pleasure to work on:
I find that this component structure works very well: it is very easy to add new features, maintain the software, and it really maximizes flexibility. If any particular layer gets too large, it can be easily broken down into separate projects/DLLs/JAR files.
Now obviously not every application I’ve written is a .Net application (I used to work in C,C++ (shudder), Perl, and Java), and not every application I’ve written is a WPF application. So while the upper layer may change slightly to expose a different interface, the fundamental structure remains the same. Similarly, not every application has a “Main()” start-up method, but regardless of how the application starts the concepts are the same.
If everybody on the team understands the purpose, scope and dependencies of every component then the benefit of this design will survive well into the future. However, without everybody on the team understanding the purpose and scope of each component, the design will be quickly corrupted and application maintenance and feature enhancements become much more difficult (I’ll talk about an example of this in a blog post laterâ€¦).
I implement each “layer” as a simple Visual Studio project, so everything except the main executable is simply a .NET assembly/DLL project. However, if a single DLL gets “too big” (whatever that means in your context) then it can be broken into multiple projects all within the same logical layer. By implementing each layer in a separate project, it becomes very easy to enforce appropriate dependencies; for example you guarantee that the domain logic layer cannot access the database access layer, and your domain logic layer has no dependencies on your model layer. This ensures that developers do not take shortcuts that erode the benefits of the strict separation.
The Utility Layer
Like the domain model (or domain object layer) my utilities project is completely database and presentation agnostic, meaning I can use it across various projects. It contains any highly reusable code such as:
- Custom collections and data structures
- Date/time utilities
- My “Money” implementation and related classes
- IO helper classes (reading CSV files, binary searching of files, etc.)
- A generic diagnostics and logging framework
- Various reusable aspects
Currently, my Utilities library is a little more than 20,000 lines of (highly reusable) code, and the final DLL is Â»250Kb. Clean separation of the above 7 functional areas (using namespaces) would make it trivially easy to break apart into separate DLLs if size ever became an issue (say if I wanted to start porting this to be deployable to Silverlight and the regular CLR for example). But at 250Kb I’m currently unconcerned with keeping this all in a single project, and I have no detailed application context or deployment problem forcing me to break it apart.
Any code in this layer must follow several rules:
- It should be easy to imagine reusing the code with other applications;
- The code must be application/project independent;
- The code must only depend on core .Net framework libraries within the client profile;
- The code must not depend on any specific UI functionality (such as ASP.NET, HTTP, WPF, etc.);
- The code must absolutely not be related to the database.
If any of these conditions are not met then the code does not belong in the utilities project; this ensures that it is easy to reuse utility project for any application that I need to write. Thus, the utilities project becomes the repository for highly useful and reusable code that makes my day-to-day development experiences faster.
Utilities and things that change
I have switched logging frameworks a number of times, and have likewise discovered a number of different diagnostics framework (such as my favourite, Gibraltar). These sorts of things are highly reusable across all occasions, but the underlying implementation may change. For example, many years ago I used log4net, but I switched and now I use NLog.
To abstract the application from the specific implementation used, I employ the separated interface design pattern by Martin Fowler. So in my utilities library I expose generic interfaces that the application can utilize depend on. In a separate assembly/JAR I provide the implementation of those interfaces.
For example in my utilities library I have an Utilities.Diagnostics namespace with these two generic interfaces defined. The first is my basic abstraction of a logger:
And the second is the standard way of getting an instance of a logger:
A concrete Singleton class provides an easy way to retrieve an actual logger:
The logger factory needs to be initialized with a concrete instance of an ILoggerFactory before it can be used, but the utilities library does not provide such an implementation.
Instead, I have another Visual Studio project (that is logically part of the utility layer) called Utilities.NLog that implements ILogger and ILoggerFactory with a logging implementation that simply delegates to NLog. When the application starts up, the main application instantiates an NLoggerFactory instance and sets it on the LoggerFactory.Instance property.
Now, anywhere in my application I can simply add the following line of code to a class to give it logging capabilities:
private static readonly ILogger m_Log =Â LoggerFactory.CreateLogger(typeof(MyClass));
The utilities project actually does have implementation of the ILogger and ILoggerFactory interfaces:
These classes simply write all messages to the built-in .NET Framedwork System.Diagnostics.Trace.WriteLine()
method. If the LoggerFactory.Instance property has not been set, then by default they will return an instance of the DebugLoggerFactory. This means that I don’t have to worry about configuring logging for any of my unit tests, and automatically all my unit tests will log to the diagnostic logger. This is very handy because this logger is automatically displayed in Visual Studio window during debugging.
I will discuss the additional layers/components in future blog posts.