Layers: No Tiers Shed

January 24, 2009

Not all of us have the luxury of going beyond a 2-Tier system for web development (web server + database server). In this world, there can be a tendancy to forget best practices – so let me drop a little advice: Code like you’re going at least 3-Tier and respect the layers.

I run across this all the time. A junior developer will create a web page (.aspx) and just start coding in the code-behind page (.aspx.cs or .aspx.vb). It starts of all good and nice: they’re setting up the look and feel, turning on and off panels, handling callbacks and postbacks, etc. But then I run across some piece of code that is obviously business logic which makes a call to some code that is without a doubt for data access.

Now, you can go ahead an create some extra folders in your project and call them “DataAccessLayer” and “BusinessLogicLayer” (or whatever your orginazation wants to name them). You can hope that everything you are doing is respecting the boundaries… or you can step-it-up a level and actually create new projects.

So here are the 5 projects I always have for any solution: Web (UI), Services (WCF, SOAP, etc.), Business (Entities for some),  DataAccess (SQL) and Core (Internal, Shared).

Web / UI
This is just for your web pages, flash files, javascript, images, etc. Anything that falls outside of the user experience shouldn’t be placed here. The best way to know is this: pretend that you are building 2 apps that have to do the same thing – one uses a website as a front-end and the other is a windows app making service calls.

Services / WCF
A lot of people avoid creating this layer when they are starting off and they go straight for the web site. This is going to cause you way more headaches in the long run. When you come back to refactor, you may realize that you were passing dataviews or datasets across. I love datasets, but we live in a world where expectations are such that clients expect to do more with their server based apps. Of course, I work on enterprise applications, so my view may be skewed.  

Try to plumb (and test) your services layer so it can work across both HTTP(S) and named pipes or another local service. (Why take the overhead of HTTP if you don’t need it?) The service layer should have minimal logic in it, but should also try and handle enough items in a single call so that the traffic going back and forth is minimal.

Business / Entities
Entities may be the wrong way to phrase it, but for some companies, that’s the right terminology. I work in a total TDD OOP shop, so this layer should be made up of mostly objects and factories. Remember that the Services, Business, and Data Access layers are all on the same Tier, so feel free to take advantage of Interfaces  (ISomeInterface) to leverage logic in your business layer when you might be in your data access layer (a common item might be IUser.GetGroups()). Also keep in mind that SQL Commands should not be executed on this layer. In fact, you could try and make it a goal that System.Data is not referenced at all by this layer.

Data Access
Please, please, please do not return DataReaders, DataViews, DataSets, or SqlCommands from this layer. If you need help trying to understand the seperation you are trying to achieve, look into Inversion of Control (IoC) concepts and Mocking (with a framework like RhinoMocks during testing) so you can begin to get a feel of what you should try and return. Stick to Data Transfer Objects, Interfaces, and scalars (bool, string, int, etc) for your return types and you should be a lot better off when you decide to support SQL, MySql and Oracle all on the same code base.

Core / Shared / Internal
I tend to split Internal from Shared. Shared is what ships with my API, while Internal sticks on the  server. IUser might make it into Shared, but the enumeration DatabaseType might stay in internal. I always err on the side of caution and stick everything in Internal and only transfer items over to Shared on an as-needed basis.

Leave a Reply