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

AngularJS and MicroServices

AngularJS and MicroServices

3a1c6ce62064c58e2420e9e27c125716?s=128

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}
  2. Angular JS & Microservices Welcome to the world of tomorrow!

  3. Goals • Working knowledge • Client side JavaScript frameworks •

    Know how to integrate them with existing services • Become more of a Cross Stack developer
  4. Who’re you? Billie Thompson @PurpleBooth billie@purplebooth.co.uk PHP Contractor

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

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

    Website Browser
  7. Book Information Service DB Queues Logic Responsive Extendible Clean Parallel

    Serve JS Frontend-y Book Jacket API Rating API Book Info Website Browser
  8. Book Information Service Silex PHP AngularJS JavaScript Express JavaScript Book

    Jacket API Rating API Book Info Website Browser
  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}
  10. MVVM Model, View and View Model

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

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

  13. None
  14. –AngularJS Demo “It looked so easy when I saw someone

    else’s talk”
  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)
  16. <!DOCTYPE html>
 <html lang="en" ng-app="booksApp">
 
 <head>
 <title>Book Info Website</title>


    <link rel="stylesheet" href="/stylesheets/style.css">
 <script src="/assets/modules/angular/angular.js"></script>
 <script src="/assets/modules/angular-resource/angular-resource.js"></script>
 <script src="/assets/modules/angular-route/angular-route.js"></script>
 <script src="/javascripts/angular/app.js"></script>
 <script src="/javascripts/angular/services.js"></script>
 <script src="/javascripts/angular/controllers.js"></script>
 </head>
 
 <body>
 <div ng-view></div>
 </body>
 
 </html>
  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')

  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')

  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: '/'
 });
 }]);
  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'
 ]); 

  21. None
  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)
  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}}
  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();
 });
 }
 ]
 );
  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();
 }] ); …
  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;
 }]);
  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;
 }]);

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

  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();
 });
 }
 ]
 );
  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);
 }
 };
  31. // book-info-website/public/javascripts/angular/services.js api.stopPushGet = function () {
 pushing = false;


    
 if (angular.isDefined(promise)) {
 $timeout.cancel(promise);
 promise = undefined;
 }
 };
  32. Alternatives? • React • Backbone • Ember

  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
  34. Express Contender for the worlds most boring logo

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

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

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

    Info Website
  38. – Express Demo “How likely is that it will break

    twice?”
  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;
  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); 

  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;
  42. // book-info-website/routes/index.js /* GET home page. */
 router.get('/', function(req, res,

    next) {
 res.render('index');
 }); …
  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)
  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;
  45. // book-info-website/routes/book-api-v1.js router.get('/', function (req, res, next) {
 booksApiClient.findAll(function (err,

    books) {
 res.json(books);
 });
 });
  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();
  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);
 });
 };
  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;
  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);
 });
 });

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

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

  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
  53. http://www.happyjar.com/comic/pay-rise/

  54. Any Questions? @PurpleBooth billie@purplebooth.co.uk