Patterns of Modern Web Applications – Fictional Case: usic.com – Server-side



Patterns of Modern Web Applications – Fictional Case: usic.com – Server-side

0 0


pomwa

Patterns of Modern Web Applications (slides)

On Github scato / pomwa

Patterns of Modern Web Applications

by Scato Eggen / @scataco

Oh hey, these are some notes. They'll be hidden in your presentation, but you can see them if you open the speaker notes window (hit 's' on your keyboard).

About Me

  • Grunge Music / Capoeira
  • Senior Developer @ Ibuildings
  • MSc in Physics
  • Education / History / Philosophy

"Modern Web Application"

What does that even mean?

  • Flashy
  • Good-looking
  • Fast
  • Responsive
  • Real-time 
  • Cheap & Maintainable

Research shows that if your page takes ... ms to load, half of your users will abandon your site.

Product owners always want to change something after the project is done. And often even before that happens.

Common Solutions

Lots of JavaScript!!!

Big Ball of Mud
AJAX all over the place

Big Ball of Mud

  • Couple of lines
  • Clean up later
  • End of project, one big file

AJAX

  • More responsive
  • Callbacks everywhere

Common Solutions

JavaScript MVC

Too much plumbing
Too many layers

Plumbing

  • More work
  • Event handlers, DOM manipulation
  • Server communication
  • Part of the database in memory

Layers

  • Templates, Views/Controllers, Models & AJAX
  • Web API, More models
  • Database
  • Add one field...

Every solution leads to more problems

The SolutionTo The Problems With The Solution

A combination of both
Design patterns
A plan of attack

Combination

  • non-MVC & MVC
  • Get better at them

Design Patterns

  • Discuss design

Plan

  • Strategy we agree on
  • Work as a team

Fictional Case: usic.com

Find reviews and discover new music

Sociological study on musical taste

"music board"

Modern: popovers...

... and modals

Why not MVC?

Why JavaScript MVC harder than Web MVC?

What is MVC?

Organizing Presentation Logic

Martin Fowler, EAA Dev

Server-side

Server-side

Server-side

Client-side

Client-side

Client-side

Client/Server

2 problems → 3 problems

Client/Server

non-MVC MVC View + Controller Model CSS + JavaScript AJAX View + Controller Controller ModelDatabase

Combine non-MVC with MVC

Non-MVC: easier to maintain, progressive enhancement, no re-render

MVC: harder to maintain, Separated Presentation, complex behavior

Organizing Your Front-end

What do we agree on?

What are best practices?

Semantic HTML

6 rules

#1: No style attributes

Bad:

<div style="color: red;">This is not a valid email address</div>

Good:

<div class="error">This is not a valid email address</div>

#2: No script attributes

Bad:

<a href="#" onclick="openModal('/contact')">Contact</a>

Good:

<a href="javascript:;" class="contact-link">Contact</a>

#3: No inline style or script tags

Bad:

<style>
    .error {
        color: red;
    }
</style>

<script>
    function openModal(url) {
        // TODO
    }
</script>

Good:

<link href="css/register.css" rel="stylesheet" type="text/css">

<script src="js/menu.js"></script>

#4: Use semantic class names

Bad:

<div class="red">This is not a valid email address</div>

Good:

<div class="error">This is not a valid email address</div>

The thing you style

Not the way you style it

#5: Use semantic tags

Bad:

<div class="lead">This is not a <i>disclaimer</i>.</div>

Good:

<div class="lead">This is not a <em>disclaimer</em>.</div>

#6: Follow the structure of your content

Bad:

<div class="menu">
    <span class="menu-item"><a href="/">Home</a></span>
    <span class="menu-item"><a href="/register">Register</a></span>
</div>

Good:

<ul class="menu">
    <li><a href="/">Home</a></li>
    <li><a href="/register">Register</a></li>
</ul>

The Module Pattern

(function () {
    var local = 0;

    $('.counter').click(function () {
        local += 1;
    });
}());

Douglas Crockford, JavaScript: The Good Parts

"Don't leave home without it!"

The Module Pattern (return)

var myNamespace = (function () {
    var local = 0;

    $('.counter').click(function () {
        local += 1;
    });

    return {
        count: function () {
            return local;
        }
    };
}());

The Module Pattern (exports)

var myNamespace = {};

(function (exports) {
    var local = 0;

    $('.counter').click(function () {
        local += 1;
    });

    exports.count = function () {
        return local;
    };
}(myNamespace));

Asynchronous Module Definition (AMD)

Used by RequireJS

define(['jquery'], function ($) {
    var local = 0;

    $('.counter').click(function () {
        local += 1;
    });

    return {
        count: function () {
            return local;
        }
    };
});

CommonJS Module Definition

Used by Browserify and Node.js

var $ = require('jquery');

var local = 0;

$('.counter').click(function () {
    local += 1;
});

exports.count = function () {
    return local;
};

CSS Preprocessors

.menu li {
    border-radius: 4px;
    background-color: #753A26;
}

#account-register input[type="submit"] {
    border-radius: 4px;
    background-color: #753A26;
}

