Service Oriented Architecture 

Presented by:

Chad King

Front End Web Development is Hard and carries inherent risk to the application

The browser is rapidly evolving

  • ECMAScript 6 && 7 
  • Web Components
  • Internet Explorer Usage is Dropping
  • There are a lot of really cool things coming down the pipeline right now
    • WebGL 3D
    • Isomorphic JavaScript
    • Cross Platform Development with JS
    • Numerous new techniques for async programming in JS
      • ​Observables
      • Generator Functions
      • Async Functions

Complexity is shifting from backend to front end

  • Routing
  • Templating
  • Backend as API platform
  • MVC/MVVM/MV*/MV What ever

It's Really hard to pick the best practices.

  • Classes, Objects, Functional
  • AMD or CommonJS
  • Dependency Managing
  • Different Solutions
    • Angular Dirty-checking
    • React's Virtual Dom

Forget About Picking The right Front End Framework:

  • Cappuccino
  • qooxdoo
  • Spine
  • Eyeballs
  • Sammy
  • Choco
  • Agility
  • Knockout
  • Angular
  • Meteor
  • React
  • Aurelia
  • ng2?

So what do we do?

Focus on What Browsers can/will support.

Some Facts:

  • Views are relatively easy to create
  • Business logic can be complex
    • Re-creating business logic can lead to lost lessons

Not Guaranteed:

  • XYZ library/framework will continue to be supported.

Guaranteed:

  • You will need to support your application beyond the life cycle of your framework.

Proposal

Service-oriented Architecture

Why Services?

  • Applications are already service heavy
  • Well written services are least opinionated
  • Avoid abuse of library features

Abusive Code

'use strict';
angular.module('demo')
  .controller('demoCtrl', function ($scope, $http) {
    var itemsPerPage = 5;
    $scope.pagination = [];
    $scope.pageList = [];

    $http.get('http://jsonplaceholder.typicode.com/users/').then(function(data){
      $scope.userList = data.data;
    });

    $scope.getPosts = function(id){
      $http.get('http://jsonplaceholder.typicode.com/users/' + id +'/posts/').then(function(data){
        $scope.postList = data.data;
        pageCount();
      });
    }

    $scope.getComments = function(post){
      $http.get('http://jsonplaceholder.typicode.com/comments?postId=' + post.id).then(function(data){
        $scope.commentList = data.data;
        secondPagination();
      });
    }

  var secondPagination = function(){
    if(!_.isUndefined($scope.postList)){
      var divisor = Math.ceil($scope.commentList.length/itemsPerPage);
      for(var i = 0; i < divisor; i++){
        $scope.pageList.push({currentPage: i});
      }
      $scope.commentList = _.chunk($scope.commentList, itemsPerPage);
    }
  }


    var pageCount = function(){
      if(!_.isUndefined($scope.postList)){
        var divisor = Math.ceil($scope.postList.length/itemsPerPage);
        for(var i = 1; i < divisor; i++){
          $scope.pagination.push({currentPage: i});
        }
        $scope.postList = _.chunk($scope.postList, itemsPerPage);
      }
    }
  });

Slightly Better

'use strict';
angular.module('sdkDemo.landing.demo-ctrl', [])
  .controller('MainCtrl', function (userList) {
    var vm = this;
    vm.userList = userList;
  });
'use strict'
angular.module('sdkDemo.landing.config', [])
.config(function ($stateProvider, $urlRouterProvider) {
  $stateProvider
    .state('home', {
      url: '/',
      templateUrl: 'app/main/html/main.html',
      controller: 'MainCtrl as vm',
      resolve: {
        userList: function(Users){
          return Users.getUsersList().then(function(data){
            return data
          });
        }
      }
    });

  $urlRouterProvider.otherwise('/');
});
'use strict';

