The two-tier architecture is the most predominate architecture in corporate America. Although the Internet is slowly changing this model, do not be fooled into thinking an Internet application is not also a two-tier architecture—it depends on how it is written. Simply put, a two-tier architecture is one where the application runs on the user's machine and the data is stored in a central location on a network (refer to Figure 1-1).
Figure 1-1: A two-tier application architecture
By its very nature, a two-tier application is not scalable beyond a certain point. There are several reasons for this. One reason is the number of connections a database can maintain concurrently. Imagine that one million users try to access the database at the same time…need I say more? There is no way to effectively manage the connections to the database when the connections are being created on the user's machine (as opposed to being able to pool database connections). Another reason why a two-tier application is not scalable beyond a certain point is application functionality in relationship to the business process that the application supports. Take a situation where a business process changes and the program has to be altered. The company may have to roll the upgraded application out to 30,000 users, which is usually too cost prohibitive to do. Scalability does not have to just reflect whether an application can support a growing number of users but also how expensive it is to support them.
Then there is the concurrency issue; that is, what happens when two or more users try to access the same record in the same database at the same time to make changes to it? Usually one or more users are blocked from making changes, which can cause the application to temporarily hang. In a two-tier application, this can be both a positive and a negative aspect of the application. The positive aspect is that one user cannot alter a record that another user is modifying. The negative aspect is that it can cause the second user's query to wait if there is a lock on the record they want to read. If the application is programmed correctly, the lock should not last for more than a few milliseconds, but on some database platforms, if the user who placed the lock is prematurely disconnected from the database, the result is a lock that cannot be removed except by the database administrator or by the database after a certain period of time. This has the potential to cause numerous problems. This particular issue is never a problem with a three-tier application, but other, more complicated issues appear with regard to this aspect of the database (see the sidebar "Database Concurrency Issues").
When multiple users try to update a single record at the same time, you will have concurrency issues. In a single-tier application, this is never an issue because there is only one user accessing data. In a two-tier system, you as the developer have the option of implementing either pessimistic or optimistic locking—however, this depends on the Relational Database Management System (RDBMS) because not all databases support optimistic locking. In most two-tier applications, you are always connected to the database when you are reading and writing data. If you set up pessimistic concurrency, when one user is trying to update a record, no other user can update the record at the same time (they will receive an error message explaining that the record is locked). If you implement optimistic concurrency, you will have to write code to handle the occurrence of one user updating a record that is not current. In a three-tier application, the developer must always handle database updates because no connection is maintained with the database. In most cases, this applies to a well-written two-tier application as well.
Microsoft's new database access technology, ADO.NET, can help to make many of these issues easier to solve—but even ADO.NET will only throw an exception saying that someone else has updated the record. It is still up to the developer to handle this situation, and it is rarely solved the same way on any two applications. How you handle this situation depends mostly on what the users want.
So, with all of these issues, when is a two-tier architecture a good solution? Usually it is when there are only going to be a small number of users who will ever use the application. When I say small number, I mean about 100 or fewer users. Another time to use two-tier architecture is when other applications will not need to access the functionality provided by the two-tier application. Take for instance an application that performs some function that is only needed in this one instance—you probably will not need to worry about incorporating this functionality into other applications. Because the functionality does not need to be reused, there is no point in creating a reusable component.
So, what is the major drawback to a two-tier system? Every time I have written a two-tier application for a small number of users, someone has come up to me and said, "That is a great program, can we use it also?" And from there it snowballs. All of a sudden, this little application I wrote for five users is suddenly being used by 15 people, and then 40 people, and then so on and so forth. Eventually someone comes to me with some serious performance problems of the application. My typical answer comes across as something like, "No, you're kidding?" At a certain point, sarcasm became a way of life for me….
However, there are things you, as a developer, can do to mitigate this risk. There is a right way and a wrong way to write a two-tier application, and typically—you guessed it—the developer chooses the wrong way. The wrong way takes less forethought when designing the application, which means the developer can show results almost immediately. In the long term, though, development will slow because it is done on a "think up things as you go" approach. The wrong way also causes an immense amount of work to be re-done when upgrading the application from two to three tiers. And this happens more often than developers would like to believe. Figure 1-1 showed a two-tier application that is not scalable beyond a certain point. However, Figure 1-2 shows a better way to build a two-tier application.
Figure 1-2: Correct architecture for a two-tier application
The right way to build a two-tier application is to treat it as a three-tier application and just install all the components on the user's local machine. This approach will cause development to initially be slower because more thought needs to go into the application in the initial stages. But, after you develop this initial strategy, the coding will go much faster and smoother, and many of the surprises that would normally catch you along the way are handled before they become issues. By building the application this way, when performance problems start cropping up, it is a small step to move the business logic and data access code off of the local machine and onto an application server. Problem solved. The only issue you will really have to deal with is where to put the business rules. (We talk a little more about object modeling and business rules in Chapter 3, "Creating the Application Infrastructure.")