I, SHARED PROJECTS
“n-dependent vs in-dependent”
There’s a long history in programming of having trouble with dependencies. Probably the most famous ones historically have been the Windows dll-hell, largely solved by .NET Framework’s independent dlls. And on the other side of the fence, the Linux package hell wasn’t all that different.
Today, NuGet and JavaScript libraries, despite being relatively easy to work with with their dependency checkers, auto-downloads and what not, are doing a fine job of partly repeating some of the same issues, on a project scale. Dependencies and interdependencies can get quite crazy if you’re not careful on the web side of things, and NuGet is starting to get tricky with interdependencies and conflicting installs as well.
On the .NET side of things, we’re starting a new phase of complications now that .NET Core is bringing both backward incompatibility and multi-platform development to the mix. And not just that — every small part of .NET framework can be installed and updated independently, which makes it light, flexible. However, on the flip side there is a very good chance that this will also make it harder to manage dependencies again, but now even within components of the same framework. The potential increase in complexity seems large.
Wouldn’t it be nice if some code just didn’t have any dependencies at all?
Enter the far less well-known Shared Project. For those few that have tried it, like me, it has quickly becomes. favorite tool for making source-code dependencies: by reducing them. A lot!
Shared Projects were made available to Visual Studio in SP2 of Visual Studio 2013. Not being able to have any dependencies on its own, Shared Projects inherit dependencies from whatever project links to it that can. So say that you have a lot of business logic that uses AutoMapper, or NewtonSoft, etc. — you can still chose to have just one dll hold your application logic, reference AutoMapper and NewtonSoft to this dll, and then any Shared Project you link to this dll will have access to the libraries. Sounds simple right?
Visual Studio makes it even simpler: for any code in a Shared Project that actually references one of these libraries, it will show you in a tooltip what project is fulfilling the dependency. Even nicer, if there are multiple dlls, services or executables referencing the shared project, it will show this for each one.
“If you don’t reference me, do I still exist?”
Why use Shared Projects?
There are so many good reasons to use Shared Projects, that I’m inclined to suggest you reverse the question. Use them, unless you find a very good reason not to!
But to name a few: first, only one application dll for instance needs to have all the references, so that is easy to update and maintain. If you end up separating out your logic in a lot of smaller, reusable projects, this can save a lot of management overhead and potential dependency conflicts that you could get with directly linking to dlls or using NuGet packages.
Also, your Shared Projects really are just code. So they are very light-weight, and compile very quickly. They may be a bit harder to share with the world than a NuGet Package, but not impossible — since they are just code, you could share them using GitHub or TFS, for instance, and then just branch them into your project. Referencing external code sources directly from say Github is already very common in JavaScript and other scripted setups as well.
As you now have just source code, you can branch a version of the code, and take all the advantages of adding features or fixing issues there. You’d also be free to at some point do a pull request to offer your improvements to the original library source code, giving back to the community or the corporate know how pool. Even if you do plan to make a NuGet package eventually, that too will become easier.
“Can Platform-less be the best platform?”
A very strong advantage also is being able to share the code in a solution that targets different, incompatible platforms. For example, you could write a project hat has all its logic in Shared Projects, right up to and including basic UI controllers. Then you create a single dll project called MyProject that references these Shared Projects. This dll then, you link to from the various different clients, including WPF, UWP, ASP.NET MVC 5 clients.
Now say that you want to share this code also with a Xamarin client. Xamarin (at the time of writing at least) supports Shared Projects natively, alongside portable class libraries. But: not the regular class library that you generated above for the WPF etc clients. So now you have two choices — either link all the individual Shared Projects to the Xamarin client, or I could create a portable class library and link the Shared Projects there, and then link the Xamarin client to that. Depending on your situation one or the other may be easier, but it should be clear that making new projects for several different dlls, share code from there, and link that to your Xamarin Clients, is far more work and harder to maintain, especially if you want to make changes across platforms.
“Get ready for some hard .NET Core benefits!”
At the time of writing, we are still waiting to be able to do the same for .NET Core projects, because the current project setup in Visual Studio 2015 does not support it yet. Presumably as soon as the .NET Core project setup has reverted from the json format, we will also get the Shared Projects back (it may well be part of the reason why it is reverting in the first place). That it wasn’t supported from the very start is actually rather surprising — when they were introduced in VS2013 SP2, I personally thought .NET Core was the very reason for their existence. But I would be very surprised if they don’t return at the next update.
Anyway, if you have played around with .NET Core projects for a while, it should be very easy to see the benefits here, very similar to the previous example.
Now, while it is no problem at all if your Shared Project is depending on, say, a NuGet package, the more you work with Shared Projects and NuGet packages, the more you may well find yourself starting to worry about the NuGet package as a dependency that your own program logic needs to be isolated from. Perhaps even as much as any other third party datasource or API — because after all, in some cases, that’s really what they are!
Shared Projects, Shared Interfaces
But the outside edges of your code aren’t the only places where you can reduce your risk and prevent yourself from unwittingly building a ball of mud. Wherever possible, writing good interfaces to declare the functionality your business logic is a great way to further reduce your code’s dependencies. Remember separation of concern — your class should do exactly and only what it was created for. If you find you are combining functionality of more than two other components, consider if you should split up more. If your class needs to persist state, only declare in an interface what state and what calls I need to update or retrieve it. Don’t make your ORM model contain logic that you can’t test or mock. Whenever you find yourself doing this, it is time to wrap it in an interfaced object, doubly so if your ORM doesn’t support using Interfaces directly (hello, EntityFramework … ). Also never have code where such model objects are created plainly like this:
var model = new ORMModel();
You always want to have this done from a business logic class that functions as a factory pattern. E.g.
var model = Models.CreateNew()
or
var newModelVersion = Models.CreateNewVersion(existingModel)
If you provide an interface for Models as well, you can then reimplement that in your test project for mocking or for providing reference objects, instead of having to rely on (potentially very hard to maintain) Builders in your test project, again improving your Agility during the project.
And if your business classes are modest about their scope and properly declare what they need from others but is not their concern, then your tests are going to be a lot easier to write and, crucially, maintain.
Throw New Exception: “Sorry, this code has expired, one of its ingr… dependencies has gone sour.”
CONCLUSION
In my ideal world of open source software, Shared Projects could end up being distributed as easily as NuGet packages, giving you the option to either branch or get a compiled version however you desire. In fact, thinking about it, it could be awesome to create, publish and search code only libraries by their Interfaces (hopefully one day someone reads this and thinks: “that exists!”).
But right now, you can already use branching and sharing from public or private source code repositories like GitHub and Team Foundation Server. This seems clearly the more durable approach for code that is more under development, though for now NuGet package are certainly also going to remain the premier publication mechanism for stable third party solutions. But when choosing for NuGet, just never forget: those packages come with string attached, both when publishing and linking! Be aware and be careful. There are plenty of pitfalls — in a large solution with many projects for instance, you need to source your NuGet packages from a higher level folder so all 80 projects don’t have to download everything individually. Your tests can easily (and needlessly!) become dependent on them too. And don’t check them into your GitHub project either — your space there is finite, and you’ll literally need to rewrite history to get rid of them!
Whether you are building a large project or MicroServices, whether you want more flexibility in packaging your logic, or where you are creating libraries easily shared across incompatible platforms; whether just want clean code that stays healthy for a long time, or want to prepare for anything else in the unknown future: Shared Projects are a great idea, and I highly recommend giving them a quick go. After just over a year now, I haven’t found a single reason to go back.
(also published on medium.com)