Backend-less Development Revisited
Tomasz Ducin
Tomasz Ducin
10th December 2016, Wrocław
Backend-less Development Revisited
Tomasz Ducin
11th October 2016, Kraków
Backend-less Development Revisited
Tomasz Ducin
15th September 2016, Kraków
Backend-less Development Revisited
Tomasz Ducin
15th July 2016, London
Backend-less Development Revisited
FullStack
FullStack
Backend-less Development Revisited
conference on JavaScript, Node & Internet of Things
conference on JavaScript, Node & Internet of Things
Tomasz Ducin
15th July 2016, London
Tomasz Ducin
16th June 2016, Darmstadt
Backend-less Development Revisited
Tomasz Ducin
13th May 2016, Kraków
Backend-less development revisited
geecon
Tomasz Ducin
11th April 2016, Warsaw
Backend-less development revisited
Backend-less development revisited
Tomasz Ducin
25th February 2016, the WEB
Backend-less development revisited
Backend-less development
in AngularJS
Tomasz Ducin
22nd February 2016, Warsaw
#6
Backend-less development
in AngularJS
Tomasz Ducin
JavaScript, Java, Python
senior software consultant @ Cybercom Poland
trainer @ Bottega IT Solutions
blah, blah, blah...



Agenda
- single page apps
- what is BLD, why & what for?
- how does it work?
- BLD in AngularJS & Sinon.JS
- automation
traditional



server-side web apps
AJAX




Single-Page Application
SPA
REST API




client-side web apps
Single Page Apps
flawless solution






solution architect
tight coupling

FE
BE

What is more important?



let's modify JSON response!
but why?
because the new structure fits better
so what is...

?
backend-less development
server-less architecture
server-less architecture
~90%
actual development
FE-BE
sync
~10%
:
Frontend development
with Backend-less approach
Technical Perspective
-
BE changes force FE changes
-
BE needs to be implemented prior to FE
-
backends being unavailable
interface testing


depends on the API


- private customer banking interface
- 1+ year to go on production
- deployed on 120+ Scandinavian banks
BLD experience: Retail Banking




why would I need BLD
if I've got backend anyway
Business Perspective
-
budget-driven development
-
big, distributed teams
-
backends being unavailable
mock your APIs!


MOCKS
MOCKS
MOCKS
DATABASE
BLD approach
-
delivering data: different levels
- mocking backend behavior
- different for GET, POST, PUT, DELETE
- delayed responses (timeout)
- request parameters, random responses
- generating data:
- JSON Schema
- Contract-first Design
JSON Schema
{
"title": "Example Schema",
"type": "object",
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"age": {
"description": "Age in years",
"type": "integer",
"minimum": 0
}
},
"additionalProperties": false,
"required": ["firstName", "lastName"]
}
{
"firstName": "John",
"lastName": "Lennon",
"age": 40
}
{
"firstName": "Paul",
"lastName": "McCartney"
}
node.js-based
fake data generator



stub server




API Blueprint
Swagger
RAML:
RESTful API Modelling Language
Contract-first Design

- stored in repository
- available in CI
- used by many
automates
- URLs
- HTTP methods
- HTTP status codes
- HTTP content-types
- HTTP headers
- content itself
/songs
get
post
/{songId}
get
/file-content
get
post
/artists
get
post
/{artistId}
get
/albums
get
/albums
get
post
/{albumId}
get
/songs
get
/songs:
description: Collection of available songs in Jukebox
get:
description: Get a list of songs based on the song title.
queryParameters:
songTitle:
description: "The title of the song to search (...)"
required: true
minLength: 3
type: string
example: "Get L"
[...]
API Contracting
Example
[...]
responses:
200:
body:
application/json:
example: |
"songs": [
{
"songId": "550e8400-e29b-41d4-a716-446655440000",
"songTitle": "Get Lucky"
},
{
"songId": "550e8400-e29b-41d4-a716-446655440111",
"songTitle": "Loose yourself to dance"
},
{
"songId": "550e8400-e29b-41d4-a716-446655440222",
"songTitle": "Gio sorgio by Moroder"
}
]


