Hey, I'm writing a book too :) Click the link below to preorder and save ;)
»» Get the Only Reference You'll "ever" Need on JavaScript Engineering Interviews, Now! ««
March 4th in Code Organization Guidelines by .

How to Refactor a Mess into an Organized Web Application (Part 2: the Server Side)

Chaos Converging Into Order

Chaos Converging Into Order

Overview

In the former post of our series, we started to refactor a simple vcard application, which initially was a total mess.

In the first part, we defined a skeleton folder structure for our application; and we laid out the foundations of the BPC architecture.

Where;

  • Behavior/business logic (B) would go to the Behavior Tier;
  • Presentation/rendering (P) would go to the Presentation Tier;
  • And events/signals/callbacks (C) would go to the Communication Tier.

In this post, we will continue our discussion by transforming our application.

BPC versus MVC

There are both similarities and differences between the BPC Architecture, and the MVC paradigm:

  • The Behavior Tier (B) has similar functionality to the Model in MVC;
  • The Presentation Tier (P) has similar functionality to the View in MVC;
  • And the Communication Tier (C) has similar functionality to the Controller in MVC.

There are a handful of client-side and server-side MVC and rapid-development frameworks such as CakePHP, Prado, Symfony on the server side; and backbone, sproutcore, knockout, ember, batman on the client-side.

Though, in this article series, we will be looking at how a really simple application can step-by-step evolve into its own framework.

It will be more educational; and as we dive deep in, we will understand why those frameworks do what they do the way they do ;)

I’ve been using and re-aligning the BPC Architecture in various production environments, for the last few years. And day by day pieces of the puzzle seem to fit better. I see that the concept has matured enough to be put into words, and that’s what I’m trying to do here :) . Though it’s still a work in progress, and I’d love to hear your feedback.

Enough chitchat; time’s candy. So let’s get our hands dirty. Shall we?

Application Constants

Let’s start by defining constants that we will be using in the server-side:

<?php
<?php
    namespace o2js\vcardapp\config\constants;

    // /o2.js/examples/vcardapp/include/config/constants.php

    class PageTitle {
        const ABSENT = 'o2js.com - VCard Demo';
        const INDEX  = 'o2js.com - Welcome to VCard Demo';
        const VCARD  = 'o2js.com - VCard Demo - Profile of "{0}"';
    };

    class PageEnum {
        const ABSENT = 0;
        const INDEX  = 1;
        const VCARD  = 2;
    };

    class ServiceKey {
        const USER_NAME = 'u';
    };

    class Path {
        const VCARD_DATA = '/o2.js/examples/vcardapp/include/persistence/data/{0}/vcard.html';
    };

    class RegExp {
        const TEMPLATE = '/({\d+})/i';
    };
?>
?>

The ServiceKey::USER_NAME is for extending our application to support showing vcard information for more than one person. We will be creating an API that will query VCard information of registered users.

Now let us create an …/volkan/vcard.html to simulate a server-side storage.

This data folder is for demonstration purposes. It will simulate the server-side persistence layer.
Normally, an RDBMS, or a NoSQL datastore (such as MongoDB) should be a better fit for the persistence tier.

VCard Template

Here’s the content of the vcard:

<!-- /o2.js/examples/vcardapp/include/persistence/data/volkan/vcard.html -->
<h1>Volkan Özçelik</h1>

<dl>
    <dt>Web</dt>
    <dd><a href="http://o2js.com">o2js.com</a></dd>

    <dt>Email</dt>
    <dd><a href="mailto:volkan@o2js.com">volkan@o2js.com</a></dd>

    <dt>twitter</dt>
    <dd><a href="http://twitter.com/linkibol">@linkibol</a></dd>

    <dt>LinkedIn</dt>
    <dd><a href="http://linkedin.com/in/volkanozcelik"
        >linkedin.com/in/volkanozcelik</a></dd>
</dl>

<p class="clear"><a href="../close" class="close" title="close"
    ><span>back to home</span></a></p>

Index Page

Now let us change index.php a little:

<?php
    namespace o2js\vcardapp\view\index;

    require("include/common/common.php");

    require("include/init/index.php");

    require("include/presentation/header/common.php");
    require("include/presentation/body/index.php");
    require("include/presentation/footer/common.php");
?>

