Published: 19 Oct 2009
By: Andrew Siemer

In this article we will start to address some of the flaws in our original design. The first step will be to refactor towards logical separation of our code. Then we can analyze the code to see what the remaining issues are. Then we will analyze the pros and cons of this design.

Contents [hide]

The Stack Overflow Inspired Knowledge Exchange Series

  • TOC Checkout the project homepage of this series to follow our journey from the creation of the famous StackOverFlow website.
  • Introduction

    In the last article we refactored our simple project from a UI direct to data access style web site into a logically separated tiered application. This took form by creating a separate data access layer area in our code as well as a separate business layer area in our code. We then moved our connection logic into its own class for reuse across the site. Next we created a repository to take care of all the querying for a given type of object. Once the data access was isolated we then created a fairly straight through business layer which can be easily modified in the future. This gave us a point to insert new functionality if the need were to arise. With the reworking of our plumbing code we then cleaned up our front end by plugging into our new business layer. We ended the article with a look at the pros and cons of this approach and found that there were significant improvements gained with just this one refactoring. There is still plenty of room for improvement in this code.

    In this article we will take the next step in improving this code by further elevating our logical tiers to physical tiers. This simply means that we will create separate assemblies for each of the separate tiers. By the end of this article we will have our web project, a business layer project, domain project, and a data access project. As we will see shortly this will alleviate some of the pains that we observed in our last article. But it should serve to surface a few other dependencies that we have not really had a good view of yet.

    Physical separation of the code

    To get started this time around we will need to create three new class library projects. One project will be called Core and will hold our business logic including service classes. The other project will be called DataAccess and will hold our Connection and Repository classes. And the last one is going to be our Domain project which will hold our domain objects.

    Right click on our current solution and add a new project. Then choose to add a Class Library project. Name it KnowledgeExchange.Core and place it in our src directory.

    Figure 1: Add new project for core

    Add new project for core

    Then add another Class Library project. This one will be named KnowledgeExchange.DataAccess and also placed in the src directory. And finally add the last class library for our Domain project.

    Once you have the projects created you will need to modify the properties for each project. You need to make sure that the assembly name and namespace reflect exactly what you want them to be. I name my projects assembly and namespace the same:

    KnowledgeExchange DOT something?

    Very sorry! I just noticed that I named our web project KnowledgeExchangeWeb without the dot. This bothers me! For that reason we will take a quick moment to "refactor" our solution file, project directories, and project name to fit with all the other stuff we are doing. Before we get started doing operations such as this make sure that you commit all your changes and that your code base is 100% green across the board. Next, make sure that you commit after each and every modification you make as your code repository can quickly get so far out of sync that you will have a heck of a time getting things merged back in. Also, make sure that you perform all modifications through your SVN client tools. I am using Tortoise and will rename my folders first using Tortoise's rename feature.

    When you hit ok you will see a scary window that says it is deleting all of your files in this folder! Don't worry, it created a new folder with the new name and copied the versioned contents into that folder, and then tagged the other folder for deletion on SVN's side.

    Make sure that you rename both the KnowledgeExchangeWeb and KnowledgeExchangeWeb.Tests directories.

    Then we will rename the project file names. Use Tortoise to do this too. When renaming the project file to KnowledgeExchange.Web and KnowledgeExchange.Web.Tests you will see this prompt to rename similar files.

    Click yes and it will rename your surrounding files with the same name.

    Now we can rename the guts of the solution file. Do this by opening your solution in your favorite text editor. There are two lines that have modifications. And there is three modifications per line. You need to rename the project name, the project's directory name, and the project file name. Here is what they should now look like:

    Once you have finished this process you should then be able to reopen your solution file and see the new names reflected.

    Migrating our existing code

    Now that we have all of our projects created and uniform we can move our existing code to their new homes. We will move the connection and repository in our new data access project. And we will move our service into our new core project. If you are using visual studio and ankhsvn then you can quite literally just drag the class files to their appropriate project. If you are only using Tortoise then you are better off creating a new class file in the appropriate project and copy the contents from file A to file B. Then delete file A. Once you have moved those files we will then look to relocate our data context which generates our domain objects. We can move the KE.dbml to the Domain project.. Then you can delete the Models, Core, and DataAccess folder from your web project.

    Now that everything is relocated we need to modify our namespaces. You will notice that all of our namespaces in the core and data access projects mention Web. The namespace should directly reflect the name of the project and folder structure for each file. For a namespace that looks like this:

    We will need to change all of the namespaces so that they look like this instead:

    Now we need to add references from one project to the next. If we have done things correctly then the web project should only need to know about our business layer. Our business layer should know about our data access layer. Then the web project, core, and data access should all be aware of our domain objects. Add references to the web project by right clicking on the project and selecting add reference. Click the projects tab and select the core and domain projects. Then do the same for the core project and add a reference to the domain and data access projects. And for the data access project add a reference to the domain project.

    Next we need to look through our code and update all the dependencies (and remove references to namespaces that no longer exist). To start we need to see if anything will compile as is. I am starting by working on the Domain project that has our KE.dbml file. We need to make sure that it has access to its settings file. Do this by opening up the design surface for the .dbml file. If it complains about not being able to locate the settings file update the project appropriately (by adding a settings file and adjusting the reference). Then you should be able to build the Domain project which all of the other projects are referencing.

    Then we can look at the data access project. This project needs to be able to use the ConfigurationManager so we need to add a reference to the project for System.Configuration. Do this by right clicking on the project, add references, then select the .net tab, and then scroll down to find System.Configuration. While in there we will also need to add a reference to System.Data.Linq for our LINQ to SQL data context reference. Lastly we will need to add a using statement to the top of our repository and connection file to be able to access the AndrewSiemer.KnowledgeExchange.Domain namespace where our Post object now lives. Build this project to make sure that everything is working in our data access project.

    Now we can update our core project. The first thing we need to do is add some using statements to the top of our PostService class. This class needs to know about the PostRepository which lives in AndrewSiemer.KnowledgeExchange.DataAccess and it also needs to know about the Post object which lives in AndrewSiemer.KnowledgeExchange.Domain. Once that is done you should be able to build the core project.

    All that is left is to update our HomeController so that it knows where the PostService now lives. Do this by adding a reference to AndrewSiemer.KnowledgeExchange.Core. Then we need to open up our Index view to update its usage of the Post object. You should now be able to build your entire solution and then browse out to see if the web page still works. Also, make sure that your local build process still works. Do this by first deleting your KnowledgeExchange database and the login for the KnowledgeExchange_dev user. Then run the build process and initialize the local database. Then run the build process to make sure that everything is still good.

    Analysis

    Not a lot has changed from a code perspective. The code is technically the same from our last article. But we have reorganized it in a manner that makes it considerably more flexible. Having said that though I wanted to introduce this form of physical tiers as this is the form that most people build their projects thinking that everything is perfect, flexible, and most importantly scalable. This is a farce though as you will see in the dependency graph.

    In the past iterations we have done a pretty good job removing or at least relaxing dependencies that don't belong in our application. We still have one major dependency in place with regards to our data access layer. Since our domain objects are generated directly by our data access layer, LINQ to SQL in our case, we are pretty well tied to LINQ to SQL. This isn't horrible in the general since of things but it does break some of our rules such as abstraction, an a large chunk of the SOLID principles! Technically speaking a developer working on this application could instantiate an instance of KEDataContext and perform ad-hoc queries at their leisure. With this tight coupling we could never easily swap out our data source.

    Pros

    We now have all of our concerns separate out into their own projects which is certainly a step in the right direction. It is now easier to limit the exposure of one tiers functionality to the next. There is now no way to make a call from the presentation layer to the data access layer. Also, having moved our domain into its own project sort of reduces the dependency between our core and data access layers which we had in our previous implementations. This is not perfect yet.

    Cons

    While we have limited exposure from one layer to the next, technically the UI still has some access to the data access layer. And while our application is flowing through the appropriate channels to perform its various requests, there is nothing stopping a rouge developer from connecting to the data base and getting at the data that they need for a piece of functionality that they are working on. Also, our ORM preferences are too well known across all of the tiers which has created a very inflexible dependency on a specific technology. And our code is still not very testable!

    Table 1: Comparison Chart

    Coding Concepts

    Yes

    Sorta

    No

    Fast/Easy to develop: Can we generate the end product quickly?

    X

    Testable: Can we write tests around the majority of the projects code?

    X

    Flexible for refactoring: Can we easily refactor the code base to add new concepts?

    X

    Well abstracted: Do the users of your code only know what they need too?

    X

    Well encapsulated: Can you change the internals of code without impacting the users of that code?

    X

    Separation of concerns? Is your code well compartmentalized and only doing what it needs to?

    X

    DRY? Does your code follow the "Don't repeat yourself motto?"

    X

    SOLID? Does this code comply with the SOLID principles?

    X

    S: Single Responsibility Principle - there should never be more than one reason for a class to change

    X

    O: Open Closed Principle - should be open for extension but closed for modification

    X

    L: Liskov Substitution Principle - functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it

    X

    I: Interface Segregation Principle - clients should not be forced to depend upon interfaces that they do not use

    X

    D: Dependency Inversion Principle - high level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions.

    X

    Swappable? Can you swap out an entire layer down the road?

    X

    Distributable? Can you push complex processing of logical areas to a separate server easily to offload computing cycles and scale?

    X

    Summary

    In this article we took the next step in improving this code by further elevating our logical tiers to physical tiers. This simply means that we created separate assemblies for each of the separate tiers. By the end of this article we had a web project, a business layer project, a domain project, and a data access project. We saw that this new structure alleviated some of the pains that we observed in our last article by strongly separating what each tier had access to. But it also surfaced our heavy dependency on our ORM choice.

    In our next article we will focus on less of the structural issues and instead work on removing our dependency on LINQ to SQL. We will achieve this by employing an object to object mapping tool called AutoMapper. We will create plain old CLR object (POCO for short) that we can then map our LINQ to SQL objects too. We will then refactor our application to be dependent upon our internal POCO objects instead of the LINQ to SQL generated objects. This will effectively allow our ORM choice to be independent of our code base which will allow us to swap LINQ to SQL out for Entity Framework 4.0 when it comes out next year!

    <<  Previous Article Continue reading and see our next or previous articles Next Article >>

    About Andrew Siemer

    I am a 33 year old, ex-Army Ranger, father of 6, geeky software engineer that loves to code, teach, and write. In my spare time (ha!) I like playing with my 6 kids, horses, and various other animals.

    This author has published 29 articles on DotNetSlackers. View other articles or the complete profile here.

    Other articles in this category


    Code First Approach using Entity Framework 4.1, Inversion of Control, Unity Framework, Repository and Unit of Work Patterns, and MVC3 Razor View
    A detailed introduction about the code first approach using Entity Framework 4.1, Inversion of Contr...
    Exception Handling and .Net (A practical approach)
    Error Handling has always been crucial for an application in a number of ways. It may affect the exe...
    jQuery Mobile ListView
    In this article, we're going to look at what JQuery Mobile uses to represent lists, and how capable ...
    Book Review: SignalR: Real-time Application Development
    A book review of SignalR by Simone.
    JQuery Mobile Widgets Overview
    An overview of widgets in jQuery Mobile.
    Top
     
     
     

    Please login to rate or to leave a comment.