CSS Preprocessors

.menu li,
#account-register input[type="submit"] {
    border-radius: 4px;
    background-color: #753A26;
}

CSS Preprocessors

@mixin primary-button() {
    border-radius: 4px;
    background-color: #753A26;
}

.menu li {
    @include primary-button();
}

#account-register input[type="submit"] {
    @include primary-button();
}

CSS Preprocessors

vs

Sass is better: conditionals and loops

Blog post on CSS Tricks by Chris Coyier

CSS Preprocessors

Just try it!

There's not excuse not to use a CSS Preprocessor

Asset Pipeline

  • Concatenate (CSS/JS)
  • Minify/compress (CSS/JS)
  • Compile (Sass/Less)
  • Optimize (RequireJS/Browserify)
  • Versioning/cache buster

Grunt

Page Modules

Modules... but what to isolate?

Alternatives to AJAX

The Page Module Pattern

views/account/register.template:

<div id="account-register">
    <form>
        <!-- ... -->
    </form>
</div>

The Page Module Pattern

css/account/register.css:

#account-register form {
    /* ... */
}

The Page Module Pattern

js/account/register.js:

$('#account-register').each(function () {
    // ...
});

The Page Module Pattern

It's about isolation

The Page Module Pattern

Use common stylesheets for:

  • Page layout
  • Header styling
  • Paragraph spacing
  • Anything in the "common" section of a style guide

 

Note: good style guides can be hard to come by

Presentation Patterns

Add custom behavior

Widget

Widget

css/shared/menu.css:

.shared-menu a[href] {
    /* ... */
}

js/shared/menu.js:

$('.shared-menu').each(function () {
    // ...
});

Validation Rule

$.validator.addMethod('strong-password', function (value, element) {
    // ...
});
<input type="password" strong-password="true">

Data Patterns

Add extra screen state without roundtrips to the server

Hidden Input

<div class="shared-avatar">
    <input type="hidden" name="avatarId" value="3">
    <!-- ... -->
</div>

Data Attribute

<span class="shared-progress" data-value="1" data-max="10"></span>

Data Literal

<script>
    var genreList = [
        {"id": 1, "name": "Post-Grunge"},
        {"id": 2, "name": "Post-Hardcore"}
    ];
</script>

Trade-off: inline js and global var!

Content Security Policy header :(

Integration Patterns

Update screen state with data from the server

HTML Fragment

$('.shared-menu').each(function () {
    var $menu = $(this);

    $menu.find('.contact-link').click(function () {
        $.get('/shared/contact', function (data) {
            $('#modal .modal-dialog').html(data);
            $('#modal').modal('show');
        });
    });
});

No client-side templates

No Web APIs

AJAX Form

$('.shared-login').each(function () {
    var $login = $(this);

    $login.find('form').submit(function () {
        $.post('/shared/login', $(this).serialize(), function (data) {
            $login.html(data);
        });
    });
});

POST

Some "technical difficulties"

  • How to close a modal...
  • How to reinitialize the login widget...

JavaScript MVC

We know what to do with non-MVC

When to use JavaScript MVC?

How to prevent lasagna code?

Do I Need One?

Page Modules

  • Isolated components
  • Extra behavior
  • No session state

JavaScript MVC

  • Interacting components
  • Complex behavior
  • Client-side session state

Isolated components

Extra behavior

No session state

JavaScript MVC

JavaScript MVC

  • Interacting components (add item to board)
  • Complex behavior (drag, rotate)
  • Client-side session state (save session state to server)

Lasagna Code

Code that is divided in several well-defined and separable layers, but where individual layers in the system are highly unstructured or disorganized.

Joe Celko

Lasagna Code

  • Low cohesion within layers
  • Hard to find stuff
  • Tight coupling between layers
  • Every change touches multiple layers

Client-side Model

DTO Presentation Model View Model Application Model

Different from the server-side model

Don't expose your entities!

REST is not the answer

Level 3: Hypermedia ControlsLevel 2: HTTP VerbsLevel 1: ResourcesLevel 0: RPC

REST Level 1.5

Hypermedia without PUT and DELETE

{
    "_links": {
        "self": { "href": "/user/music-board" }
    },
    ...
}

Hyperlinks work best with "networky" data, like users, bands, other users

One Bundle, One Asset

bundle.js

shared/menu.js

account/board.js

Bundled into one asset, next to Widgets and Page Modules

No way to include half a bundle

Don't mix in Widgets and Page Modules

Conclusion

To summarize

Polyglot Architecture

Page Modules JavaScript MVC Web MVC Web API

Complexity where you need it

Side by side without problems

Further Reading

EAA Dev, very interesting

EIP, if you're getting into Web Sockets

Good Parts, if you haven't already

The End

by Scato Eggen / @scataco

Questions?

All images licensed under Creative Commons, by: Maurice Vanderfeesten, Rennette Stowe, Rajeev3065, ちいた, CEphoto, Uwe Aranas, Eric Hadley-Ives, Jason McDonald & National Guard