Where

  • include/common/common.php has common includes shared between views,
  • include/init/index.php initializes static constants for the current view,
  • include/presentation/header/common.php and include/presentation/footer/common.php simply render the page header and footer.
  • And include/presentation/body/index.php is responsible for rendering the body.

And let’s observe these file one by one:

include/common

<?php
    namespace o2js\vcardapp\common;

    // include/common/common.php

    require("include/config/constants.php");
    require("include/persistence/state/State.php");
?>

State Class

State.php defines a class that holds the page state.

<?php
    namespace o2js\vcardapp\state;

    // include/persistence/state/State.php

    use o2js\vcardapp\config\constants\PageEnum;
    use o2js\vcardapp\config\constants\PageTitle;

    class State {
        public static $currentPageTitle = PageTitle::ABSENT;
        public static $currentPageEnum  = PageEnum::ABSENT;
    };
?>

The class simply defines a default set of state data.
These static State members are then initialized at include/init/index.php:

init/index

<?php
    namespace o2js\vcardapp\init;

    // include/init/index.php

    use \o2js\vcardapp\state\State;
    use \o2js\vcardapp\config\constants\PageTitle;
    use \o2js\vcardapp\config\constants\PageEnum;

    State::$currentPageTitle = PageTitle::INDEX;
    State::$currentPageEnum  = PageEnum::INDEX;
?>

And here are the header, body, and footer parts of the application:

presentation/header/common

<?php
    namespace o2js\vcardapp\presentation\header;

    // include/presentation/header/common.php

    use \o2js\vcardapp\state\State;

    $pageTitle = State::$currentPageTitle;
?>
<!DOCTYPE HTML>
<html>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta http-equiv="Content-Style-Type" content="text/css" />
    <title><?php echo  $pageTitle; ?></title>
    <link rel="Stylesheet" type="text/css" href="/vcardapp/css/main.css" />
<head>
</head>
<body>

presentation/footer/common

<?php
    namespace o2js\vcardapp\presentation\footer;

    // include/presentation/footer/common.php
?>
</body>
</html>

presentation/body/index

<?php
    namespace o2js\vcardapp\presentation\body;

    // include/presentation/body/index.php
?>
<div id="VCardContainer">
    <p id="VCardActivator"><a href="people/volkan"
        class="super button action" id="vcard-volkan">Volkan Özçelik</a></p>
    <p id="VCardContent"></p>
</div>

You can see what we’ve done so far at this github history snapshot.

Everything Put Together

After all this setup, our initial page will look like this:

VCard Button

VCard Button

And the rendered HTML will be:

<!DOCTYPE HTML>
<html>
    <!-- http://localhost/o2.js/examples/vcardapp/ -->
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta http-equiv="Content-Style-Type" content="text/css" />
    <title>o2js.com - Welcome to VCard Demo</title>
    <link rel="Stylesheet" type="text/css" href="/vcardapp/css/main.css" />
<head>
</head>
<body>
<div id="VCardContainer">
    <p id="VCardActivator"><a href="people/volkan"
        class="super button action" id="vcard-volkan">Volkan Özçelik</a></p>
    <p id="VCardContent"></p>
</div></body>
</html>

But when clicking the button we will get an error:

URL not found

URL not found

This error is because we have not defined a page for the people/volkan link. We’ll work it out by defining required presentation and business components (into includes/presentation and include/business folders respectively).

Let’s start doing it by defining simple rewrite rules first:

URL Rewrite Rules

Options +FollowSymLinks
RewriteEngine on

RewriteRule ^include/?.*$ - [F,L] # disallow access to sensitive folders.

RewriteRule ^index.php$ http://localhost/o2.js/examples/vcardapp [R]
RewriteRule ^close$     http://localhost/o2.js/examples/vcardapp [R]

RewriteRule ^people/(.*)$ vcard.php?u=$1

These rules will map:

  • …/examples/vcardapp/close to http://…/examples/vcardapp
  • and …/examples/vcardapp/people/volkan to http://…/vcard.php?u=volkan.

We can do further URL-rewrite/SEO optimization, but that’s good enough for now :) .

VCard Page

But our vcard.php is currently missing. So let’s create it:

<?php
    namespace o2js\vcardapp\view\vcard;

    require("include/common/common.php");

    require("include/business/manager/VCardManager.php");

    require("include/init/vcard.php");

    require("include/presentation/header/common.php");
    require("include/presentation/body/vcard.php");
    require("include/presentation/footer/common.php");
