A web application is built upon a client-server architecture, a 3-tier architecture to be precise. As we’ve seen in the previous article, the code we write is separated into a client, and a server part. In this article, we will have a closer look at this separation.
More power to you!
A web browser is the key component in a web application’s 1st tier (the client). When the world-wide-web was still in its infancy, web browsers and client devices were not as powerful as they are today. They were only powerful enough to render simple HTML, and execute some simple Javascript. But as client devices become more and more powerful, the web browser follows. The structure of HTML documents become more complex with extensive CSS and Javascript.
Today, it’s not uncommon to see web applications with user interface as complex and beautiful as those of desktop applications. The capability of web browsers have evolved from simply rendering HTML documents, into performing application-like operations. We can use this to our advantage when we’re designing our web app's architecture. Not only in terms of user experience such as interactivity and ease-of-use, but also in the internal structure of the application. This could impact maintainability and scalability which is important to us developers. By tapping into the power of modern browsers and client devices, we could ease the server’s (and hopefully also developer’s) burden slightly, if not considerably.
Today, it’s not uncommon to see web applications with user interface as complex and beautiful as those of desktop applications. The capability of web browsers have evolved from simply rendering HTML documents, into performing application-like operations. We can use this to our advantage when we’re designing our web app's architecture. Not only in terms of user experience such as interactivity and ease-of-use, but also in the internal structure of the application. This could impact maintainability and scalability which is important to us developers. By tapping into the power of modern browsers and client devices, we could ease the server’s (and hopefully also developer’s) burden slightly, if not considerably.
Roads to Rome
If we build our web application the way we build websites, each HTTP request would result in a response containing a ready-to-render HTML document. It would be the server’s (2nd tier) responsibility to cook (generate) a well-done HTML document. All the browser has to do is serve (render) the HTML for the user to view.
However, there’s another way. We could leave the cooking part to the browser. All the server has to do is give the recipe (HTML view template & Javascript) and the ingredients (data) to the browser. It would be the browser’s responsibility to cook the ingredients based on the recipe (fills the template with data) and serve it to the user.
This way, the server does not deliver a blend of data and view (display format, layout, etc). Instead, it will deliver each of them independently through different request-response cycle. The view will then be incorporated by the browser to become a rich presentation tier that’s ready to request and display data on user’s behalf. Since we’ve separated the data and the view, we can create another view (e.g. native mobile / desktop app) to request the same data. The data can even be requested and consumed by another server (2nd tier), which is what a web service is all about.
However, there’s another way. We could leave the cooking part to the browser. All the server has to do is give the recipe (HTML view template & Javascript) and the ingredients (data) to the browser. It would be the browser’s responsibility to cook the ingredients based on the recipe (fills the template with data) and serve it to the user.
This way, the server does not deliver a blend of data and view (display format, layout, etc). Instead, it will deliver each of them independently through different request-response cycle. The view will then be incorporated by the browser to become a rich presentation tier that’s ready to request and display data on user’s behalf. Since we’ve separated the data and the view, we can create another view (e.g. native mobile / desktop app) to request the same data. The data can even be requested and consumed by another server (2nd tier), which is what a web service is all about.
Reading between the lines
If we decided to go the website way, we will be generating dynamic HTML quite extensively. This is not to say that we would otherwise not generating any dynamic HTML at all. It’s just that the extent of it would be far less if we decided to separate the view from the data. This is because, as explained above, we have moved the responsibility of data-filling (injecting data into the view) to the client.
Mind you though, even if we have separated the view from the data, the view template itself would sometimes needs to be dynamic. For example, if we have a view template that can fetch data from different service endpoints (e.g. URL) dynamically. It may depends on the interaction context of when / where we display the view. We would then need to dynamically put the service endpoint information into the view. But this is far less complicated than the task of blending view and data.
Mind you though, even if we have separated the view from the data, the view template itself would sometimes needs to be dynamic. For example, if we have a view template that can fetch data from different service endpoints (e.g. URL) dynamically. It may depends on the interaction context of when / where we display the view. We would then need to dynamically put the service endpoint information into the view. But this is far less complicated than the task of blending view and data.
Data in the Raw
Building a server that generates dynamic HTML is a task that’s familiar to those who have ever built a website with dynamic contents. We can accomplish this easily using server-side scripting languages such as PHP/JSP/ASP. But building a server that generates raw data might not sound familiar to some. First, we need to determine the format of data that we will exchange between the client and the server. Remember that the flow of data is bidirectional. The client may need to send some data along with the request it send to the server.
Common options for data format is XML and JSON. The former is more elaborate and flexible, while the latter is more concise and compatible. This is due to the fact that JSON is native to Javascript. Secondly, we must handle the HTTP request-response at the server at a lower level. This is because we need to do some things to the response such as attaching some data, setting the appropriate content-type, etc, instead of simply returning an HTML page. How to do this largely depends on the technology / language that we use for the 2nd tier. For example, if we’re using Java, we could use JSP to serve the view, and Servlet to serve the data.
Common options for data format is XML and JSON. The former is more elaborate and flexible, while the latter is more concise and compatible. This is due to the fact that JSON is native to Javascript. Secondly, we must handle the HTTP request-response at the server at a lower level. This is because we need to do some things to the response such as attaching some data, setting the appropriate content-type, etc, instead of simply returning an HTML page. How to do this largely depends on the technology / language that we use for the 2nd tier. For example, if we’re using Java, we could use JSP to serve the view, and Servlet to serve the data.
About Time
One more thing to note is about the synchronicity of the request-response cycle from the point of view of the client. If we go the website way, our request-response cycle will be synchronous. This means that these cycle happen in sequence after one another. Each response from a request will refresh the page, and only after this we can issue another request.
But if we decided to split the view and the data, our request-response cycle will mostly be asynchronous. This means that these cycles can happen independently one another. We can issue new requests even before we receive the response of the previous request. We will be informed (via callback function) when the responses arrive. Thus, a response does not necessarily refresh the page. We don’t want to display XML/JSON data from the response as-is, but instead inject that data into the view. We can achieve this using Javascript’s XmlHttpRequest which is the heart of AJAX / AJAJ.
But if we decided to split the view and the data, our request-response cycle will mostly be asynchronous. This means that these cycles can happen independently one another. We can issue new requests even before we receive the response of the previous request. We will be informed (via callback function) when the responses arrive. Thus, a response does not necessarily refresh the page. We don’t want to display XML/JSON data from the response as-is, but instead inject that data into the view. We can achieve this using Javascript’s XmlHttpRequest which is the heart of AJAX / AJAJ.
Jump on the Mobilewagon
Building a web application is more complicated than building a website. This is especially true if we decide to implement an architecture that separate the view from the data. We might end up with more source files, more Javascript, and more functions at the server-side. But this complexity does not come in vain, in fact, our code will be easier to maintain because it handles different concerns (view & data) separately.
Another advantage is, we can now create different types of clients without any change to the server part. For example, we can create a native mobile application to perform the same task as our web application. This will be relatively easy because we can reuse the server part, in which most of our code resides. Our native mobile application will only consists of minimal elements. Those are the UI (views), HTTP client (to request data from the server), and some event-handling and data-binding code. This is essentially what the client part of our web application contains.
Another advantage is, we can now create different types of clients without any change to the server part. For example, we can create a native mobile application to perform the same task as our web application. This will be relatively easy because we can reuse the server part, in which most of our code resides. Our native mobile application will only consists of minimal elements. Those are the UI (views), HTTP client (to request data from the server), and some event-handling and data-binding code. This is essentially what the client part of our web application contains.
No comments:
Post a Comment