12

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.

  • Also, if this is not a good forum for a discussion like this, can someone suggest a better one? – Josh Oct 2 '13 at 14:23
8

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.

  • Yes, this makes sense. The problem is in the practical application. As you do updates to Foundation for v1.1 you want to use those changes in Project B as you're developing. If the way you have been sharing that code is through nuget, then your options are: 1) Publish new nuget package, or 2) Manually copy dll's around. Neither of these seem like good choices. – Josh Oct 2 '13 at 16:21
  • Some of this can be mitigated by having an effective test suite, but there's still going to be back and forth as you add features. – Josh Oct 2 '13 at 16:22
  • @Josh Yes, the updates to Foundation should be complete and testable independent of how Project B is going to use them (since they are going into a shared library), so 'publish new nuget package' is the natural way to go. Don't import and use Foundation v.Next in Project B until it's a stable package in nuget. It takes a little discipline, but it's a lot better than the mess of doing it otherwise. – Eric King Oct 2 '13 at 17:14
  • 1
    I totally understand what you're saying, but I don't think it's very realistic. As you develop a feature, even if you have all the business logic and data access layer fully tested, there is going to be modification before it gets pushed to production. Maybe you realize you need to add another property to the business logic, or the product owner comes back with changes or clarifications. Maybe some of your simplifying assumptions were wrong. These are things that happen all of the time. If you're limited to locking in released of your base logic, it seems very limited indeed. – Josh Oct 2 '13 at 17:40
  • 2
    As we've been doing this for a few months, this is basically what we settled on. I think I was too worried about having a formal process. Once we started actually working on it, things ended up going really smoothly. Thanks for your input. – Josh Jan 22 '14 at 21:43
4

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.

  • Maybe I wasn't as clear as I could be in my question. This is about what comes next. You've done all that, and now you need to share your code effectively between your different projects. Someone says "just use nuget for that" and it sounds great until you get into the details and run into problems - like how to synchronize different changes from different branches and applications. – Josh Oct 2 '13 at 14:20
  • Well, I agree with Eric King. Foundation code should be pushed as a stable version, not an unstable one. The key is maintaining a stable API: if you know the signatures of your foundation methods are not going to change, you can safely push the methods to Foundation and refactor them later without breaking anything. – Robert Harvey Oct 2 '13 at 15:37
4

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:

  1. 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.

  2. 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.

  3. 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.

1

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.

  • I"m not talking about managing glue code. We've already got a solution for that and it works well. I'm talking about sharing business logic. For example, our applications deal with managing organizations and people. So, we need to create a person, and there's a bunch of business rules associated with how a person should be created. Instead of creating it several times, we want to have one package that handles everything from constructing the object to persisting it that we share between the different projects that need to create people. – Josh Oct 2 '13 at 14:27
0

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.