angular.module('sdkDemo.api.users', [])
  .service('Users', function($http){

    this.getUsersList = function(){
      return $http.get('http://jsonplaceholder.typicode.com/users/').then(function(data){
        return data.data
      });
    }

    this.getUserPosts = function(id){
      return $http.get('http://jsonplaceholder.typicode.com/users/' + id +'/posts/').then(function(data){
        return data.data
      });
    }

    this.getPostsComments = function(id){
      return $http.get('http://jsonplaceholder.typicode.com/comments?postId=' + id).then(function(data){
        return data.data
      });
    }

    this.paginate = function(postList, perPage, countAt){
      var result = {};
      result.pagination = [];
      if(!_.isUndefined(postList)){
        var divisor = Math.ceil(postList.length/perPage);
        for(var i = countAt; i < divisor; i++){
          result.pagination.push({currentPage: i});
        }
        result.list = _.chunk(postList, perPage);
      }
      return result;
    };
  });

What's Wrong With This?

  • Relies heavily on the existence of $scope
    • Relies on framework to organize our code for us
  • Impossible to migrate to other technologies without rewriting code completely
    • Angular 2.0
    • React 
    • Pure Web Components

How Do We Quit being addicted to frameworks?

  • Use as many standard technologies as you can where you can.
    • Es6
    • Web Components
  • Write your business logic into vanilla es6 services

ES6 Classes

'use strict';
import {RequestProvider} from './request-provider';

export class Resource extends RequestProvider{
  constructor(){
  }

  get(path){
    let request = super.create(path, {method: 'GET'});
    return super._fetch(request);
  }

  post(path, payload){
    let request = super.create(path, {method: 'POST', body: JSON.stringify(payload)});
    return super._fetch(request);
  }

  put(path, payload){
    let request = super.create(path, {method: 'PUT', body: JSON.stringify(payload)});
    return super._fetch(request);
  }

  remove(path){
    console.log(this.path)
    let request = super.create(this.path, {method: 'DELETE'});
    return super._fetch(request);
  }

}

Consuming a Service

'use strict';
import {Vigi} from './vigi';
import {BaseUrl} from './base-url-config';
import {Rest} from './rest-service';

 const vigi = () => {
   let baseUrl = new BaseUrl();
   let rest = new Rest();
   return new Vigi(baseUrl, rest);
};



var moduleName = 'vigi';
angular.module(moduleName, [])
    .factory('vigi', vigi);
export default moduleName;
'use strict';
class MainCtrl {
  constructor ($scope, vigi) {

    vigi.setBaseUrl('http://jsonplaceholder.typicode.com');
    vigi.one('posts').get().then(data => {
      console.log(data);
    });

    vigi.one('posts', 3).remove();

    vigi.one('posts').post({body: 'super testy', title: 'greatest ever'}).then(data => {
      console.log(data)
    });

    vigi.one('posts', 1).update({body: 'updated', title: 'blah'}).then(data => {
      console.log(data)
    })
  }
}

MainCtrl.$inject = ['$scope', 'vigi'];
export default MainCtrl;

Benefits

  • Easy to reason about
  • Does not rely on anything framework specific except for rendering
    • Highly Portable
    • Requires rebuilding views and minimal logic 
      • Relatively cheap
  • Highly Testable
  • Not Es6 Reliant
    • Es6 is a tool to assist in code design
    • Can be replicated in normal javascript

Downsides

  • Move into patterns away from framework conventions
    • Lose some conciseness
    • Lose some optimizations
  • Little assistance for application architecture
    • Requires piecing together application from library
    • More low level attention
      • Browser support
      • Implementing already solved problems

What to Do?

That depends...

Business needs

  • How important is scalability?
  • How much time do I have?
  • How disciplined are my teams?

What does this buy me? Framework Independence: You may not be able to swap out frameworks in a single sprint but it will now look like a manageable task.

What can we do to make our services better?

  • Use a Dependency Injection System
    • DI.js
    • Injection module from Aurelia
    • Injection module from ng2
  • Write unopinionated services that can be used as dependencies across your platform/product line 
    • Think about the $http provider. What if you had a similar service written into vanilla JS that you could take with you to any project. 
  • Place these services into Private NPM Modules
    • Rapidly build new applications using your existing code base.

Questions?

Copy of deck

By social4hyq

Copy of deck

  • 722