Sinon.JS code & demo

XMLHttpRequest
var fs = sinon.fakeServer.create();
fs.respondWith(HTTP_METHOD, URL,
[HTTP_STATUS, HEADERS, DATA]
);
sinon.fakeServer
Fake HTTP backend implementation
var value = [{"name": "John", "age": 68 }, ...];
var fakeServerWrapper = {
init: function() {
this.fs = sinon.fakeServer.create();
this.fs.respondWith("GET", 'customers/',
[200, { "Content-Type": "application/json" },
JSON.stringify(value) ]);
this.fs.autoRespond = true;
},
restore: function() {
this.fs.restore();
}
};
// fakeServerWrapper.init();
AngularJS code & demo



when(method, url, [data], [headers])
.respond(HTTP_STATUS, DATA, HEADERS);
whenGET(url, [headers]);
whenHEAD(url, [headers]);
whenDELETE(url, [headers]);
whenPOST(url, [data], [headers]);
whenPUT(url, [data], [headers]);
...
$httpBackend
Fake HTTP backend implementation
when(method, url, [data], [headers])
.respond(function(...){
return [HTTP_STATUS, DATA, HEADERS];
});
whenGET(url, [headers]);
whenHEAD(url, [headers]);
whenDELETE(url, [headers]);
whenPOST(url, [data], [headers]);
whenPUT(url, [data], [headers]);
...
var demoApp = angular.module('demoApp', ['ngResource']);
demoApp.controller('fetchCtrl', function ($scope, $resource) {
var Events = $resource('/events/:eventId', {
eventId: '@id'
});
$scope.fetch = function () {
Events.query({'eventId': 6}, function (data) {
// do something with data
});
};
});
// ...
// mock part
var data = [{
"name": "FullStack",
"edition": 2016
}, ...];
demoApp.config(function ($provide) {
$provide.decorator('$httpBackend',
angular.mock.e2e.$httpBackendDecorator);
});
demoApp.run(function ($httpBackend) {
$httpBackend.whenGET('/events/6').respond(
200, data, {header: 'one'}
);
});
mock automation
...
<script type="text/javascript" src="app.js"></script>
<script type="text/javascript" src="mock.js"></script>
...
mock.js
bundle
mock.js
bundle
1. mock data
2. mock logic
[{
"firstName": "Hans",
"lastName": "Schmidt",
"age": 47,
"city": "Berlin"
}, {
"firstName": "Karl",
"lastName": "Fischer",
"age": 26,
"city": "Hamburg"
}, {
"firstName": "Peter",
"lastName": "Mueller",
"age": 71,
"city": "Leipzig"
}, ...]
3. dependencies
var mockData = require('data.json');
function applyGETcollection(){ ...
return mockData;
}
function applyGETitem(id){ ...
return mockData[id];
}
function applyPOST(item){ ...
mockData[item.id] = item;
}
function applyPUT(item){ ...
mockData.push(item);
}
function applyDELETE(id){ ...
delete mockData[id];
}
-
$httpBackend for AngularJS
-
fakeServer for sinon.js
-
etc.
mock.js

Abstract
Syntax
Tree
var modB = require('b');
var modC = require('c');
module.exports = ...
module.exports = ...
a.js
var modD = require('d');
module.exports = ...
b.js
c.js
module.exports = ...
d.js
a.js
b.js
c.js
d.js
bundle.js


a.js
b.js
c.js
d.js
bundle.js
native
require
node.js function
bundle working
in the browser
a.js
b.js
c.js
d.js
loose files
...
<script type="text/javascript" src="app.js"></script>
<script type="text/javascript" src="mock.js"></script>
...
mock data
mock logic
dependencies
mock.js
-
e2e tests (integration)
-
production
grunt serve [--mock], gulp serve [--mock]
-
development
-
e2e tests (UI only)
mocks
no mocks
THX!


independent clients make
everybody benefit