Upgrade to Pro — share decks privately, control downloads, hide ads and more …

AngularJS and MicroServices

AngularJS and MicroServices

Billie Thompson

July 02, 2015
Tweet

More Decks by Billie Thompson

Other Decks in Technology

Transcript

  1. Code/Demo Server
    book-jacket-api
    book-info-website
    Book Jacket
    API
    Rating API
    Book Info
    Website
    Browser
    ratings-api
    {server}.herokuapp.com
    https://github.com/PurpleBooth/{server}

    View Slide

  2. Angular JS &
    Microservices
    Welcome to the world of
    tomorrow!

    View Slide

  3. Goals
    • Working knowledge
    • Client side JavaScript frameworks
    • Know how to integrate them with existing services
    • Become more of a Cross Stack developer

    View Slide

  4. Who’re you?
    Billie Thompson
    @PurpleBooth
    [email protected]
    PHP Contractor

    View Slide

  5. Imagination Time!
    http://www.businesscat.happyjar.com/comic/coffee/

    View Slide

  6. Book Information Service
    Book Jacket
    API
    Rating API
    Book Info
    Website
    Browser

    View Slide

  7. Book Information Service
    DB
    Queues
    Logic
    Responsive
    Extendible
    Clean
    Parallel
    Serve JS
    Frontend-y
    Book Jacket
    API
    Rating API
    Book Info
    Website
    Browser

    View Slide

  8. Book Information Service
    Silex
    PHP
    AngularJS
    JavaScript
    Express
    JavaScript
    Book Jacket
    API
    Rating API
    Book Info
    Website
    Browser

    View Slide

  9. Code/Demo Server
    book-jacket-api
    book-info-website
    Book Jacket
    API
    Rating API
    Book Info
    Website
    Browser
    ratings-api
    {server}.herokuapp.com
    https://github.com/PurpleBooth/{server}

    View Slide

  10. MVVM
    Model, View and View Model

    View Slide

  11. Book Information Service
    Book Jacket
    API
    Rating API
    Book Info
    Website
    Browser

    View Slide

  12. MVVM Framework
    View
    View
    Model
    Model
    Remote
    API
    Browser Server

    View Slide

  13. View Slide

  14. –AngularJS Demo
    “It looked so easy when I saw someone else’s
    talk”

    View Slide

  15. //- book-info-website/views/index.jade
    doctype html

    html(lang="en" ng-app="booksApp")

    head

    title Book Info Website

    link(rel='stylesheet', href='/stylesheets/style.css')

    script(src='/assets/modules/angular/angular.js')

    script(src='/assets/modules/angular-resource/angular-resource.js')

    script(src='/assets/modules/angular-route/angular-route.js')

    script(src='/javascripts/angular/app.js')

    script(src='/javascripts/angular/services.js')

    script(src='/javascripts/angular/controllers.js')

    body

    div(ng-view)

    View Slide





  16. Book Info Website















    View Slide

  17. //- book-info-website/views/index.jade

    script(src='/assets/modules/angular/angular.js')

    script(src='/assets/modules/angular-resource/angular-resource.js')

    script(src='/assets/modules/angular-route/angular-route.js')

    View Slide

  18. //- book-info-website/views/index.jade
    script(src='/javascripts/angular/app.js')

    script(src='/javascripts/angular/services.js')

    script(src='/javascripts/angular/controllers.js')

    View Slide

  19. // book-info-website/public/javascripts/angular/app.js

    var booksApp = angular.module('booksApp', [

    'ngResource', 

    'ngRoute',

    'booksServices',

    'booksControllers'

    ]);


    booksApp.config(['$routeProvider',

    function($routeProvider) {

    $routeProvider.

    when('/', {

    templateUrl: 'partials/list.html',

    controller: 'IndexController'

    }).

    when('/:isbn', {

    templateUrl: 'partials/details.html',

    controller: 'DetailController'

    }).

    otherwise({

    redirectTo: '/'

    });

    }]);

    View Slide

  20. //- book-info-website/views/index.jade
    html(lang="en" ng-app=“booksApp”)

    // book-info-website/public/javascripts/angular/app.js

    var booksApp = angular.module('booksApp', [

    'ngResource', 

    'ngRoute',

    'booksServices',

    'booksControllers'

    ]);

    View Slide

  21. View Slide

  22. // book-info-website/public/javascripts/angular/app.js
    booksApp.config(['$routeProvider',

    function($routeProvider) {

    $routeProvider.

    when('/', {

    templateUrl: 'partials/list.html',

    controller: 'IndexController'

    }).

    when('/:isbn', {

    templateUrl: 'partials/details.html',

    controller: 'DetailController'

    }).

    otherwise({

    redirectTo: '/'

    });

    }]);

    //- book-info-website/views/index.jade
    body

    div(ng-view)

    View Slide

  23. //- book-info-website/views/partials/list.jade

    div(ng-controller="IndexController")

    h1= title

    h2 Book list!

    ul

    li(ng-repeat="book in books")

    a(href="#/{{book.isbn}}") {{book.title}}

    View Slide

  24. // book-info-website/public/javascripts/angular/controllers.js

    var booksControllers = angular.module('booksControllers', []);


    booksControllers.controller('IndexController', ['$scope', 'Book', function ($scope, Book) {

    Book.query({}, function (books) {

    $scope.books = books;

    });

    }]);


    booksControllers.controller(

    'DetailController',

    ['$scope', '$routeParams', 'Book',

    function ($scope, $routeParams, Book) {

    Book.pushGet({isbn: $routeParams.isbn}, function (book) {

    $scope.book = book;

    });


    $scope.$on('$destroy', function () {

    Book.stopPushGet();

    });

    }

    ]

    );

    View Slide

  25. // book-info-website/public/javascripts/angular/controllers.js
    var booksControllers = angular.module('booksControllers', []);


    booksControllers.controller(
    'IndexController',
    ['$scope', 'Book',
    function ($scope, Book) {

    $scope.books = Book.query();

    }]
    );

    View Slide

  26. // book-info-website/public/javascripts/angular/services.js

    var booksService = angular.module('booksServices', []);


    booksService.factory('Book', ['$resource', '$timeout',

    function ($resource, $timeout) {

    var api = $resource('api/v2/:isbn', {'isbn': '@id'});


    var promise;

    var pushing = false;


    api.pushGet = function (params, callback) {

    if (!pushing) {

    pushing = true;


    var refreshData = function () {

    api.get(params, function (book) {

    callback(book);


    promise = $timeout(

    function () {

    refreshData(params, callback);

    }, 4000);

    });

    };


    refreshData(params, callback);

    }

    };


    api.stopPushGet = function () {

    pushing = false;


    if (angular.isDefined(promise)) {

    $timeout.cancel(promise);

    promise = undefined;

    }

    };


    return api;

    }]);

    View Slide

  27. // book-info-website/public/javascripts/angular/services.js
    var booksService = angular.module('booksServices', []);


    booksService.factory('Book', ['$resource', '$timeout',

    function ($resource, $timeout) {

    var api = $resource('api/v2/:isbn', {'isbn': '@id'});

    …

    return api;

    }]);


    View Slide

  28. MVVM Framework
    View
    View
    Model
    Model
    Remote
    API
    Browser Server

    View Slide

  29. // book-info-website/public/javascripts/angular/controllers.js
    booksControllers.controller(

    'DetailController',

    ['$scope', '$routeParams', 'Book',

    function ($scope, $routeParams, Book) {

    Book.pushGet({isbn: $routeParams.isbn}, function (book) {

    $scope.book = book;

    });


    $scope.$on('$destroy', function () {

    Book.stopPushGet();

    });

    }

    ]

    );

    View Slide

  30. // book-info-website/public/javascripts/angular/services.js
    var promise;

    var pushing = false;


    api.pushGet = function (params, callback) {

    if (!pushing) {

    pushing = true;


    var refreshData = function () {

    api.get(params, function (book) {

    callback(book);


    promise = $timeout(

    function () {

    refreshData(params, callback);

    }, 4000);

    });

    };


    refreshData(params, callback);

    }

    };

    View Slide

  31. // book-info-website/public/javascripts/angular/services.js
    api.stopPushGet = function () {

    pushing = false;


    if (angular.isDefined(promise)) {

    $timeout.cancel(promise);

    promise = undefined;

    }

    };

    View Slide

  32. Alternatives?
    • React
    • Backbone
    • Ember

    View Slide

  33. Future of frontend
    development
    • Simpler than it looks
    • Has a lot of similarities with server side frameworks
    • Fast, as data is loaded after the bare minimum

    View Slide

  34. Express
    Contender for the worlds
    most boring logo

    View Slide

  35. Book Information Service
    Book Jacket
    API
    Rating API
    Book Info
    Website
    Browser

    View Slide

  36. Blocking
    Book Jacket
    API
    Rating API
    Book Info
    Website
    Book Info
    Website

    View Slide

  37. Non-blocking
    Book Jacket
    API
    Rating API
    Book Info
    Website
    Book Info
    Website

    View Slide

  38. – Express Demo
    “How likely is that it will break twice?”

    View Slide

  39. // book-info-website/app.js

    var express = require('express');

    var path = require('path');

    var favicon = require('serve-favicon');

    var logger = require('morgan');

    var cookieParser = require('cookie-parser');

    var bodyParser = require('body-parser');


    var routes = require('./routes/index');

    var bookApi1 = require('./routes/book-api-v1');

    var bookApi2 = require('./routes/book-api-v2');


    var app = express();


    // view engine setup

    app.set('views', path.join(__dirname, 'views'));

    app.set('view engine', 'jade');


    // uncomment after placing your favicon in /public

    //app.use(favicon(__dirname + '/public/favicon.ico'));

    app.use(logger('dev'));

    app.use(bodyParser.json());

    app.use(bodyParser.urlencoded({ extended: false }));

    app.use(cookieParser());

    app.use(express.static(path.join(__dirname, 'public')));

    app.use("/assets/modules", express.static(__dirname + '/bower_components'));


    app.use('/', routes);

    app.use('/api/v1', bookApi1);

    app.use('/api/v2', bookApi2);


    // catch 404 and forward to error handler

    app.use(function(req, res, next) {

    var err = new Error('Not Found');

    err.status = 404;

    next(err);

    });


    // error handlers


    // development error handler

    // will print stacktrace

    if (app.get('env') === 'development') {

    app.use(function(err, req, res, next) {

    res.status(err.status || 500);

    res.render('error', {

    message: err.message,

    error: err

    });

    });

    }


    // production error handler

    // no stacktraces leaked to user

    app.use(function(err, req, res, next) {

    res.status(err.status || 500);

    res.render('error', {

    message: err.message,

    error: {}

    });

    });



    module.exports = app;

    View Slide

  40. // book-info-website/app.js


    var routes = require('./routes/index');

    var bookApi1 = require('./routes/book-api-v1');

    var bookApi2 = require('./routes/book-api-v2');

    app.use("/assets/modules", express.static(__dirname + '/bower_components'));


    app.use('/', routes);

    app.use('/api/v1', bookApi1);

    app.use('/api/v2', bookApi2);

    View Slide

  41. // book-info-website/routes/index.js

    var express = require('express');

    var router = express.Router();


    /* GET home page. */

    router.get('/', function (req, res, next) {

    res.render('index');

    });


    /* GET home page. */

    router.get('/partials/details.html', function (req, res, next) {

    res.render('partials/details');

    });


    /* GET home page. */

    router.get('/partials/list.html', function (req, res, next) {

    res.render('partials/list');

    });



    module.exports = router;

    View Slide

  42. // book-info-website/routes/index.js
    /* GET home page. */

    router.get('/', function(req, res, next) {

    res.render('index');

    });

    View Slide

  43. //- book-info-website/views/index.jade

    doctype html

    html(lang="en" ng-app="booksApp")

    head

    title Book Info Website

    link(rel='stylesheet', href='/stylesheets/style.css')

    script(src='/assets/modules/angular/angular.js')

    script(src='/assets/modules/angular-resource/angular-
    resource.js')

    script(src='/assets/modules/angular-route/angular-route.js')

    script(src='/javascripts/angular/app.js')

    script(src='/javascripts/angular/services.js')

    script(src='/javascripts/angular/controllers.js')

    body

    div(ng-view)

    View Slide

  44. // book-info-website/routes/book-api-v1.js

    var express = require('express');


    var booksApiClient = require('./../api-client/books');


    var router = express.Router();


    router.get('/', function (req, res, next) {

    booksApiClient.findAll(function (err, books) {

    res.json(books);

    });

    });


    router.get('/:isbn', function (req, res, next) {

    booksApiClient.getBook(req.params.isbn, function (err, bookDetail) {

    res.json(bookDetail);

    });

    });


    module.exports = router;

    View Slide

  45. // book-info-website/routes/book-api-v1.js
    router.get('/', function (req, res, next) {

    booksApiClient.findAll(function (err, books) {

    res.json(books);

    });

    });

    View Slide

  46. // book-info-website/api-client/books.js

    var makeHttpRequest = require('./client');


    var Books = function () {


    var endpoint = "http://book-jacket-api.herokuapp.com/";


    this.getBook = function (isbn, callback) {

    makeHttpRequest(endpoint.concat(isbn), callback)

    };


    this.findAll = function (callback) {

    makeHttpRequest(endpoint, callback)

    };

    };


    module.exports = new Books();

    View Slide

  47. // book-info-website/api-client/client.js
    var http = require('http');


    module.exports = function (url, callback) {

    http.get(url, function (res) {

    var str = '';


    res.on('data', function (chunk) {

    str += chunk;

    });


    res.on('end', function () {

    var apiResponse = JSON.parse(str);


    callback(null, apiResponse);

    });

    }).on('error', function (e) {

    callback(e);

    });

    };

    View Slide

  48. // book-info-website/routes/book-api-v2.js


    var express = require('express');

    var async = require('async');

    var _ = require('underscore');


    var booksApiClient = require('./../api-client/books');

    var ratingsApiClient = require('./../api-client/ratings');


    var router = express.Router();


    router.get('/', function (req, res, next) {

    booksApiClient.findAll(function (err, books) {

    res.json(books);

    });

    });


    router.get('/:isbn', function (req, res, next) {

    async.parallel({

    'book': function (callback) {

    booksApiClient.getBook(req.params.isbn, function (err, bookDetail) {

    callback(err, bookDetail);

    });

    },

    'rating': function (callback) {

    ratingsApiClient.getRating(req.params.isbn, function (err, ratings) {

    callback(err, ratings);

    });

    }

    }, function (err, results) {

    _.extend(results.book, {'rating': results.rating});

    res.json(results.book);

    });

    });


    module.exports = router;

    View Slide

  49. // book-info-website/routes/book-api-v2.js
    router.get('/:isbn', function (req, res, next) {

    async.parallel({

    'book': function (callback) {

    booksApiClient.getBook(req.params.isbn, function (err, bookDetail) {

    callback(err, bookDetail);

    });

    },

    'rating': function (callback) {

    ratingsApiClient.getRating(req.params.isbn, function (err, ratings) {

    callback(err, ratings);

    });

    }

    }, function (err, results) {

    _.extend(results.book, {'rating': results.rating});

    res.json(results.book);

    });

    });


    View Slide

  50. Alternatives?
    • Akka/Storm/React
    • Queues + Any language
    • Go/Rust/Other language with parallelisation

    View Slide

  51. Express
    • Non-blocking
    • Front-end Developer Friendly
    • Lightweight

    View Slide

  52. In conclusion
    • Lower the barrier to entry to allow flexibility in you
    team
    • MVVM frameworks are going to get more popular
    • MVVM frameworks require APIs
    • Use middleware to hide none public services

    View Slide

  53. http://www.happyjar.com/comic/pay-rise/

    View Slide

  54. Any Questions?
    @PurpleBooth
    [email protected]

    View Slide