This post came out of a team design discussion last summer. We were trying to decide how to layer and deploy a new tool for administering part of our system. In order to work through the options, I took a step back and thought about design in the abstract. It helped me a great deal at the time. I hope it helps you, too.
Logical Application Layers
It makes good sense to design applications in logical layers. In most modern business applications, there are (at least) three layers:
- A UI layer to display information to the user and handle user input;
- A “middle-tier” or business layer to handle data validation and process business rules; and,
- A data access layer to handle storage and retrieval of data from some form of repository.
This allows you to keep all of your UI code together in one place, separate from the business rules and data access code (increasing cohesion). And, it makes it easy for you to ensure that the UI code never calls the data access code directly (reducing coupling). These are the hallmarks of good OO design.
Physical Application Tiers
It may also make sense to deploy your logical layers across multiple physical environments (or tiers). For example, you could:
- Deploy all three layers to a single physical environment (though not generally recommended), or
- Deploy the UI layer to a separate environment in a DMZ, or
- Deploy all three layers to separate physical tiers.
This allows you to improve security (through the use of firewalls and service accounts). And, it allows you to improve performance (through the use of load balancers at each tier). These are the hallmarks of a well deployed application. Notice though, that to do this, you need to design logical layers into your application well before you try to deploy it.
Packaging Logical Layers for Deployment to Physical Tiers
Packaging is the process of placing classes into DLLs. In other words, how do I decide where to put a new class when creating it? Obviously, if I plan to deploy the application across multiple physical environments, then it makes sense to create multiple packages (or DLLs). But, if I decide to host two (or more) layers on the same physical tier, I may still want to package those layers separately, and deploy both DLLs. So, packaging decisions can be influenced by deployment decisions, but not the other way around.
Furthermore, how you package your logical layers (in one, two or three DLLs) and where you deploy your logical layers (on one, two or three physical environments) has nothing to do with the logical layers themselves. In other words, no matter how I package and deploy my application, it will still contain the same classes and methods in each logical layer.
If you choose to deploy your logical layers on a single physical environment, each layer can call its subordinate layers directly (regardless of how many DLLs you create). But, if you choose to deploy your logical layers across multiple physical environments, then you’re going to need some glue to allow the layers to communicate across those environments. A common approach to this issue is to introduce a thin service layer between two logical layers to handle the communications, like this:
- Front-end Tier
- UI Layer
- Service Layer Proxy
- Back-end Tier
- Service Layer
- Business Layer
- Data Access Layer
Notice that the service layer in this case actually spans the two physical environments. A portion of the service layer, called the “proxy” lives on the front-end. And, the remainder of the service layer lives on the back-end. Microsoft does a good job of hiding all this from you in WCF. But, the underlying proxy and service layers are still there.
The important thing to remember about service layers is that they should only handle the communications across physical environments. There should be no UI, business or data access logic in the service layer or its proxy. Again, this goes to increasing cohesion and reducing coupling.