With the advent of the .NET Framework and Visual Studio .NET (VS .NET), Microsoft Visual programmers now have a real motivation for developing multi-tiered applications. With VS .NET, developing in this fashion is much more practical and efficient. This is a best practice in object-oriented development - the logical separation of functional layers in a solution. However, with more power comes more complexity. Multi-tiered applications can quickly become unmanageable if they are not organized logically, consistently, and correctly. This is not only a problem for the original developer, but it also haunts maintenance personnel who must decipher how to piece a solution together and deploy it.
Nevertheless, Microsoft leaves organization and naming standards of various layers (or projects), namespaces, and the solutions that contains them very much up to the developer. Because of this lack of official direction, there is no "right-way" to organize solutions as developers scramble to find which works best in their situation or company. Moreover, any experienced Visual Studio .NET developer knows the problem is compounded when Web applications are involved.
Object-oriented solutions can become large, so proper organization of the code is crucial. In this article, I will explain one way to structure object-oriented n-tiered applications to be effective, simplistic, and highly maintainable. I would encourage you to investigate this and other namespace-organzation techniques and set a standard for your company or team. This will help insure that your applications are portable between developers, cut down on the confusion maintainers inevitably will face, allow for efficient storage in your code repository (such as Visual Source Safe), and will promote code reuse without having to hack the code or namespaces each time for your purposes.
Example Application
Because I am trying to demonstrate an effective solution naming scheme, we will create and work with a sample application. You can download the project files HERE. Our application is for a fictitious company named "FunCo." The FunCo public relations department has a need to send mass e-mails out to its subscribers to update them on various events and news. The example application we will create to fulfill this need is called the "AutoEmailer" system. The AutoEmailer application has 5 tiers: a physical database tier (not used in this article), a data access tier, a business logic tier, a Web presentation tier, and a Windows service that automates the mailing of e-mails. Each of the four code tiers will be organized in a VS .NET solution and will be written in C#. We can now use this fictitious application to explain how we organize and name the various layers / projects, namespaces, physical file structure, and solution.
Before creating any application, the first step is laying the foundation by selecting the application namespace.
Namespace Organization
The root of any application is the namespace. Namespaces are designed to organize code in a hierarchical fashion. They logically separate code into various "categories" similar to how Windows organizes files in the file explorer. This is an extremely effective method of organization. However, uncontrolled and impractical use of namespaces can result in lost and unmanageable code.
In almost all circumstances, the industry recommends your company name be the root element of your namespaces. This will clarify who the namespace belongs to and lends the assumption that it will be reasonably unique. It is very important for your company or development team to standardize on how you are going to use this basic namespace root. Many times, developers will use several namespaces or variations of one namespace for their company (remember that namespaces are case sensitive so "FunCo" is different from "Funco"). This will wreak havoc when you are trying to build a common code library or when a developer would like to use code from another project, requiring them to hack the code for each use.
At the second namespace level, Intellinet either uses the application name or the name of the department the application belongs to. This is dependant on how large and segregated the company is. If your company is small and has a flat organization structure or multiple departments own the application, your next level may be the application name. If your company is large and segregated or your application has a specific departmental owner, you may want to the second level of you namespace to be the department name.
The application name will usually be the next level followed by the logical segments of the application (business layer, data layer, etc.). This will be explained later in the article.
Intellinet has standardized on never allowing less than two and no more than three levels for the application root namespace. This will ensures we are descriptive in the namespace but do not overanalyze the company structure causing unnecessary complexity. However, depending on the size and organizational complexity, some large corporations may warrant the use of more than three namespace levels.
In FunCo's case, they have a fairly flat divisional organization and are too small of a company to segment the namespace by department. Therefore, we will simply use "FunCo.AutoEmailer" as our application root namespace. The various tiers are appended to this namespace as needed (such as "FunCo.AutoEmailer.BusinessTier").
Solution and Project Naming
Intellinet standardizes on naming compiled project output files the same as their namespaces. For instance, the AutoEmailer solution may have 4 project outputs (executables or DLLs). Each of these files is named the same as their project namespace (such as "FunCo.AutoEmailer.BusinessTier.dll"). While this may seem odd to have periods in your project and filenames, Microsoft has done this with most of their DLL libraries shipped with .NET (as evidenced by files included in .NET such as System.Windows.Forms.dll, System.Web.Services.dll, etc.). The purpose of this is to clarify who's the owner and what the purpose is of the file or code segment. By simply looking at the filename, we know that this dll belongs is the business logic of the AutoEmailer application.
Taking it one step further, this scheme also holds true for solution and project names. Intellinet will typically name the application solution the namespace of the application (such as "FunCo.AutoEmailer"). All tiers - or projects - contained in the solution will be children of that namespace. Therefore, all projects contained in the solution will have fully-qualified namespaces as their names. This will help with project identification, portability, and consistency.
In this case, we have four child projects - or tiers - in the FunCo.AutoEmailer solution.
Figure 1 - Four projects in the solution. Each project name is an extension of the solution namespace showing they belong to that application.
Additionally, projects outside of the solution namespace may be added in a consistent matter. Because the project is name the namespace, we know what the code set does and who it belongs to. Here, we add a project containing a common library of functions used by the rest of the application. The project has the same company root and is in a consistent naming scheme.
Figure 2 - The same solution with another added project as an example of code reuse. We can see by the names that there are four tiers to the FunCo.AutoEmailer application and one project not specific to the AutoEmailer application.
The naming of the projects and the purpose of the project is important for effective code reuse. A generic namespace will probably contain generic code. While a more specific namespace such as "FunCo.AutoEmailer.BusinessTier" will contain more specific code. Also, by naming a project descriptively with a namespace, you run less of a chance of name clashes when you reference or add another project to your solution. "FunCo.AutoEmailer.DataAccessTier" is much more specific and less likely to clash with a general project name like "DataAccess."
By naming the project the same as its namespace, all files created in that project will be created with that namespace by default. Also, all output from the project (DLLs) will have the namespace name so they will be easily recognizable. By default, any files contained in a folder you add to a project will be appended with the folder name in their namespace. For instance, if I add a child folder in the PresentationTier project called "Common", any files I add to the folder will have the default namespace "FunCo.AutoEmailer.PresentationTier.Common." This can allow further granularity in the file purpose without having to create a new project in the solution.
Solution File Structure Organization
Because the solutions and projects are named namespaces, and namespaces are hierarchical in nature, this provides an opportunity for a very elegant solution and project file structure scheme. Intellinet standardizes its physical file structure off the namespace elements. Therefore, by default, a single folder called "FunCo" will reside in the "Visual Studio Projects" folder. The FunCo folder will contain a folder called "AutoEmailer" which will contain the solution file (.sln) for the application. The AutoEmailer project will contain three folders called BusinessTier, DataAccessTier, and AutoSendService. Each of these folders will contain their respective C# project file (.csproj) and any supporting files / folders. The library project resides under the root folder but not in the AutoEmailer folder because it is not part of that namespace.
This scheme lends itself very well to a sound and intuitive file structure for applications. By enforcing this scheme, files and project are much easier to find and maintain. Consequently, storage in a code repository (such as Visual Source Safe) is simpler because only portions of the namespace may be checked out at a time.
Figure 3 - Files are located in relation to their place in the namespace. Solution and project files reside in the root folder of their corresponding namespace name.
Because the actual solution and project files reside in the root folder of their namespace, additional solutions may be created within the application for unit testing, etc. In this example, I have added a solution to the "FunCo\AutoEmailer\BusinessTier" folder that contains the already existing FunCo.AutoEmailer.BusinessTier project and a child project called "TestHarness." This solution only contains these projects (plus any referenced files / project outputs) to test the FunCo.AutoEmailer.BusinessTier project in isolation. The solution and TestHarness project follow the same naming standards as other projects.
Figure 4 - Isolated solution for unit testing.
Web applications and Web services follow the same folder structure as other types of projects, only they reside in an IIS-managed folder. When the IDE creates Web applications, it creates them through the Web server by default. Therefore, our presentation layer project files will be physically located at "C:\Inetpub\wwwroot\FunCo\AutoEmailer\PresentationTier" and logically located at "http://localhost/FunCo/AutoEmailer/PresentationTier." Of course, you can use any alias you like through IIS to create a more meaningful URL such as "http://localhost/Emailer." In the downloadable example, the .csproj project file is located in the IIS folder and not in the folder structure with the other projects. This can be done, however, since a Web .csproj file simply points to a URL to locate its member files.
Web services projects can use the same naming standards in their URI namespaces. The scheme follows all of the recommended practices for specifying namespaces for publication on the Internet.
Source Control Integration
A useful consequence of this naming scheme is its natural translation to your source control repository. By setting your solutions up hierarchically, the placement of file in source control becomes much more intuitive.
One problem common with .NET developers is the pains of setting up a new workstation with files from source control. Developers sometimes spend hours trying to get their files bound to the source control server while being able to open and close the solution correctly. This is compounded when a Web application is involved.
There is no silver bullet for remedying this problem because of the number of factors involved. However, by adopting this project structure throughout your development organization, you will get more predictable results when trying to perform this process. You should be able to document the steps to take when retrieving project files from source control to your workstation saving the frustration that is common.
You will also be surprised how clean your source control server will look using this hierarchical scheme.
Building the Example
The following are step-by-step instructions for creating a solution with this naming structure. This method can be adapted for use in your projects. I have also included a sample solution for download HERE.
Open Visual Studio .NET and select File > New > Blank Solution. In the Location box, make sure you are pointing to the "Visual Studio Projects" folder (or whatever your folder for projects is located). In the Name box, type "FunCo.AutoEmailer". Notice how the solution location hint is creating a folder called "FunCo.AutoEmailer". This will need to be changed. Click OK and close the IDE.
Open Windows Explorer and browse to the "Visual Studio Projects" folder. Rename the FunCo.AutoEmailer folder to "FunCo". Browse into your new FunCo folder and create a folder called "AutoEmailer". Select the FunCo.AutoEmailer.sln and the FunCo.AutoEmailer.suo files and drag them into the AutoEmailer folder.
Browse into the AutoEmailer and double click the FunCo.AutoEmailer.sln file to open the solution in the IDE. Click File > Add Project > New Project. Select the "Visual C# Projects" > Class Library project type. Name the project "DataAccessTier". Click OK.
Figure 5 - Creating a project called DataAccessTier. Notice the location hint places the project in a folder called "DataAccessTier."
If the Solution Explorer window is not showing, close the IDE and reopen the solution. Right-click on the DataTier project and select Rename. Rename the project "FunCo.AutoEmailer.DataAccessTier". Right-click on the project again and click Properties. Change the Assembly Name and Default Namespace values to "FunCo.AutoEmailer.DataAccessTier". Click OK.
Right-click on the solution in the Solution Manager and select Add > New Project. Select a Class Library project type and name the project "BusinessTier". Click OK.
Rename the project "FunCo.AutoEmailer.BusinessTier" and change the Assembly Name and Default Namespace to "FunCo.AutoEmailer.BusinessTier". Click OK.
Add a new "Windows Server" type project to the solution in the same manner. Name the project "AutoSendService" and make the necessary changes to "FunCo.AutoEmailer.AutoSendService".
Right-click on the solution and add a new "ASP.NET Web Application" type project to the solution. Change the location box value to "http://<localwebserver>/FunCo/AutoEmailer/PresentationTier" where "localwebserver" is your local server (most likely "localhost"). Click OK. Rename the project to "FunCo.AutoEmailer.PresentationTier" and change the Assembly Name and Default Namespace to "FunCo.AutoEmailer.PresentationTier".
Figure 6 - Creating an ASP.NET Web Application project called "PresentationTier."
Right-click on the solution in the Solution Manager and select Add > New Project. Select a Class Library project type and name the project "Utility". Change the location box value to "Visual Studio Projects\FunCo" since this project is outside of the "AutoEmailer" namespace. Click OK.
Rename the project "FunCo.Utility" and change the Assembly Name and Default Namespace to "FunCo.Utility". Click OK.
Figure 7 - The complete solution after all of the projects have been added.
Make any references to other assemblies or project and build the solution.
Figure 8 - The complete physical project structure after all of the projects have been added.
Conclusion
By adopting an effective solution structure and standardizing that structure throughout your development organization, countless hours can be saved from searching for files and code reuse will become accessible and more of a reality. By effective project and namespace naming standards, developers can rest assured that they will not need to rework projects to fit their namespace. By naming projects that same as their namespace, developers will also know what files belong to and what code they contain thereby reducing the need to search through code to find the appropriate member. This was a step that Microsoft has taken in their .NET framework to simplify navigation through their object model. This scheme can do the same for your organization.
About the Author
Solomon Shaffer is a Senior Systems Consultant with Intellinet Corporation, based in the Atlanta area. Intellinet, the southeast's only four-time Microsoft Gold Certified Partner, provides application-development and infrastructure consulting services throughout the southeast. Intellinet builds enterprise .NET systems using object-oriented development techniques and their custom process, a best-of-breed development methodology combining the leading agile techniques in the business.
Solomon resides in Alpharetta, Georgia, with his wife, Marcie. He has actively developed Microsoft-centric applications for over five years and has worked with .NET since Beta 1. Solomon can be reached at solomons@intellinet.com.
Collections are a vital element of any object-oriented architecture. This article, by Luther Stanton of Intellinet Corporation, introduces collections, looks at some of the benefits and potential drawbacks of using custom collections versus built-in collection-like elements, and then provides a discussion of interfaces and implementations. [Read This Article][Top]
Traditional relational database management systems (RDBMS) don’t lend themselves very well to object-oriented programming. Matt Culbreth of Intellinet Corporation introduces object databases and explains why they can be a valuable alternative to RDBMSs. [Read This Article][Top]
Mailing List Want to receive email when the next article is published? Just Click Here to sign up.