SOA with Angular

Agile and Simplicity

Why is frontend web development so difficult?

The browser is evolving rapidly

  • ECMAScript 6
  • Web Components
  • Internet Explorer usage dropping

Complexity shifted from backend to frontend

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

Nebulous best practices

  • Classes or Objects?
  • AMD or CommonJS?
  • Dependency managing
  • Different solutions
    • Angular dirty-checking
    • React's virtual dom

So what do I do?

Solution

Focus on what browsers can/will support

Guarantees

  • ECMAScript 6
  • Web Components

Some facts

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

Not guaranteed

  • [insert framework/library] will continue to be supported

Proposal

Service-oriented Architecture

Why services?

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

Abuse

app.controller('AbusiveCtrl', function ($scope, $rootScope, $http) {
  $scope.clickHandler = function () {
    $http.get('/data/from/file.json')
      .then(function (response) {
        var data = response.data;
        $rootScope.state = !!data.completed;
        $rootScope.$broadcast('data:received', data);
      });
  };
});

Not much better

app.factory('AbusiveService', function ($http, $rootScope) {
  return {
    getData: getData
  };

  function getData() {
    return $http.get('data/from/file.json')
      .then(function (response) {
        var data = response.data;
        $rootScope.state = !!data.completed;
        $rootScope.$broadcast('data:received', data);

        return data;
      });
  }
}).controller('AbusiveCtrl', function ($scope, AbusiveService) {
  $scope.clickHandler = AbusiveService.getData;
});

Problems

  • Relies heavily on the existence of $scope/$rootScope
    • Angular's built-in eventing structure
    • Attaches global state in unorganized manner
  • Impossible to migrate to other technologies without rewriting code completely
    • Angular 2.0
    • React
    • Pure Web Components

Enter ES6

ES6 Classes

import {myUser as User} from 'my/models/user';

class Users {
  constructor(http) {
    this._cache = new Map();
    this._http = http;
  }

  getById(id) {
    return this._http.get('/users/' + id)
      .then(this._cacheUser);
  }

  _cacheUser(response) {
    var user = new User(response.data.user);
    this._cache[user.id] = user;

    return user;
  }
}

Classes with AtScript

import {Inject} from 'di/index';
import {myHttpResponse as HttpResponse} from 'my/foundation/httpResponse';
import {myHttp as Http} from 'my/core/api';
import {myUser as User} from 'my/models/user';

@Inject(Http);
class Users {
  _cache: map;
  _http: Http;

  constructor(http: Http) {
    this._cache = new Map();
    this._http = http;
  }

  getById(id) {
    return this._http.get('/users/' + id)
      .then(this._cacheUser);
  }

  _cacheUser(response: HttpResponse) {
    var user = new User(response.data.user);
    this._cache[user.id] = user;

    return user;
  }
}

Consuming service

import {myUsers as Users} from 'my/models/Users';
import {myPosts as Posts} from 'my/models/Posts';
import {myPosts as Comments} from 'my/models/Comments';

angular.module('my.app')
  .factory('Users', Users)
  .factory('Posts', Posts)
  .factory('Comments', Comments);

Controller

angular.module('my.app')
  .controller('HomeCtrl', function ($state, Home) {
    this.ctrl = Home;

    this.goToPost = function (post) {
      post.get()
        .then(function () {
          $state.go('post', { id: post.id });
        });
    };
  })
  .controller('PostCtrl', function ($state, Post) {
    this.ctrl = Post;
  });

View

<div class="scroll-container">
  <div pull-to-refresh="home.ctrl.posts.get().then(home.refresh)"></div>
  <div ng-repeat="post in home.ctrl.posts._posts">
    <list-post post="post" go-to="home.goToPost"></list-post>
  </div>
  <div load-more="home.ctrl.posts.getMore().then(home.update)"></div>
</div>

Benefits

  • Easy to reason about
  • Does not rely on anything framework-specific except for rendering
    • No $digest loop necessary
  • 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

Decision?

Answer

It depends

Business needs

  • How important is scalability?
  • How much time do I have?
  • How disciplined is (are) my team(s)?

Available for work

  • wesley.cho@gmail.com

Slides available at

https://slides.com/wesleycho/soa-angular

SOA with Angular

By Wesley Cho

SOA with Angular

Service-oriented Architecture with Angular.js - keeping agile and simple.

  • 5,031