Lately, I have been tasked with taking not one, but TWO applications (for separate clients) that have been extensively developed over the past 20-odd years, and updating them to meet the modern needs of their users. Both of these applications have been built and specifically tailored for their user’s needs over the past two decades, however they have grown into absolute beasts; monolithic solutions that have reached their limits in terms of scalability and maintainability (not to mention outdated, both projects utilize technologies that have been around as long or longer than I have). To their owners’ dismay, the projects are both in need of new features to maintain and grow their user bases. Software rewrite time? Absolutely.
For both projects, the clients have decided that it is time to completely rebuild, utilizing modern technologies that will be maintainable for the next decades to come (both are going the .NET route). Both projects, having supported dozens of users, are now requiring the ability to support hundreds, if not more users, and have the ability to support traditional desktop/laptop applications, as well as web and mobile applications. “Great!” I said. “Time to do some project planning! We should plan on utilizing a tiered architecture.” Glossed over eyes followed. “What is that?”
What Does N-Tier Really Mean?
Let’s start by saying that the answer to this question varies from person-to-person. To me, N-Tier in general is a combination of two different concepts:
- The application(s) run on multiple pieces of hardware – From a physical perspective, a tiered application runs on more than one computer. A client may run part of the application on their computer, which communicates with a server sitting somewhere to access data that the server retrieves from a database somewhere else. In this case, there would be 2+ tiers, depending on the number of devices involved with the application.
- The application(s) responsibilities are split into separate layers – The application’s source code itself is divided in such a way that code is modular, or “layered”, similar to the way a network stack is divided. Generally, there is a Data Access Layer (DAL), Business Logic Layer (BLL), and Presentation Layer (PL), though these layers are sometimes subdivided into even smaller, discrete chunks of specialized logic, and may use Data Transfer Objects (DTOs) to communicate with each other for Inter-Process Communication (IPC).
When combined, these two concepts add up to my own one-liner definition, something along the lines of “a layered software architecture that is capable of running on one or more devices”. Simple enough, right?
So what is so great about an N-Tier architecture?
Consider the scenario my two clients are currently in: They have both developed highly specialized and sophisticated applications that have grown exponentially over years, which do just about everything their clients need. The applications are very fast, and work well, but have a steep learning curve for the uninitiated. The learning curve applies to both end users as well as the developer(s) themselves. Their two tier architectures work well when only a few users are running the applications, but are a nightmare to operate if more than a couple people run the application at the same time, and are riddled with issues such as data integrity, security, and concurrency. Using the software in more complex situations (like over a VPN), only add to the problems.
- Pros: Software is extremely fast for anyone who knows exactly how to use it. We’ll refer to this as “speed”.
- Cons: Only a few people can use it at the same time, data and security is hard to maintain
Now, if these applications were redesigned to use a N-tiered architecture, many of the data integrity, security, and concurrency issues could be easily resolved by splitting up the application’s code into multiple layers that can communicate with each other, and splitting up the application itself so it could run on multiple devices at the same time. This would allow more users to work with more data in a more secure way, at the expense of sacrificing some application speed.
- Pros: Software becomes much more scalable and robust, allowing for more users and more execution environments, with heightened data integrity and security. We’ll call this “strength”.
- Cons: Because the application is layered and split across multiple devices, latency is introduced, which can slow down the application as a whole
But I want my application to be fast and secure!
Well, I want to retire right now without working another minute. Not going to happen. Remember the old proverb “You can’t have your cake and eat it too“? I can’t think of a better way to describe this all too common statement that I hear. This is a huge issue with every client I have ever worked with, and there is no clear-cut answer to this with respect to the application as a whole. As the developer, it is my job to help the client decide which of these they want (speed or strength) within different parts of their application. If they want more speed, they need to understand that they are sacrificing strength; conversely, where strength is needed, speed is going to decrease. In sports terms, for the less tech-savvy: A heavyweight boxer is never going to win a 100M dash against professional runners, and those same runners aren’t going to make it to round 2 in the ring with the boxer.
When trying to answer this question, you need to make sure that the client’s needs are being met. If they are on the phone with a customer working on a sale, time is of the essence, so you need to make sure the software is fast enough to meet the demand, However, if complex analytics are being run against historical data for the past year to forecast the next quarter’s numbers, the client will likely be happy to wait a little longer to make sure the numbers are as accurate as possible. Every application is going to have features that fall into one category or the other. If the client believes that every last feature of their application only falls into one of the categories (specifically speed), then an N-tier architecture is not for them. Recommend they revert back to a DOS-based system and move on with your career. If they are more worried about supporting hundreds of users spanning multiple geographic locations from various devices in real time, then they’re in luck! N-tier design is the way to go.
Preparing For an N-Tier Architecture
You’ve decided that an N-Tier architecture is an appropriate way to rebuild your applications, excellent! Now what? Step 1: Plan, plan, plan!!! The absolute worst thing you can do for your project is to decide to go tiered, and then jump straight into coding without putting any thought into what the end goals of the application are. Break out a pencil and some graph paper, and start drawing some pictures. Does your application need to run on multiple servers? Are there plans for a desktop application, mobile apps, websites? Before you do anything else, figure out how the clients plan on using the application. This is going to define how many tiers you need to plan out building for. If the application is only going to be run as a desktop application accessing a single server which houses the data, then you can easily plan on using a simplified 2-tier architecture, with 2-3 layers of software abstraction. If the client needs the same setup along with a website that customers can use. you may need an extra layer of software to handle the website. Throwing in some mobile apps? Same thing. Got a second server that handles generating reports? 3+ tiers, maybe more.
Use that chunk of wood (or plastic, though I prefer the feel of a quality Ticonderoga #2 in my hand) and graphite to draw pictures to represent the different machines involved, and how the devices will interact. You’ll likely end up with a spider web shaped graph. Once you figure out how many tiers you’ll need, you can start defining the software’s architecture itself, and start drawing pictures to show how the software will need to be layered. This picture is most likely going to look like an onion, with the data access layer(s) in the center, the business logic layers in the middle, and the presentation layers on the outside. If your layered picture doesn’t look as described, you either have an unusual way of looking at the world, or a poorly designed architecture. To enforce the onion concept, think about this: if you handed your client an onion and told them to look at it, the only thing they would ever see would be the outside of the onion, the presentation layer (unless they take a knife to it, only a hacker would do that though). They should never see the business logic or data access layers.
Once you have figured out what tiers are needed and what software layers are required, you will need to decide what layers will run on which tiers. At this point, you need do, in a sense, combine your two crude drawings into a single drawing that will show everything in one picture. This single picture will become your software’s road map, and should be able to relay everything about how the software needs to be built and inter-operate. This picture, once complete, should be recreated on the computer for readability. Microsoft Visio is a great tool for this purpose, but something as simple as Paint or The GIMP will do the trick, so long as the image is clear and concise.
Pick Your Technologies
Before you start writing code for your amazing new software platform, be very thorough in evaluating the technologies you plan on using. While there are many “pre-canned” solutions that handle the majority of N-Tier application development, not all of them are going to be quite right for your specific use cases. Make sure that, if you use an existing solution, that you:
- Make sure the solution will address all of the hardware and software requirements for your application. The solution should be able to operate on all required hardware, and allow all software layers to communicate effectively.
- Be sure to weigh the solutions speed versus strength. If parts of your application need to be lightning fast while others can be slow but require integrity, make sure the solution you are evaluating is flexible enough to fulfill these needs.
- Make sure you the solution is flexible enough to work around or modify existing behavior. The last thing you want to do is put a year or two’s worth of work into your application, only to find that the solution you evaluated can’t do XYZ, and there is no way to work around or fix the issue without an enormous amount of effort or getting the solution’s developers involved.
- Prefer multiple solutions that perform smaller, specific tasks over single solutions that “can do it all”. While you may end up with more code dependencies and APIs that you have to work with, it is better to have small, specifically targeted third-party libraries that excel at performing the tasks they do, than to have a single solution that does everything you need it to do in a mediocre way. This may be a painful thing to do at the beginning of a project, but in five years when you decide that your third-party data access layer library is now slow by comparison to newer solutions, you can swap out that specific section of your project, without rewriting your entire project. Modularity is king.
Building Your Platform
You have decided on using an N-Tier architecture, meticulously planned out your platform, and picked your technologies. After long hours of plotting, you are finally ready to start writing some software! But where to start?
- Build from the inside out: Start with your Data Access Layer(s). If you don’t have a solid underlying data structure, no amount of presentation layer customization is going to make your software fast and easy to use. Build all of the POCO data structures first, and then worry about defining the Business Logic Layer(s) and DTOs to manipulate the data stored in your POCOs. Once your DAL is structurally sound and your BLL is functional, put your focus on the Presentation Layer to build the User Experience that your client expects to see, tying it to your BLL. By the time the client is satisfied with the PL, your BLL will have been vetted, and your DAL perfected.
- Keep your layers as decoupled as possible: Your software layers should be as separate from each other as possible, and should never skip over layers. Anywhere that layers are linked together, they should be done so using interfaces (code contracts), which strictly control how the links are made.
- Keep your layers as linear as possible: The links between your layers should be as concrete as the law of gravity. The Presentation Layer should never have direct access to the Data Access Layer (ideally, it should not even have knowledge of its existence) without going through the Business Logic Layer.
- Validate your data: Before your data is ever presented to a user or written to a database, be sure that it is first validated somewhere in your architecture. Without data validation, there is no data integrity. Depending on the specific use cases of the data, the validation can be done at the PL (i.e. user input validation), the BLL (i.e. calculation validation), or the DAL (i.e. data type/length/value checks). In most cases, if you perform validation before data is written to the database, it can be assumed to be valid when read, so validation does not always have to be bidirectional.
- Be fault-tolerant: If your data fails validation for any reason, does not bubble up from the DAL to the PL, of the PL locks up, be sure your architecture is graceful enough to catch and log the error, and gracefully recover if possible. User’s like to see corrupt data about as much as they like to see the Blue Screen of Death.
- Make sure your platform is scalable: When building your platform, always assume that your customer is going to ask for more features, and be sure to write your platform in such a way that adding these new features is a painless process. Upgrades, feature additions, and bug fixes are inevitable, so plan accordingly.
- Test!!! As with any other software project, testing is a requirement. Unit testing is very helpful when building tiered applications. be sure to write unit tests that will exercise each layer individually, as well as tests that exercise interoperability between directly linked layers (DAL<–>BLL and BLL<–>PL), as well as indirectly-linked layers (DAL<–>PL). Also, make sure your tests account for interoperability between your different layers and their tiers.
By this point, you have probably had a few cups of coffee, done more reading than you planned to, and have become overwhelmed with the amount of thought and effort involved with building an N-Tier platform. Now take a deep breath, and remember: once you have built your first POCO for your DAL, your first BLL object to manipulate the DAL using DTOs over IPC, and first PL screen to display the data and accept user input to pass back through the layers, the rest is easy, since you will already have a full understanding of the overall process of building a tiered platform. Your project will have a slow start, but by this point, development speed is going to rapidly increase, and you will quickly see your massively scalable, flexible, and integral platform come to fruition. Just remember to follow your road map drawing. Now, go forth and create!
Like this article? Feed the developer, every dollar counts: