Visual Studio Projects: A Tutorial in Setting Up Solutions to Succeed
August 20, 2010 1 Comment
As a consultant, I have spent time in numerous environments at various companies. And while I find differences in each of the environments, there are some elements I find in multiple companies that impede there development efforts. One of these is setting up their projects.
In this post, I am going to show a general method of setting up a Visual Studio project that works well on projects that encompass numerous teams, or even program level development that spans multiple applications. The goals of the method are:
- Aid in creating testable software
- Increase team velocity by allowing teams to work in parallel
- Develop with lower risk
In this post, I am going to cover the following topics, from a high level view.
- Domain Driven Design
- Contract First Development
- Loose Coupling and Separation of Concerns
- Working with multiple teams
The items above are covered rather loosely, so I will call them out as we approach them.
Contract First
Let’s jump in feet first.
Rather than beat around the subject, one of the core elements of my set up is focusing on contract first development. This is a paradigm shift for many people in two respects.
- Contract First means you spend time planning the contracts up front, rather than developing via a stream of consciousness, determining contracts later
- Contract first dictates setting up interfaces for your software, which many developers are not used to, until they need to add an interface
The core of contract first is I determine anywhere there is a boundary and figure out its public interface, or contract, up front. Some of the boundaries are obvious, as they end up in some form of “user” interface (“user” in quotes here, as the user may be a system, not a human being). A good example of this type of boundary, from a systems perspective, is a “web service”. Note that “web service” here means any service that can be served via a network, so this could be ASMX, Remoting or WCF. When many people talk about web services, they generally only focus on services that reside in IIS and use SOAP, but I don’t see the server or message protocol as a determinant factor, especially when we look at WCF which allows you to switch protocols quite easily. If you want to simply the definition, you can call this a service boundary.
The other main boundary we routinely work with is an assembly boundary, which is exposed the public methods on our classes. In reality, this is the same boundary as a web service, except how the interface is expressed (no WSDL for an assembly). That and we can have an assembly boundary in a single process.
I could mention separation in boundaries, like in-process, out-of-process on same machine, different machine, etc., but the main difference in type of contract lies in how the contract is expressed.
Contract Expression
On of the simplest expressions of contract is an interface. In .NET languages, you will add an interface to your project, like so:
public interface IRepository<T, in U> where T : class where U : struct { IQueryable<T> GetAll(); T GetById(U id); void Create(T obj); void Save(T obj); void Delete(T obj); }
public interface IQuoteRepository : IRepository<Quote, int> { IQueryable<Quote> GetAllByAuthorName(string authorName); }
To create a concrete example, you will have the class adhere to the interface, like so:
public class QuoteRepository : IQuoteRepository { public IQueryable<Quote> GetAllByAuthorName(string authorName) { throw new NotImplementedException(); } public IQueryable<Quote> GetAll() { throw new NotImplementedException(); } public Quote GetById(int id) { throw new NotImplementedException(); } public void Create(Quote quote) { throw new NotImplementedException(); } public void Save(Quote quote) { throw new NotImplementedException(); } public void Delete(Quote quote) { throw new NotImplementedException(); } }
Fairly simple and I believe we all get this. If we need a service boundary, we will add some attributes to the class and the interface. I then can create a Web Service Description Language (WSDL) file that describes the interface in a way it is easily consumable from a variety of environments.
The point here is the interface is a good code description of a contract, while WSDL is a good way to express the same contract for a “web service”.
Setting Up for Contract First
In Visual Studio, this is an easy exercise. The rule is every time I have a boundary, I need to have interfaces defined. The most common way I see this done is like the following screen capture:
If we run this setup against our goals, we find that we have aided the creation of testable software, as we have interfaces. And, with the interfaces, we have some ability to run teams in parallel, provided the team implementing the library does not change the library too radically. But we still have a high risk of halting development if someone from the library building team checks in broken code, as we cannot utilize the latest library for our interfaces.
There is a way to alter the solution setup to mitigate this risk: separate the interfaces. The following screen shot shows this method:
We can then isolate our teams by creating two solutions, as seen below:
Setup for Library Building team is shown in the previous example, while the setup for the team consuming the library to build the business tier library(s) is set up as follows:
The important takeaway here is both the data and business teams use the domain models and the contracts. The data team is setting up concrete implementations, while the business team is working with mocks and stubs that adhere to the contracts (interfaces). Note also that the business team can set up contracts (highlighted in the screenshot above) so the UI team can work from mocks and stubs.
Very early in the process, we should have the interfaces completed and then compile the .Contracts projects and include the compiled libraries. This should be done as soon as the contracts are firmed up and completed. The reason for doing this is it reduces the temptation to change contracts without thinking them through. This reduces the risk of having teams develop in parallel. It can also foster communication between teams, as it is quite clear when you change contract you are working with “publicly released” software, even if it is only internal to the company.
Working on the Domain
The domain is another area we have to spend some time on. In my projects, I will divide the entire solution into the following “layers” or “tiers”.
- UI – contains one or more user interface projects
- Business – contains the application logic
- Data – Data Access bits
- Framework – Components shared across multiple applications
- Domain – Domain (data) models shared across layers
- Test – Unit and Integration tests coded with a particular unit test framework
I will cover these in a bit.
Nearly every project will consume the Domain project, as it contains the domain models. The data layer will be responsible for filling the models and persisting them back. The actual data access technology should not matter (DataSets, EF, LINQ to SQL), as the models are independent of access. The business layer will perform business rules validation, etc, on the models. And the UI will bind the models in many cases. I say “many cases” as certain UI technologies may be more suited to creating presentation models that may or may not have direct parity with the domain models. A prime example of this is MVVM (Model – View – ViewModel), which is commonly used with Silverlight. The idea of separating domain models from presentation models is also present in ASP.NET MVC, especially when strongly typed models are used on your views.
Step-By-Step
Before going through the steps, let’s make up a silly story of what we are building.
Greetings, Inc. is a company that creates hello messages that plug into other company’s application. They are interested in a web application version of their old COM based application that delivers a personalized Hello message with a random quote. The application is to be developed using .NET technologies and has to be flexible enough to support various user interfaces.
I am in the process of researching setting this up as a template and sharing it. I am not there yet. So, here is a brief step-by-step on how I set up a project. This explanation goes through the ideal order of setting things up.
- Create an Empty Solution with the following naming convention <CompanyName>.<ProjectName>. In this case, the application is GreetingsInc.HelloWorld
- Create Solution folders in Visual Studio 2010
- Create the same folders in Windows Explorer – this is an optional step, but I find it easier to develop this way, as my logical folders correspond to a physical folder. To do this, I run the following script (makeDirectories.bat):
mkdir Business
mkdir Data
mkdir Domain
mkdir Framework
mkdir Test
mkdir UIcd Data
mkdir SqlServer - The next step is to create a domain model project: GreetingsInc.HelloWorld.Domain
After creating the project, I create my two domain models: HelloMessage (the full message) and Quote (quote returned from database):
- Add a project to the Data folder called GreetingsInc.HelloWorld.Data.Contracts
- Reference the Domain project
- Add a Generic Repository and then HelloMessage and Quote repositories:
We can go on here, but at this point, I am ready to start Test Driven Development of my business layer and allow the developers of the data layer to go off and start developing their work on an independent schedule. Here is how the interfaces appear:
Here are two tasks that can now be done in parallel:
- Data tier team begins setting up Repositories for data access, adhering to the interfaces
- Business tier team sets up mocks for the Repository to deliver a known answer and use them to create the business functionality
Summary
One of the most oft missed portions of development schedules is starting with decoupling as a first step. By focusing on the domain models first, then contracts, you force the project to a very loosely coupled state. In addition, you set up the work so the contracts (interfaces here) can be compiled and consumed in a “locked” state, reducing risk of the project not being able to integrate at a later date. This is an extremely valuable exercise.
Peace and Grace,
Greg
Twitter: @gbworld