Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Management and structure of a growing .NET project

Tags:

c#

structure

We're building a .NET software platform for test automation for in house use in our company.

The application is composed of a GUI (WinForms) component, and various "Actions" that are dynamically being loaded into it to be executed.

There are approximately ~ 100 Action projects going already, with this number increasing. Some of these projects are interdependent on other projects and so on.

All actions loaded must reference our "SDK" dll for various activities (results to the main app, logging, etc).

With this relatively simple design, we are facing some management decisions that we'd like to solve in the best way:

  1. Should actions ("plugins") reference our SDKs project output, or some known stable version of it? For example, when developing a large application (MS Office just for the example), not all teams work with source code for all components naturally.

What is the best solution for something like this ? and why?

  1. How to properly verify that all needed dependencies (3rd party libraries for example) are indeed taken from the correct location ?

What are common practices in scenarios where managing multitude of projects that are linked in between? are there any tips for doing so ?

like image 250
lysergic-acid Avatar asked Jun 21 '11 17:06

lysergic-acid


2 Answers

This is a problem that doesn't have a clear answer, but...

There are two paths you can take. A strongly coupled system or a loosely coupled system.

For a strongly coupled system I can suggest two directories for binaries: a 3rd party directory and a directory that houses DLLs that you company builds that other developers can reference. The 3rd party DLLs (outside your company) should be located in source control so that all developers reference the same versions of the 3rd party DLLs from the same location this avoids developer machine inconsistencies and having the problems of installing 3rd party software on every machine. The in house DLLs should not be referenced in source control and should be built on each developers machine via an automated build batch file or similiar. In a build post step you can copy them all to the same directory and as long as developers get the latest source control and build, everyone has the same DLLs from within your company.

For example, get latest, build (using a batch file to build all the projects needed), and then as a post build step copy the output to common. Now all of your other projects can reference the common compnay DLLs and the third party DLLs from the same location and everyone is consistent.

The problem is that the references are strong coupled, so changes can sometimes be problematic if not communicated properly.

A loosely coupled system uses a framework such as MEF (Managed Extensibility Framework) and your components reference "Contract DLL" which define the interfaces for your components. The project reference the interface or contract DLLs and don't really care about the implementation and then MEF manages the plugin for you.

In this case, you reference the interface DLL but not the actual DLL that implements.

For example, say I have an interface called ILog with a method called LogMessage.

private ILog _logger;
_logger.LogMessage();

So, in a strongly coupled case: Action.DLL references Logger.DLL directly.

In a loosely coupled case Action.DLL references ILog.DLL (just the interface). Logger.DLL implements ILog.DLL. But Action.DLL has no refernce to Logger.DLL directly.

Now I can have any number of DLLs that implment the ILog interface, but the Action.DLL does not reference them directly. That's pretty cool and one of the more exciting features of MEF and loose coupling in general, the ability to not to have dependencies.

How you choose to go, either way is acceptable, I think the loosely coupled idea fits your scenario the best as teams would just have to know the contracts versus the actual implementations.

I wouldn't have one massive contract DLL, I would try and break the interfaces into logical groupings. For example, logging seems like a Utility type of interfance, so I would create a Utility contract DLL with a ILog interface. How it is split up depends on what you are trying to do. Or each interface could be a contract DLL, but maybe that is a little extreme.

like image 175
Jon Raynor Avatar answered Sep 20 '22 04:09

Jon Raynor


This is a somewhat complex topic, especially in .NET land. I don't know about "best" solution, but I'll explain how we manage it; perhaps you will it useful for yourself.

This allows you to build large systems with lots of linked projects, but incurs in a lot of complexity issues. As, I think, any solution of this kind would.

First: physical structure (we use SVN).

  • There is a source control root for each project
  • Each project has its own trunk, branches and tags
  • The trunk folder has a versioned \src and \build folder, and an unversioned \lib folder The \lib folder contains binaries to reference. Those binaries could be 3rd party libraries or other projects that you need to link to (e.g., your SDK). All binaries under \lib come from an enterprise ivy repository (see http://ant.apache.org/ivy/). There is a lot of movement these days in .NET land concerning NuGet, so you could check that out too.

Your versioned \build folder contains build scripts, for example to get binaries from ivy, publish your project to ivy, or compile each project. They will also come in handy when you want to point a Continuous Integration server at each of your projects.

Second: To define where dependencies come from

Answer: They come from your ivy repository (it could be as simple as a network shared file system). You have created your repository, so you have control as to its contents. Be very careful with 3rd party binaries that get installed in the GAC. Visual Studio is a pain in the a^^ to deal with this.

Specifically:

How to properly verify that all needed dependencies (3rd party libraries for example) are indeed taken from the correct location ?

Ivy gives you a great flexibility with dependencies, and it also resolves transitive dependencies; e.g., you can depend on SDK rev="1.+" status="latest.release" which would mean "the latest stable 1.x release of SDK, or on SDK rev="2.+" status="latest.integration" which would mean the latest available binary of 2.x SDK (probably as produced from a continuous integration build).

So you will always depend on compiled binaries, never on project output. And you can control which version of the binaries to get. 3rd party dependencies will probably be brought in as transitive upon your SDK.

This also means that the amount of code in your projects will stay as small as you need to have workable Visual Studio solutions. It also means that refactoring tools like ReSharper will be a lot less useful. There will also be a certain amount of complexity concerning your build scripts and your branching strategy. That depends a lot on the logic of your components.

This is a brief overview, if you think this is the sort of thing you want I can expand the answer. Good luck; the .NET ecosystem, and Visual Studio in particular, isn't really thought to work like this.

like image 35
Alvaro Avatar answered Sep 23 '22 04:09

Alvaro