In our work, we have several different .net applications that share a lot of base functionality. We’ve built these applications using a clean n-tier architecture, but we’ve hit that moment where we realize that we’ve re-implemented the same functions several different times. Obviously this violates DRY, and we would like to correct that. We’re already using Nuget to some success for common glue code (wiring up IoC, logging, settings), but we also would like to share our data and business layers between all our applications. The idea is that the UI would only deal with the parts of the business layer it actually needs.
This seems like a straight-forward problem at first, but ongoing development could provide some pitfalls and we’re not sure how to proceed. Let’s say we make our One Business Layer to Rule Them All. For brevity, I’ll call it “Foundation.” We port our applications to use the Foundation, and everything is runnding great. The Foundation is distributed to light UI layers via nuget, and we’re looking good. But then we start adding features to our applications, and we run into trouble.
Let’s say we’re working on Project A and we add a new feature that requires changes to Foundation. We make the changes to foundation (Foundation-A) and push them out to the nuget feed as an unstable package. Project A gets the latest nuget package, and all is good. Meanwhile, another developer is working on Project B. He gets the latest Foundation from source control, but takes it from a stable branch, so that it doesn’t have Project A changes in it. He makes changes and created Foundation-B. And all is good. But then we discover that Foundation-A and Foundation-B implementation functionality that could actually share code, so we combine them. Meanwhile Foundation-C is floating out there with it’s own changes. Eventually, Foundation-B is ready for production, so we push it out. But then we need to update Production A, B, & C with the new foundation, so we update the nuget packages and deploy (as long as nothing breaks).
This seems like it could work, but we’re worried about working with different database schemas and keeping everything synchronized between the various branches of the Foundation repository as well as the Project A, B, and C repositories. It seems like it will probably take a lot of manual work, which opens up the possibility for errors. I would like this as automated as possible.
Here’s the stack we’re using: C#, TFS with Continuous Integration, Nuget. Our applications are all various types of ASP.NET applications. We’re willing to look at different SCM’s if it will make things easier.
I’m looking for ways to keep Nuget sane with our different source code branches. We don’t want to accidentally push development code into production because we reference the wrong Nuget Package.
1
We make the changes to foundation (Foundation-A) and push them out to the nuget feed as an unstable package.
Here’s where your problem begins… Don’t do that.
Any changes to Foundation v1.0 should inherently be valuable to all consumers of Foundation, otherwise it doesn’t belong in Foundation. So, when creating the nuget package, do it as an official, stable version of Foundation (i.e. v1.1), or don’t do it at all.
Project B should build its Foundation enhancements as it normally would, but (in good source management fashion) should merge in the trunk changes (v1.1) before pushing a stable Foundation (v1.2) to nuget.
Other projects which can use the Foundation enhancements can upgrade their nuget references when appropriate, or stick with the older versions if they need to.
I agree with @Giedrius; this seems to me to be more of a source control/branching issue in the sense that if the branching/merging of Foundation is handled properly, the package management issues become moot.
6
Refactor your duplicate code into functions that are applicable in a more abstract way, and put them into their own libraries or frameworks. Make them loosely-coupled and architecture-agnostic, and you should never have any problem. If you need inspiration, study how the .NET framework abstracts concepts using things like interfaces, generics, and software patterns such as Dispose()
.
Also, remember that not all duplicate code is really duplicated; some of it is glue code or code that is otherwise necessary for sustaining the architecture, so don’t obsess over being too DRY.
2
Code Reuse
There have been several approaches to code reuse that have found favour over the years. Each approach has it’s place, and more importantly its issues, but the viable ways to reuse code in .NET are:
-
Common Library. The code that is needed in more than one place is put into a common library, and all other parts of the code base then have a single reference to this code. The principle downside is that you end up with the majority of your project depending on this library which contains lots of unrelated functions. This is a bad idea from a quality assurance aspect.
-
Common Source Code. The code that is needed in more than one place is written once and placed in a source file in a common directory structure. All projects that need this code then include this file as one of their source files. This provides code reuse, and the advantages of write once, use many. However. Its downside is that it becomes possible to have different parts of the project compiled with different versions of this code – which may introduce some subtle defects that can be very tricky to detect and identify by quality assurance.
-
Service. The common code is implemented as a service, that other aspects can access. This has the advantage that there will be a single service in a deployment, and avoids dependencies. However it will introduce latency and failures. This kind of approach works well in large distributed products where high availability and fault tolerance are already understood and managed.
Managing NuGET
Here you have a much more interesting problem. Managing multiple configurations. My advice here is to not to manage a diverse customer base with different code versions, but with configuration files. There are at least 3 layers of configuration data to manage. Base (internal) product configuration that the customer never sees. The default configuration options that your customer may change and the final layer of configuration options that your customer have changed. By separating these different layers you will be able to roll out upgrades without destroying your customers configurations.
I think problem is not in nuget/source control/branching, but in what slips into glueing code.
Robert has nice answer, just to see whole picture, I recommend to think about what dependencies these common utilities will bring into each project:
http://ayende.com/blog/3986/let-us-burn-all-those-pesky-util-common-libraries
http://blog.jordanterrell.com/post/CommonUtility-Libraries-Dead.aspx
Best way to avoid the hell you’re having is to open source your glue code. In such way you’ll start to care, that no business logic would go public, so no concrete project dependences will slip into glue code, that it would be abstract enough to be reused by anyone, even outside your company – and if that glueing code will be good enough – you’ll get community input also.
1
The solution is simple : create separate project for it, and manage it as separate thing : it’s own requirements, testing, conventions, documentation, planning, people, etc.. This way, you ensure that quality remains high and possible breaking changes will get evaluated first before they create a problem.
Or even better : make it “open-source” within your organization. So anyone can change the code, but only few selected people will have full commit rights. And those people will be responsible for ensuring quality and correct features.