More with Three-Tiered Design

Reuven shares more tips on the usefulness and limitations of three-tiered architecture—and just what is it?
Editing a Person

Now that we have seen how to add a new person to the database using our People object, let's try a slightly more difficult task: changing a person's first name, using the update_first_name method. (See Listing 3 and Listing 4 at ftp://ftp.linuxjournal.com/pub/lj/listings/issue82/ for examples.) We can only invoke this method once we have already selected an individual, which means that our editing form will have to let us do so.

While it might be tempting to let users select an entry by typing a name or e-mail address into a text field, this is prone to too many errors to be effective. Instead, we will allow users to choose from a <select> list. This removes the possibility that a user will enter an e-mail address (or another defining characteristic) for a person who might not be in our database.

We want to use a unique key to choose the person whose first name will be modified—but at the same time, it seems a bit impersonal to present a list of e-mail addresses. My solution was to go back to the People object (People.pm) and define a new method, get_names_and_addresses (see Listing 5 at ftp://ftp.linuxjournal.com/pub/lj/listings/issue82/). This returns a list of array references, where each array reference contains a name and an e-mail address. The former can be used as a unique key (and as the “value” within an <option> tag), while the latter can be used for display purposes. We can thus iterate over the e-mail addresses and produce a <select> list as follows:

<select name="email">
% # Iterate through the names and addresses,
  # printing them out
% foreach my $info (@names_and_addresses) {
  <option value ="<% $info->[1] %>"><% $info->[0] %>
% }
</select>

Allowing users to edit other user attributes would proceed in a similar way. Indeed, so long as you ensure that the user chooses a key that uniquely identifies the user, you can change any and all of its attributes using a similar type of form.

Adding an Appointment

Now that we have seen how we can use the People object to indirectly manipulate our People table in the database, we will start to look into our appointment book, handled by the Appointments object. This object allows us to add an appointment with a particular person on a particular day and time.

In order to accomplish this, we will (once again) need two components. The first component (add-appointment-form.html, in Listing 6 at ftp.linuxjournal.com/pub/lj/listings/issue82) produces an HTML form that allows users to enter a new appointment into the system, choosing a person from a predefined <select> list. (If this were an actual project, I would put the <select> list in a separate component, allowing other components to produce a menu of entries in the address book.) In addition, we have to know when the appointment starts, as well as when it ends. Once again, I prefer to have people select a date and time from <select> lists, since it removes the problems associated with time and date formats.

The following Mason code produces the three <select> lists that we need in order to have the user choose a month, day and year. By defining the @months and @years arrays in advance, we can make the code more readable, as well as update the system for future years quickly and easily:

<select name="begin_month">
% foreach my $month (@months) {
    <option value="<% $month %>"><% $month %>
% }
</select>
<select name="begin_day">
% foreach my $day (1 .. 31) {
    <option value="<% $day %>"><% $day %>
% }
</select>
,
<select name="begin_year">
% foreach my $year (@years) {
    <option value="<% $year %>"><% $year %>
% }
</select>

The second component, add-appointment.html (see Listing 7 at ftp.linuxjournal.com/pub/lj/listings/issue82), allows us to add a new entry into the appointment calendar. It checks (using an <%args> section) that we have submitted all of the required name-value fields from add-appointment-form.html. We then issue the same kinds of basic checks that our other components have done.

Are These Three Tiers?

Now that we have demonstrated how easy it is to create a three-tier web application, it's time to consider how many tiers we're really using. Does the term “three-tier” really apply here?

The term “three-tiered architecture” grew out of a dissatisfaction with another popular architecture known as “client/server”. For example, databases and web servers are both examples of modern client/server systems. Just as a client/server system typically refers to two physical computers, a three-tiered system refers to three physical computers, with each tier residing on a separate machine.

By contrast, the simple three-tiered application we have examined certainly had three layers in that there were distinct software systems that had clear goals and APIs and made it possible for the application and database layers to speak through a common middleware layer. At the same time, at least two of these layers (the web application and the middleware objects) were on the same computer without any real possibility for separation. If the web application were to become swamped with traffic, we could certainly add one or more identical Apache servers—but there is no way to put the application layer on one computer and the middleware objects on another.

So while I believe that we have now demonstrated some of the advantages of three tiers from the perspective of an application developer needing standard APIs, we have not seen a true implementation of such a system. In order to do so, however, we must have the ability to perform remote procedure calls (RPCs), such that a web application on one computer can invoke a subroutine or object method on another computer. This is possible and is getting increasingly easy with the growth of SOAP (Simple Object Access Protocol), but it brings with it a number of other problems and caveats, including the need to learn yet another transmission protocol.

While we're reconsidering how to define tiers—perhaps we should consider that the application developed does have three tiers, but that they are defined in a different way than we have considered until now. Instead of counting the tiers as (a) database, (b) middleware layer and (c) web server, perhaps we should count them as (a) database, (b) web server and (c) web browser? If we think in these terms, then we have indeed created a three-tier architecture—but come to think of it, so has anyone who has ever written a CGI program that talks to a database.

Moreover, we can introduce additional layers of abstraction into the mix here, complicating things further. What about stored procedures, triggers and views created on the relational database? Although not a physical tier, it can certainly make life easier for the person writing either an object layer or an application that accesses the database. Indeed, stored procedures are often better than an object middleware layer, because they execute on the database and are precompiled, making them relatively speedy.

We can also execute code on the web client (i.e., inside of the web browser) using JavaScript. While I generally encourage my clients to avoid JavaScript as much as possible, this buggy, insecure language riddled with cross-platform incompatibilities is the only way to execute programs from within a web browser, rather than on the server.

So, when we have a web application that uses a relational database, stored procedures, an object middleware layer, a web application layer and client-side JavaScript divided between three computers, how many tiers do we have? It's probably still three, but the fact is that it doesn't really matter what you call it. In the end, a decent design that takes into account your project's specifications, including the need for future growth, is the right way to go—regardless of how it jibes with the latest buzzwords and techniques.

______________________

Webcast
How to Build an Optimal Hadoop Cluster to Store and Maintain Unlimited Amounts of Data Using Microservers

Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.

Learn More

Sponsored by AMD

White Paper
Red Hat White Paper: Using an Open Source Framework to Catch the Bad Guy

Built-in forensics, incident response, and security with Red Hat Enterprise Linux 6

Every security policy provides guidance and requirements for ensuring adequate protection of information and data, as well as high-level technical and administrative security requirements for a system in a given environment. Traditionally, providing security for a system focuses on the confidentiality of the information on it. However, protecting the data integrity and system and data availability is just as important. For example, when processing United States intelligence information, there are three attributes that require protection: confidentiality, integrity, and availability.

Learn more about catching the bad guy in this free white paper.

Learn More

Sponsored by DLT Solutions