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 full-size slide

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

    View full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size 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 full-size slide

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

    View full-size 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 full-size slide

  10. MVVM
    Model, View and View Model

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  14. //- 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 full-size slide





  15. Book Info Website















    View full-size slide

  16. //- 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 full-size slide

  17. //- 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 full-size slide

  18. // 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 full-size slide

  19. //- 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 full-size slide

  20. // 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 full-size slide

  21. //- 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 full-size slide

  22. // 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 full-size slide

  23. // 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 full-size slide

  24. // 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 full-size slide

  25. // 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 full-size slide

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

    View full-size slide

  27. // 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 full-size slide

  28. // 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 full-size slide

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

    pushing = false;


    if (angular.isDefined(promise)) {

    $timeout.cancel(promise);

    promise = undefined;

    }

    };

    View full-size slide

  30. Alternatives?
    • React
    • Backbone
    • Ember

    View full-size slide

  31. 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 full-size slide

  32. Express
    Contender for the worlds
    most boring logo

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  37. // 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 full-size slide

  38. // 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 full-size slide

  39. // 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 full-size slide

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

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

    res.render('index');

    });

    View full-size slide

  41. //- 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 full-size slide

  42. // 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 full-size slide

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

    booksApiClient.findAll(function (err, books) {

    res.json(books);

    });

    });

    View full-size slide

  44. // 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 full-size slide

  45. // 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 full-size slide

  46. // 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 full-size slide

  47. // 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 full-size slide

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

    View full-size slide

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

    View full-size slide

  50. 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 full-size slide

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

    View full-size slide

  52. Any Questions?
    @PurpleBooth
    [email protected]

    View full-size slide