?>

vcard.php is quite similar to the index.php above; the only things that differ are the VCardManager.php and include/presentation/body/vcard.php.

Let’s see them one by one:

VCardManager Class

<?php
    namespace o2js\vcardapp\business\manager;

    // includes/business/manager/VCardManager.php

    use \o2js\vcardapp\config\constants\Path;
    use \o2js\vcardapp\config\constants\RegExp;

    class VCardManager {
        public static function getVCardHtml($userName) {
            return file_get_contents(
                    $_SERVER['DOCUMENT_ROOT'
                ].preg_replace_callback(
                    RegExp::TEMPLATE,
                    function($match) use ($userName) {
                        return $userName;
                    },
                    Path::VCARD_DATA
                )
            );
        }
    };
?>

VCardManager::getVCardHtml($userName) simply gets the vcard html by finding and reading the relevant file include/persistence/data folder.

presentation/body/vcard

And here’s include/presentation/body/vcard.php:

<?php
    namespace o2js\vcardapp\presentation\body;

    // include/presentation/body/vcard.php

    use o2js\vcardapp\config\constants\ServiceKey;
    use o2js\vcardapp\business\manager\VCardManager;

    $user_name = $_GET[ServiceKey::USER_NAME];
?>
<div id="VCardContainer">
<?php
    echo VCardManager::getVCardHtml($user_name);
?>
</div>

What we have done so far is reachable at this github history snapshot.

the Rendered VCard

After all these, clicking the vcard button will display the following vcard:

vcard

vcard

With the following HTML:

<!DOCTYPE HTML>
<html>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta http-equiv="Content-Style-Type" content="text/css" />
    <title>o2js.com - VCard Demo - Profile of "{0}"</title>
    <link rel="Stylesheet" type="text/css" href="/vcardapp/css/main.css" />
<head>
</head>
<body>
<div id="VCardContainer">
<h1>Volkan Özçelik</h1>

<dl>
    <dt>Web</dt>
    <dd><a href="http://o2js.com">o2js.com</a></dd>

    <dt>Email</dt>
    <dd><a href="mailto:volkan@o2js.com">volkan@o2js.com</a></dd>

    <dt>twitter</dt>
    <dd><a href="http://twitter.com/linkibol">@linkibol</a></dd>

    <dt>LinkedIn</dt>
    <dd><a href="http://linkedin.com/in/volkanozcelik"
        >linkedin.com/in/volkanozcelik</a></dd>
</dl>

<p class="clear"><a href="../close" class="close" title="close"
    ><span>back to home</span></a></p>
</div></body>
</html>

… and that concludes our discussion.

Hey, Wait! Did I Miss Something?! There’s no JavaScript in Here!

While the initial application was using JavaScript to dynamically display the vCard information, this application does not have a single line of JavaScript. That’s totally on purpose.

The former application was displaying vcard info dynamically without requiring a page refresh. That was, of course, a nice feature to have. However, it was totally unusable if the user agent did not have JavaScript enabled. Moreover, the vcard information will not be crawlable by search engines, since it was generated via JavaScript.

Progressively Enhance

So instead, in this article, we implemented a base set of functionality that does not depend on JavaScript. We will be enhancing this functionality with JavaScript in the upcoming articles. That’s called progressive enhancement, and it’s an always-to-keep-in-mind best practice.

Wait for the follow up article, where we will be adding some JavaScript kung-fu into the mix ;) .

And until then, feel free to share your comments ;) .

It's me: Volkan! Jack of all Trades, Samurai of JavaScript ;) Since 2003, I’ve been doing front-end development on client-heavy AJAX web applications. Currently, I'm a JavaScript Hacker at "SocialWire". Before that I was a VP of Technology at "GROU.PS", a well-known do-it-yourself social networking platform that allows people to come together and form interactive communities around a shared interest or affiliation. Before that, I was a JavaScript Engineer at "LiveGO", a globally known social mash-up. Before that, I was the CTO of a business network ("cember.net") which was acquired by Xing AG for around 4.2M Euros. See my linkedin profile to find more about me; I also share worth-following bits an pieces on twitter.

VIsit Volkan Özçelik's website

2 Comments

Leave a Reply





o2.js _
Fork Ribbon