EPF – A framework for keeping your Ember.js apps in sync.



EPF – A framework for keeping your Ember.js apps in sync.

1 0


epf-preso


On Github ghempton / epf-preso

EPF

A framework for keeping your Ember.js apps in sync.

Created by Gordon L. Hempton / @ghempton / GroupTalent / epf.io

Why Another Framework?

History

  • Migrated from Rails to Ember w/ Ember Data
  • First PR made two years ago
  • Been through every single revision of Ember Data

Ember Data - The Good

  • Loading data
  • Serialization
  • Sideloading
  • Relationships
  • Right level of abstraction

Ember Data - The Bad

  • Transactions
  • Data is "locked" while being committed
  • Limited error handling
  • Corner cases
  • Live on a fork

Needed to move off of Ember Data.

Other Solutions

  • Too lightweight
  • Missing features
  • Hard to migrate from ED

Time to build a new persistence framework.

EPF was born

Observations

  • Reading data is easy*
  • Modifying data is difficult (esp. relationships)
  • Always expect new data

Designing EPF

  • Non-blocking
  • Focus on synchronization
  • Correctness first, performance second
  • Straightforward migration from Ember Data
  • Re-think primitives (e.g. transactions)

EPF Today

  • Everything shown implemented
  • Used in production at GroupTalent
  • ~35 inter-related models
  • Not perfect

API

Setup

App.Adapter = Ep.RestAdapter.extend({
  namespace: 'api' // optionally set a prefix for all urls
});

Defining Models

App.Group = Ep.Model.extend({
  name: Ep.attr('string'),
  users: Ep.hasMany(App.User)
});

App.User = Ep.Model.extend({
  name: Ep.attr('string'),
  group: Ep.belongsTo(App.Group)
});

Loading Models

session.load(App.User, 1);

session.load('user', 1); // string version

session.load('user', 1).then(function(user) {
  // user will be loaded
});

Creating Models

session.create(App.User, {name: 'herp'});

session.create('user', {name: 'herp'}); // alternative string version

session.flush().then(function() {
  // changes will have been persisted
});

Updating Models

user.set('name', 'embereno');

session.flush().then(function() {
  // changes will have been persisted
});

// updates can happen while a flush is pending
user.set('name', 'kris');

Relationships

var group = session.create('group', {name: 'emberenos'});

user.set('group', group);

// inverse relationships automatically updated
group.get('users'); // [user]

Putting it all together

var group = session.create('group', {name: 'emberenos'});
var user = session.create('user', {name: 'dave'});

user.set('group', group);

group.get('users').pushObject(existingUser);

session.flush().then(function() {
  // all changes will be persisted
});

Isolating Changes

var user = session.load(App.User, 1);

// this create a "fork" of the session
var childSession = session.newSession();

// this user will be a separate instance from `user`
var childUser = childSession.load(App.User, 1);

user === childUser; // false, they are separate instances
user.isEqual(childUser); // true, key based comparison

childUser.name = 'derp'; // this will not affect `user`

// flush changes immediately to the parent session and server
childSession.flush();

Merging Changes - Under the Hood

$.getJSON('/users/1', function(data) {
  var user = App.User.create(data);
  session.merge(user);
});

var socket = io.connect('http://localhost');
socket.on('user', function (data) {
  var user = App.User.create(data);
  session.merge(user);
});

Naive Sync

What if...

  • Updates happen while request is in transit?
  • Error handling?

EPF Sync

EPF Sync

Roadmap

  • Implement low hanging features
  • Cleanup some internals (better perf)
  • Improved collections (pagination, etc.)
  • Socket Adapter
  • Starter kit

Future

REST vs. Sockets

Realtime?

Differential Synchronization

by Neil Fraser

That is all.

Gordon L. Hempton / @ghempton / GroupTalent / epf.io

Thanks

Tom Dale, Yehuda Katz, Stefan Penner, and all the ED contributors

Ray Bunnage and heartsentwined for early adoption