FALCOR One Model Everywhere Jafar Husain @jhusain Every user - - PowerPoint PPT Presentation

falcor
SMART_READER_LITE
LIVE PREVIEW

FALCOR One Model Everywhere Jafar Husain @jhusain Every user - - PowerPoint PPT Presentation

FALCOR One Model Everywhere Jafar Husain @jhusain Every user wants to believe that the entire cloud is on their device. What if you could code that way? This is the story of how eliminated 90% of the networking code in our app. Jafar


slide-1
SLIDE 1

Jafar Husain @jhusain

One Model Everywhere

FALCOR

slide-2
SLIDE 2

Every user wants to believe that the entire cloud is on their device.

slide-3
SLIDE 3

What if you could code that way?

slide-4
SLIDE 4

This is the story of how eliminated 90% of the networking code in our app.

slide-5
SLIDE 5

Jafar Husain

  • Netflix’s Cross Team UI Tech Lead
  • Architect of FALCOR
  • Active Participant in TC-39, the JavaScript

standards committee

slide-6
SLIDE 6

2014 3 2 1

slide-7
SLIDE 7

2010

Netflix had a RESTful API.

slide-8
SLIDE 8

is a browsing problem.

slide-9
SLIDE 9

http://www.netflix.com/genreLists ?rowOffset=0&rowSize=5&colOffset=5 &colSize=15&titleprops=name,boxshot http://www.netflix.com/setRating ?titleId=5&view=movieDetailPage

RPC API

2010 1

Netflix’s RESTful API

http://www.netflix.com/genreList/12429 http://www.netflix.com/title/1601923

slide-10
SLIDE 10

http://www.netflix.com/genreLists ?rowOffset=0&rowSize=5&colOffset=5 &colSize=15&titleprops=name,boxshot http://www.netflix.com/setRating ?titleId=5&view=movieDetailPage

RPC API

2011

slide-11
SLIDE 11

Why move away from REST?

slide-12
SLIDE 12

Once the web was a place to get things.

slide-13
SLIDE 13

Today the web is a place to do things.

slide-14
SLIDE 14

Web Server CDN Application Server

Web Servers serve large resources. App Servers serve small resources.

slide-15
SLIDE 15

Fine-Grained Title Resource

http://netflix.com/titles/95632123

{ id: 2523, name: “House of Cards”, boxshot: “http://…/018-192-x50.png”, rating: 5, bookmark: 52119562, director: “David Fincher”, // a few more fields }

slide-16
SLIDE 16

REST: Multiple Roundtrips

01: GET /genreLists 02: GET netflix.com/titles/956325 03: GET cdn01.netflix.com/072-192-x50.png 04: GET netflix.com/titles/992338 05: GET cdn03.netflix.com/018-192-x50.png 06: GET netflix.com/titles/912738 07: GET cdn09.netflix.com/651-70x50.png

27: GET netflix.com/titles/561826 28: GET cdn23.netflix.com/018-70x50.png

slide-17
SLIDE 17

Client HTTP Cache

REST: Cache Coherence

Title DB

Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue

Personalized Recommendations

Personalize d Genre Lists

Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue

Title Title Title

Personalize d Genre Lists

slide-18
SLIDE 18

REST

  • Cache Consistency
  • Loose Coupling
  • High Latency
  • Large Message Sizes
slide-19
SLIDE 19

http://www.netflix.com/genreLists ?pageSize=10x15&titleprops=id,boxshot http://www.netflix.com/setRating ?titleId=5&view=movieDetailPage

RPC API

2011

slide-20
SLIDE 20

/genreList?pageSize=13x15 /search?text=house /genreList?pageSize=13x15 /search?text=netflix /titleDetails?id=342 /titleDetails?id=342

Netflix RPC

slide-21
SLIDE 21

RPC: One Request per View

/titles/95632123

{ id: 2523, name: “House of Cards”, boxshot: “http://…/018-192x50.png”, rating: 5, bookmark: 52119562, executiveProducer: “David Fincher”, director: “David Fincher”, description: “This Emmy-winning original thriller series stars Golden Globe winner Kevin Spacey as ruthless, cunning Congressman Francis Underwood, who will stop at nothing to conquer the halls of power in Washington D.C. his secret weapon: his gorgeous, ambitious, and equally conniving wife Claire.” }

/genreLists?page=4x4?props=name,boxshot

[ { id: 2523, boxshot: “http://…/018-192x50.png” }, { id: 5123, boxshot: “http://…/062-192x50.png” }, { id: 78432, boxshot: “http://…/923-70x50.png }, { id: 6342, boxshot: “http://…/9423-70x50.png }, ... ]

slide-22
SLIDE 22

RPC: No Cache Consistency

Title Title Title

Personalize d Genre Lists

/genreLists ?page=4x4 &props=name,title /getTitleDetails?id=3432

Same info, two different URLS!

slide-23
SLIDE 23

RPC

  • Low Latency
  • Small Messages
  • Tight coupling
  • Manual Cache Management
slide-24
SLIDE 24

REST

  • Cache Consistency
  • Loose Coupling

RPC

  • Small Message Sizes
  • Low Latency

?

slide-25
SLIDE 25

FALCOR

slide-26
SLIDE 26

One Model Everywhere

slide-27
SLIDE 27

/model.json

slide-28
SLIDE 28

The Data is the API

slide-29
SLIDE 29

JSON

var member = new falcor.Model({ source: new HttpSource(‘/member.json’)});

Falcor JSON Model

var member = { name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }; print(member[“location”][“address”]); member.getValue([“location”, “address”]). toPromise(). then(print);

slide-30
SLIDE 30

Bind to the Cloud

slide-31
SLIDE 31

var memberModel = new falcor.Model(new falcor.HttpSource("/member.json")); var MemberView = React.createClass({ mixins: [FalcorComponent], renderContent: function() { return this.props.model.get( ["name"], function(name) { return <div>Welcome {name}</div>; }); }, renderLoading: function() { return <div><img src="spinner.gif"></div>; } }); React.render(<MemberView model={memberModel}/>);

MVC Framework Integration: React

var memberModel = new falcor.Model(new falcor.HttpSource("/member.json")); var MemberView = React.createClass({ mixins: [FalcorComponent], renderContent: function() { return this.props.model.get( ["name"], function(name) { return <div>Welcome {name}</div>; }); }, renderLoading: function() { return <div><img src="spinner.gif"></div>; } }); React.render(<MemberView model={memberModel}/>);

slide-32
SLIDE 32

Angular 2.0 (preview)

<div> {{ model.getValue([“person”, “name”])|rx}} lives at {{ model.getValue([“person”, “address”])|rx }} </div> Steve McGuire lives at 344 Seaside

slide-33
SLIDE 33

What When Where

slide-34
SLIDE 34

What data do you want?

Nothing Every value

slide-35
SLIDE 35

var member = new falcor.Model({ source: new falcor.HttpSource(“/member.json”) }); member.get([“genreLists”, 0, “name”])

What data do you want?

member. get([“genreLists”, {to:2}, {from:0, to:1}, “boxshot”]);

slide-36
SLIDE 36

When can data arrive?

Now (sync) Later (async)

slide-37
SLIDE 37

When: Reactive Programming

Choose any async model:

  • Promises (ES6)
  • Observable (Proposed for ES7)
  • Node-style Callbacks
  • Node Streams
slide-38
SLIDE 38

Where is the data?

Local Remote

slide-39
SLIDE 39

var member = { name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }}; print(member[“location”][“address”]);

JSON

var member = new falcor.Model({cache:{ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }}); print(member[“location”][“address”]);

Falcor

var member = { name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }; print(member[“location”][“address”]); var member = new falcor.Model({cache:{ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }}); member.get( [“location”, “address”], (address) => print(address)). toPromise();

Falcor Server

var member = new falcor.Model({cache:{ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }}); member.get( [“location”, “address”], (address) => print(address)). toPromise(); var member = new falcor.Model({cache:{ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }}); (new falcor.HttpServer(member)). listen(80); var member = new falcor.Model({source: new falcor.HttpSource(“member.json”)}); member.get( [“location”, “address”], (address) => print(address)). toPromise(); [“location”][“address”] member.get( [“location”, “address”]). toPromise(). then(json => response.write(json));

slide-40
SLIDE 40

var member = new falcor.Model({source: new falcor.HttpSource(“member.json”)}); member.get( [“location”, “address”], (address) => print(address)). toPromise();

Falcor

var member = new falcor.Model({cache:{ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }}); (new falcor.HttpServer(member)). listen(80);

Falcor Server

var member = new falcor.Model({cache:{ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }}); (new falcor.HttpServer(member)). listen(80);

Virtual Falcor Server

var member = new falcor.Model({ router: new falcor.Router([ { route: [“member”,[“name”,”occupation”]], get: (pathSet) => }, { route: [“member”,“location”,[“country”, ”city”, “address”]], get: (pathSet) => } ]) }); (new falcor.HttpServer(member)).listen(80);

var member = new falcor.Model({cache:{ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }}); (new falcor.HttpServer(member)). listen(80);

var member = new falcor.Model({ { route: [“member”,[“name”,”occupation”]], get: (pathSet) => memberDB. exec(`SELECT ${pathSet[1].join(‘,’)} FROM user WHERE id = ${request.cookies.userid}`) }, { route: [“member”,“location”,[“country”, ”city”, “address”]], get: (pathSet) => } ]) }); (new falcor.HttpServer(member)).listen(80); var member = new falcor.Model({ { route: [“member”,[“name”,”occupation”]], get: (pathSet) => memberDB. exec(`SELECT ${pathSet[1].join(‘,’)} FROM user WHERE id = ${request.cookies.userid}`) }, { route: [“member”,“location”,[“country”, ”city”, “address”]], get: (pathSet) => locationServer. getLocation(request.cookies.userid). then(location => ({ member: { location: getProps(location, pathSet[2]) } }) } ]) }); (new falcor.HttpServer(member)).listen(80);

slide-41
SLIDE 41

How do we do it efficiently?

slide-42
SLIDE 42

Falcor Optimization

  • Batching
  • Caching
  • Path Optimization
slide-43
SLIDE 43

Building Netflix with Falcor

slide-44
SLIDE 44

Defining a Model

slide-45
SLIDE 45
slide-46
SLIDE 46

Netflix Member JSON Model

var member = { genreLists: [ { name: "Suggestions For You", titleList: [ { id: 523, name: "Friends", rating: 5, // more fields }, // "Brain Games", "Spartacus"... ], }, // "New Releases", "British TV Shows", ... ] } var member = { genreLists: [ { name: "Suggestions For You", titleList: [ { id: 523, name: "Friends", rating: 5, // more fields }, // "Brain Games", "Spartacus"... ], }, // "New Releases", "British TV Shows", ... ] } var member = { genreLists: [ { name: "Suggestions For You", titleList: [ { id: 523, name: "Friends", rating: 5, // more fields }, // "Brain Games", "Spartacus"... ], }, // "New Releases", "British TV Shows", ... ] }

slide-47
SLIDE 47

We’re almost ready to move our model into the cloud.

http://netflix.com/member.json

slide-48
SLIDE 48

There’s just one problem…

slide-49
SLIDE 49

Netflix’s Domain Model is a Graph

slide-50
SLIDE 50

JSON is for Trees.

slide-51
SLIDE 51

When we convert a graph to a JSON we get duplicates.

“Suggestions for You” “New Releases” Genre Lists

slide-52
SLIDE 52

Introducing JSON Graph

slide-53
SLIDE 53

JSON Graph

  • Graph format for JSON
  • Tree with Symbolic Links
  • Every value type is a resource
slide-54
SLIDE 54

model[“genreLists”][0]["titlesList"][0][“name”] model[“genreLists”][0]["titlesList"][0][“name”] model.get([“genreLists”, 0, "titlesList”, 0, “name”]) model.get([“genreLists”, 0, "titlesList”, 0, “name”]) model.get([“genreLists”, 0, "titlesList”, 0, “name”]) model.get([“genreLists”, 0, "titlesList”, 0, “name”]) model.get([“genreLists”, 0, "titlesList”, 0, “name”]) model.get([“genreLists”, 0, "titlesList”, 0, “name”]) model.get([ “name”]) model.get([ “titlesById” ,956, “name”]) model.get([“titlesById”, 956, “name”]) model.get([“titlesById”, 956, “name”]) model.get([“titlesById”, 956, “name”]) model.get([“titlesById”, 956, “name”]) var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ { id: 956, name: "Friends", rating: 3 } ] }, { name: "New Releases", titlesList: [ { id: 956, name: "Friends", rating: 3 } ] } ] }

JSON

var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ { id: 956, name: "Friends", rating: 3 } ] }, { name: "New Releases", titlesList: [ { id: 956, name: "Friends", rating: 3 } ] } ] } model[“genreLists”][0]["titlesList"][0][“name”] var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ { id: 956, name: "Friends", rating: 3 } ] }, { name: "New Releases", titlesList: [ { id: 956, name: "Friends", rating: 3 } ] } ] } model[“genreLists”][0]["titlesList"][0][“name”] model[“genreLists”][0]["titlesList"][0][“rating”] = 5 var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ { id: 956, name: "Friends", rating: 3 } ] }, { name: "New Releases", titlesList: [ { id: 956, name: "Friends", rating: 3 } ] } ] } var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ { id: 956, name: "Friends", rating: 5 } ] }, { name: "New Releases", titlesList: [ { id: 956, name: "Friends", rating: 3 } ] } ] } model[“genreLists”][0]["titlesList"][0][“rating”] = 5 var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ { id: 956, name: "Friends", rating: 5 } ] }, { name: "New Releases", titlesList: [ { id: 956, name: "Friends", rating: 3 } ] } ] } var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ { id: 956, name: "Friends", rating: 5 } ] }, { name: "New Releases", titlesList: [ { id: 956, name: "Friends", rating: 3 } ] } ] } var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ ] }, { name: "New Releases", titlesList: [ ] } ] }

JSON Graph

var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ ] }, { name: "New Releases", titlesList: [ ] } ], titlesById: { “956”: { } } } { id: 956, name: "Friends", rating: 3 } { id: 956, name: "Friends", rating: 3 } { id: 956, name: "Friends", rating: 3 } { id: 956, name: "Friends", rating: 3 } var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ ] }, { name: "New Releases", titlesList: [ ] } ], titlesById: { “956”: { name: "Friends", rating: 3 } } } name: "Friends", rating: 3 var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ [“titlesById”, 956] ] }, { name: "New Releases", titlesList: [ [“titlesById”, 956] ] } ], titlesById: { “956”: { name: "Friends", rating: 3 } } } { id: 956, name: "Friends", rating: 3 } var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ [“titlesById”, 956] ] }, { name: "New Releases", titlesList: [ [“titlesById”, 956] ] } ], titlesById: { “956”: { name: "Friends", rating: 3 } } } var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ [“titlesById”, 956] ] }, { name: "New Releases", titlesList: [ [“titlesById”, 956] ] } ], titlesById: { “956”: { name: "Friends", rating: 3 } } } var model = { genreLists: { { name: "Suggestions For You" titlesList: [ [“titlesById”, 956] ] }, { name: "New Releases", titlesList: [ [“titlesById”, 956] ] } }, titlesById: { “956”: { name: "Friends", rating: 3 } } } var model = { genreLists: { “0”: { name: "Suggestions For You" titlesList: [ [“titlesById”, 956] ] }, “1”: { name: "New Releases", titlesList: [ [“titlesById”, 956] ] }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } } var model = { genreLists: { “0”: { name: "Suggestions For You" titlesList: { } }, “1”: { name: "New Releases", titlesList: { } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } } “0”: , length: 75 “0”: , length: 75 [“titlesById”, 956] [“titlesById”, 956] var model = { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } } var model = { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } } var model = { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } } var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); “titlesById”, 956 var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); model[“genreLists”][0]["titlesList"][0][“rating”] = 5 model.setValue(["genreLists", 0, "titlesList", 0, "rating"], 5) model.setValue(["genreLists", 0, "titlesList", 0, "rating"], 5) model.setValue([“genreLists”, 0, "titlesList”, 0, “rating”], 5) model.setValue([“genreLists”, 0, "titlesList”, 0, “rating”], 5) model.setValue([“genreLists”, 0, "titlesList”, 0, “rating”], 5) model.setValue([“genreLists”, 0, "titlesList”, 0, “rating”], 5) model.setValue([ “rating”], 5) model.setValue([ “titlesById” ,956, “rating”], 5) model.setValue([“titlesById”, 956, “rating”], 5) model.setValue([“titlesById”, 956, “rating”], 5) model.setValue([“titlesById”, 956, “rating”], 5) var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); “titlesById”, 956 var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 5 } } }); model[“genreLists”][0]["titlesList"][0][“rating”] = 5

slide-55
SLIDE 55

JSON Graph: Small Resources

{ genreLists: { “0”: { name: “Suggestions for You”, titlesList: { “0”: [“titlesById”, 956], “1”: [“titlesById”, 192], // more titles length: 75 }, }, length: 40 }, titlesById: { “956”: { name: “Friends”, rating: 5 } } }

slide-56
SLIDE 56

JSON Graph

[“genreLists”, “length”] [“genreLists”, *, name] [“titlesById”, 23432, “name”] [“titlesById”, 23432, “rating”] [“titlesById”, 23432, “description”] [“titlesById”, 23432, “bookmark”] [“titlesById”, 23432, “director”] // more fields

HTTP/REST

/genreLists /titles/23432

Fine-Grained Resources

slide-57
SLIDE 57

Idempotent Operations

Falcor Model

get set

HTTP/REST

GET PUT

slide-58
SLIDE 58

Building a Virtual Model

http://netflix.com/member.json

slide-59
SLIDE 59

Virtual Model: Defining Routes

var member = new falcor.Model({ router: new falcor.Router([ { route: ["genreLists", Router.INTEGERS, Router.INTEGERS], // example match: ["genreLists", [1,2,3], [1,5,2]] pathSet => { // retrieve data from member’s personalized list DB // return as JSON Graph }, }, { route: [“titlesById”, Router.INTEGERS, [“name”, “rating”]], // example match: [“titlesById”, [2343,5123], [“name”]] pathSet => { // retrieve data from title DB // return as JSON Graph } } ]) });

slide-60
SLIDE 60

Title Route

var member = new falcor.Model({ router: new falcor.Router([ // genre list route snipped { route: [“titlesById”, Router.INTEGERS, [“name”, “rating”]], // example match: [“titlesById”, [2343,5123], [“name”]] pathSet => { var titleIds = pathSet[1], fields = pathSet[2]; titleStore. getTitlesByIds(titleIds). then(titles => { var titlesById = {}; titles.forEach(title => { titlesById[title.id] = {}; fields.forEach(field => titlesByid[title.id][field] = title[field]); }); return { titlesById: titlesById }; }); } ]) });

slide-61
SLIDE 61

Virtual Model Path Evaluation

var member = new falcor.Model({ router: new falcor.Router([ { route: ["genreLists", Router.INTEGERS, Router.INTEGERS], pathSet => { /* retrieve data from lists DB */}, }, { route: [“titlesById”, Router.INTEGERS, [“name”, “rating”]], pathSet => {{ /* retrieve data from titles DB */} } ]) }); var member = new falcor.Model({ source: new falcor.HttpSource('member.json’)}); member. getValue([“genreLists”, 0, 0, “name”]). toPromise(); member.get(["genreLists", 0, 0, "name"]) var member = new falcor.Model({ router: new falcor.Router([ { route: ["genreLists", Router.INTEGERS, Router.INTEGERS], ([“genreList”],[0],[0]]) => { /* retrieve data from lists DB */}, }, { route: [“titlesById”, Router.INTEGERS, [“name”, “rating”]], pathSet => {{ /* retrieve data from titlesDB */} } ]) }); var member = new falcor.Model({ router: new falcor.Router([ { route: ["genreLists", Router.INTEGERS, Router.INTEGERS], pathSet => { /* retrieve data from lists DB */}, }, { route: [“titlesById”, Router.INTEGERS, [“name”, “rating”]], pathSet => {{ /* retrieve data from titles DB */} } ]), cache: { genreLists: { “0”: { “0”: [“titlesById”, 926] } } } }); var member = new falcor.Model({ router: new falcor.Router([ { route: ["genreLists", Router.INTEGERS, Router.INTEGERS], pathSet => { /* retrieve data from lists DB */}, }, { route: [“titlesById”, Router.INTEGERS, [“name”, “rating”]], pathSet => {{ /* retrieve data from titles DB */} } ]), cache: { genreLists: { “0”: { “0”: [“titlesById”, 926] } } } }); member.get(["genreLists", 0, 0, "name"]) var member = new falcor.Model({ router: new falcor.Router([ { route: ["genreLists", Router.INTEGERS, Router.INTEGERS], pathSet => { /* retrieve data from lists DB */}, }, { route: [“titlesById”, Router.INTEGERS, [“name”, “rating”]], pathSet => {{ /* retrieve data from titles DB */} } ]), cache: { genreLists: { “0”: { “0”: [“titlesById”, 926] } } } }); “titlesById”, 926 { genreLists: { “0”: { “0”: [“titlesById”, 926] } } } member.get([“titlesById”, 926, “name”]) var member = new falcor.Model({ router: new falcor.Router([ { route: ["genreLists", Router.INTEGERS, Router.INTEGERS], pathSet => { /* retrieve data from lists DB */}, }, { route: [“titlesById”, Router.INTEGERS, [“name”, “rating”]], ([“titlesById”, [926], [“aname”]]) => {{ /* retrieve data */} } ]), cache: { genreLists: { “0”: { “0”: [“titlesById”, 926] } } } }); var member = new falcor.Model({ router: new falcor.Router([ { route: ["genreLists", Router.INTEGERS, Router.INTEGERS], pathSet => { /* retrieve data from lists DB */}, }, { route: [“titlesById”, Router.INTEGERS, [“name”, “rating”]], pathSet => {{ /* retrieve data from titles DB */} } ]), cache: { genreLists: { “0”: { “0”: [“titlesById”, 926] } }, titlesById: { “926”: { name: “Friends” } } } }); var member = new falcor.Model({ router: new falcor.Router([ { route: ["genreLists", Router.INTEGERS, Router.INTEGERS], pathSet => { /* retrieve data from lists DB */}, }, { route: [“titlesById”, Router.INTEGERS, [“name”, “rating”]], pathSet => {{ /* retrieve data from titles DB */} } ]), cache: { genreLists: { “0”: { “0”: [“titlesById”, 926] } }, titlesById: { “926”: { name: “Friends” } } } }); var member = new falcor.Model({ router: new falcor.Router([ { route: ["genreLists", Router.INTEGERS, Router.INTEGERS], pathSet => { /* retrieve data from lists DB */}, }, { route: [“titlesById”, Router.INTEGERS, [“name”, “rating”]], pathSet => {{ /* retrieve data from titles DB */} } ]), cache: { genreLists: { “0”: { “0”: [“titlesById”, 926] } }, titlesById: { “926”: { name: “Friends” } } } }); member.get([“titlesById”, 926, “name”]) { titlesById: { “926”: { “name”: “Friends” } } } cache: { genreLists: { “0”: { “0”: [“titlesById”, 926] } }, titlesById: { “926”: { name: “Friends” } } } var member = new falcor.Model({ source: new falcor.HttpSource('member.json’), cache: { genreLists: { “0”: { “0”: [“titlesById”, 926] } }, titlesById: { “926”: { name: “Friends” } } }); member. getValue([“genreLists”, 0, 0, “name”]). toPromise(); var member = new falcor.Model({ router: new falcor.Router([ { route: ["genreLists", Router.INTEGERS, Router.INTEGERS], pathSet => { /* retrieve data from lists DB */}, }, { route: [“titlesById”, Router.INTEGERS, [“name”, “rating”]], pathSet => {{ /* retrieve data from titles DB */} } ]);

slide-62
SLIDE 62

Virtual Model Path Evaluation

var member = new falcor.Model({ source: new falcor.HttpSource('member.json’), cache: { genreLists: { “0”: { “0”: [“titlesById”, 926] } }, titlesById: { “926”: { name: “Friends” } } } }); member. getValue([“genreLists”, 0, 0, “name”). toPromise(); member.get([“titlesById”, 926, “rating”]) var member = new falcor.Model({ router: new falcor.Router([ { route: ["genreLists", Router.INTEGERS, Router.INTEGERS], pathSet => { /* retrieve data from lists DB */}, }, { route: [“titlesById”, Router.INTEGERS, [“name”, “rating”]], pathSet => {{ /* retrieve data from titles DB */} } ]); var member = new falcor.Model({ source: new falcor.HttpSource('member.json’), cache: { genreLists: { “0”: { “0”: [“titlesById”, 926] } }, titlesById: { “926”: { name: “Friends” } } } }); member. getValue([“genreLists”, 0, 0, “rating”]). toPromise(); var member = new falcor.Model({ source: new falcor.HttpSource('member.json’), cache: { genreLists: { “0”: { “0”: [“titlesById”, 926] } }, titlesById: { “926”: { name: “Friends” } } } }); member. getValue([“titlesById”, 926, “rating”]). toPromise();

slide-63
SLIDE 63

The World Wide Web is Flat

slide-64
SLIDE 64

The Type of the WWW

Map<String, Resource>

slide-65
SLIDE 65

The WWW is Flat

GET http://../genreLists/0/0/name 302 http://../titles/23432/name GET http://../titles/23432/name “Friends”

slide-66
SLIDE 66

The WWW is Flat (continued)

GET http://../genreLists/0/0/rating 302 http://../titles/23432/rating GET http://../titles/23432/rating 5.0

slide-67
SLIDE 67

No Hierarchy

GET http://../genreLists/0/0/name 302 Redirect http://../genreLists/0/0/* http://../titles/23432/*

slide-68
SLIDE 68

Sets Are Idempotent

model.set( { path: [“titles”, 234324, “rating”], value: 5 });

slide-69
SLIDE 69

Non Idempotent Operations

model.call( [“genreList”,0,”add”], [[“titles”, 234234]])

slide-70
SLIDE 70

Function Calls

  • Can invoke function that lives anywhere in

Graph

  • Functions can only be called, not

downloaded

  • Functions can invalidate any resource in

cache

slide-71
SLIDE 71

Falcor

  • Cache Consistency
  • Loose Coupling
  • Low Latency
  • Small Message Sizes
slide-72
SLIDE 72

Additional Features

  • Automatic Cache Management

– Items purged based on order in LRU – Custom size can be applied to items – One total size for all heterogenous types

  • Fast Dirty Checking

– All set operations on leaves mark branches as dirty – Can be used for fast model diff a la immutable types

slide-73
SLIDE 73

Mobile.js Web.js TV.js

Three JavaScript UIs

slide-74
SLIDE 74

Open-Source Soon

  • Should be available in the next few

months

  • Looking for help integrating with existing

MVC frameworks

  • @falcorjs
slide-75
SLIDE 75

Questions?

slide-76
SLIDE 76

JSON Graph and REST

[“videosById”, 512] [“genreLists”, 0, 0] [“genreLists”,{to:5},{to:5}, /videosById/512 /genreLists?col=0&row=0 /genreLists

slide-77
SLIDE 77

JSON Graph

  • Serializable Graph
  • Fine-Grained
slide-78
SLIDE 78

cache: { location: { address: “344 Seaside” } }}); var member = new falcor.Model({source: new falcor.HttpSource(“member.json”)});

Falcor Client

var member = new falcor.Model({ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }); (new falcor.HttpServer(member)). listen(80);

Falcor Server

member. get([“location”, “address”], (address) => <div>{address}></div>). subscribe(jsx => updateUI(jsx)); var member = new falcor.Model({ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }); (new falcor.HttpServer(member)). listen(80); , member. get([“location”,[“country”,“city”]], (address) => <div>{address}></div>). subscribe(jsx => updateUI(jsx)); location: { country: “US”, city: “Pacifica”, } location: { country: “US”, city: “Pacifica”, }

slide-79
SLIDE 79

/member.json

slide-80
SLIDE 80

A Simple Issue Tracking App

slide-81
SLIDE 81

Issue Status

Inconsistent data displayed Open Slow load times due to … Closed Caching in browser is slow.. Resolved Zip code is not validated Open Switching profiles does… Closed 500 error incorrectly… Resolved Template not updated… Open Font is incorrect on user… Resolved Dialog does not close… Open Open and Assigned To Me

Issue Status

Inconsistent data displayed Open Slow load times due to … Closed Caching in browser is slow.. Resolved Zip code is not validated Open Switching profiles does… Closed 500 error incorrectly… Resolved Template not updated… Open Font is incorrect on user… Resolved Dialog does not close… Open

Zip code is not validated

Currently we are not validating ZIP Codes and we are seeing a lot of invalid data in the database. Description Assigned To Kim Trott Status Open Lib added to build.

Issue Tracking

Lib added to build. Add Comment Kim Trott

slide-82
SLIDE 82

Issue Tracking: Domain Model

User Issue Comment

slide-83
SLIDE 83

Let’s talk about REST.

slide-84
SLIDE 84

REST: Issue Tracking End Points

/user/{userId} /issue/{issueId} /comment/{commentId} /user/{userId}/issues /issue/{issueId}/comments

slide-85
SLIDE 85

User Resource: /user/5232

{ name: “Kim Trott”, title: “Director”, twitter: “@alwayson”, email: “alwayson@netflix.com, // more fields… }

slide-86
SLIDE 86

Issue Resource: /issue/926

{ name: “Zip code is not validated”, description: “We need to apply…”, status: “Resolved”, dueDate: 956568342, // more fields… }

slide-87
SLIDE 87

Comment Resource: /comment/612

{ text: “What about the Canadians? Don’t we need to validate postal codes?”, user: “/user/5232” } { text: “What about the Canadians? Don’t we need to validate postal codes?”, user: “/user/5232” }

slide-88
SLIDE 88

User Issues: /users/5232/issues

[ “/issues/926”, “/issues/696”, “/issues/651”, “/issues/2239”, ]

slide-89
SLIDE 89

/issues/926/comments

[ “/comments/912”, “/comments/555”, “/comments/61”, “/comments/610 ]

slide-90
SLIDE 90

Issue Tracking with REST

Your Issues

Issue Status

Inconsistent … Open Zip code is… Open Caching in us... Resolved Test harness… Open Switching prof Closed 500 error not… Resolved Template not… Open Font is incorr… Resolved

slide-91
SLIDE 91

R#0: GET /users/532/issues? page=15 R#1: GET /issues/5412

db.issues.get(5412)

R#2: GET /issues/926

db.issues.get(926)

R#15: GET /issues/891

db.issues.get(891)

...

SELECT TOP 15 issues FROM user WHERE userId = 532

... ...

Your Issues

Issue Status

Inconsistent … Open Zip code is… Open Caching in us... Resolved Test harness… Open Switching prof Closed 500 error not… Resolved Template not… Open Font is incorr… Resolved

Issue Status

Inconsistent … Open Zip code is… Open Caching in us... Resolved Test harness… Open Switching prof Closed 500 error not… Resolved Template not… Open Font is incorr… Resolved

R#16: GET /issues/926

Zip code is not validated

Currently we are not validating ZIP Codes and we are seeing a lot

  • f invalid data in the

database. Description Assigned To Kim Trott Status Open Open Resolved Resolved

R#17: PUT/issues/926

db.issues.set( 891, { status: “Resolved”, // more properties ));

SQ L No SQ L

Issue Tracking with REST

Phone HTTP Cache

Your Issues

Issue Status

Inconsistent … Open Zip code is… Resolved Caching in us... Resolved Test harness… Open Switching prof Closed 500 error not… Resolved Template not… Open Font is incorr… Resolved

0 OK “/issues/5412”, “/issues/926”, “/issues/6293”, “/issues/6102”, “/issues/9232”, “/issues/16”, “/issues/0239”, “/issues/7623”, “/issues/2239”, “/issues/512”, “/issues/523”, “/issues/9823”, “/issues/615”, “/issues/2267”, “/issues/612”,

HTTP/1.0 200 OK name: “Inconsistent formatting applied to dates.”, description: “We need to apply…”, status: “Open”, dueDate: 956568342, // more fields…

slide-92
SLIDE 92

Phone HTTP Cache

REST: Cache Coherence

Issue DB

Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue

User DB

User Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue User

slide-93
SLIDE 93

REST = Laaaaaaaattteeennccccyy

slide-94
SLIDE 94

Overfetching with REST

{ name: “Zip code is not validated”, description: “We need to apply…”, status: “Resolved”, dueDate: 956568342, // more fields… } /issues/26343 description: “We need to apply…”,

slide-95
SLIDE 95

Issue Tracking with RPC

Your Issues

Issue Status

Inconsistent … Open Zip code is… Open Caching in us... Resolved Test harness… Open Switching prof Closed 500 error not… Resolved Template not… Open Font is incorr… Resolved

slide-96
SLIDE 96

RPC: Stale Caches

slide-97
SLIDE 97

RPC: Gerrymandered “Resource”

User Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue

issueList

slide-98
SLIDE 98

RPC: Tight Coupling

Your Issues

Issue Status

Inconsistent … Open Zip code is… Open Caching in us... Resolved Test harness… Open Switching prof Closed 500 error not… Resolved Template not… Open Font is incorr… Resolved

Issue Status Last Comment

Inconsistent forma… Open “Could we just…” Zip code is… Open “Canadian postal?” Caching in us... Resolved “Why manage it… Test harness… Open “Should take a while.” Switching profiles… Closed “Cut loading time…”

slide-99
SLIDE 99

REST

  • Cache Consistency
  • Loose Coupling

RPC

  • Small Message Size
  • Low Latency
slide-100
SLIDE 100
  • Distributed Architecture for Web Applications
  • Support Idempotent Operations and RPC calls
  • Maximize Cache Utility, Minimize Message Size
slide-101
SLIDE 101

How it works

slide-102
SLIDE 102

Issue Tracking with Falcor

Your Issues

Issue Status

Inconsistent … Open Zip code is… Open Caching in us... Resolved Test harness… Open Switching prof Closed 500 error not… Resolved Template not… Open Font is incorr… Resolved

slide-103
SLIDE 103
  • Distributed Architecture for Web Applications
  • Maximize Cache Utility, Minimize Message Sizes
  • Support for Idempotent Operations and RPC calls
slide-104
SLIDE 104

Most domain models are Graphs.

slide-105
SLIDE 105
slide-106
SLIDE 106

JSON Graph

var model = { user: { id: 5232, name: “Kim Trott”, // more props… issues: [ { id: 926 name: “Zip code is not validated”, // more props… assignedTo: { … } comments: [ { id: 612 text: “Is there a library?”, user: { … } }, // more comments… ] }, // more issues ] } } var model = new falcor.Model({cache: { user: { id: 5232, name: “Kim Trott”, // more properties… issues: [ { id: 926 name: “Zip code is not validated”, // more properties assignedTo: { … } comments: [ { id: 612 text: “Is there a library?”, user: { … } }, // more comments… ] }, // more issues ] }, }}); model[“user”][“issues”][0][“comments”][“0”][“text”]

slide-107
SLIDE 107

REST End Points

slide-108
SLIDE 108

REST

/issues/{id} /users/{id}

slide-109
SLIDE 109

yahoo.com nytimes.com/942 yahoo.com/banner.png slate.com slate.com/9565 reddit.com

Key Value

Browser Cache

GET slate.com GET slate.com PUT slate.com

Key Value slate.com <html><head…

GET slate.com/9565 GET slate.com/9565 PUT slate.com/9565

X

Key Value slate.com <html><head… Key Value slate.com <html><head… slate.com/9565 <html><head… Key Value slate.com <html><head… slate.com/9565 <html><head… Key Value slate.com <html><head… slate.com/9565 <html><head…

slide-110
SLIDE 110

The Power of Idempotence

GET /genreLists/0/name PUT /genreLists/0/0/rating 5

slide-111
SLIDE 111

PathSets

[“genreLists”, {from: 0, to: 5}, “titles”, {to:3}, [“name”, “rating”]]

slide-112
SLIDE 112
slide-113
SLIDE 113

How do we do it efficiently? 1.Batching 2.Caching 3.Path Optimization

slide-114
SLIDE 114

Most domain models are Graphs.

slide-115
SLIDE 115

How do we store a graph as JSON without duplicates?

slide-116
SLIDE 116

Introducing JSON Graph

slide-117
SLIDE 117

model[“genreLists”][0]["titlesList"][0][“name”] model[“genreLists”][0]["titlesList"][0][“name”] model.get([“genreLists”, 0, "titlesList”, 0, “name”]) model.get([“genreLists”, 0, "titlesList”, 0, “name”]) model.get([“genreLists”, 0, "titlesList”, 0, “name”]) model.get([“genreLists”, 0, "titlesList”, 0, “name”]) model.get([“genreLists”, 0, "titlesList”, 0, “name”]) model.get([“genreLists”, 0, "titlesList”, 0, “name”]) model.get([ “name”]) model.get([ “titlesById” ,956, “name”]) model.get([“titlesById”, 956, “name”]) model.get([“titlesById”, 956, “name”]) model.get([“titlesById”, 956, “name”]) model.get([“titlesById”, 956, “name”]) var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ { id: 956, name: "Friends", rating: 3 } ] }, { name: "New Releases", titlesList: [ { id: 956, name: "Friends", rating: 3 } ] } ] }

JSON

var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ { id: 956, name: "Friends", rating: 3 } ] }, { name: "New Releases", titlesList: [ { id: 956, name: "Friends", rating: 3 } ] } ] } model[“genreLists”][0]["titlesList"][0][“name”] var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ { id: 956, name: "Friends", rating: 3 } ] }, { name: "New Releases", titlesList: [ { id: 956, name: "Friends", rating: 3 } ] } ] } model[“genreLists”][0]["titlesList"][0][“name”] model[“genreLists”][0]["titlesList"][0][“rating”] = 5 var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ { id: 956, name: "Friends", rating: 3 } ] }, { name: "New Releases", titlesList: [ { id: 956, name: "Friends", rating: 3 } ] } ] } var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ { id: 956, name: "Friends", rating: 5 } ] }, { name: "New Releases", titlesList: [ { id: 956, name: "Friends", rating: 3 } ] } ] } model[“genreLists”][0]["titlesList"][0][“rating”] = 5 var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ { id: 956, name: "Friends", rating: 5 } ] }, { name: "New Releases", titlesList: [ { id: 956, name: "Friends", rating: 3 } ] } ] } var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ { id: 956, name: "Friends", rating: 5 } ] }, { name: "New Releases", titlesList: [ { id: 956, name: "Friends", rating: 3 } ] } ] } var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ ] }, { name: "New Releases", titlesList: [ ] } ] }

JSON Graph

var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ ] }, { name: "New Releases", titlesList: [ ] } ], titlesById: { “956”: { } } } { id: 956, name: "Friends", rating: 3 } { id: 956, name: "Friends", rating: 3 } { id: 956, name: "Friends", rating: 3 } { id: 956, name: "Friends", rating: 3 } var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ ] }, { name: "New Releases", titlesList: [ ] } ], titlesById: { “956”: { name: "Friends", rating: 3 } } } name: "Friends", rating: 3 var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ [“titlesById”, 956] ] }, { name: "New Releases", titlesList: [ [“titlesById”, 956] ] } ], titlesById: { “956”: { name: "Friends", rating: 3 } } } { id: 956, name: "Friends", rating: 3 } var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ [“titlesById”, 956] ] }, { name: "New Releases", titlesList: [ [“titlesById”, 956] ] } ], titlesById: { “956”: { name: "Friends", rating: 3 } } } var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ [“titlesById”, 956] ] }, { name: "New Releases", titlesList: [ [“titlesById”, 956] ] } ], titlesById: { “956”: { name: "Friends", rating: 3 } } } var model = { genreLists: { { name: "Suggestions For You" titlesList: [ [“titlesById”, 956] ] }, { name: "New Releases", titlesList: [ [“titlesById”, 956] ] } }, titlesById: { “956”: { name: "Friends", rating: 3 } } } var model = { genreLists: { “0”: { name: "Suggestions For You" titlesList: [ [“titlesById”, 956] ] }, “1”: { name: "New Releases", titlesList: [ [“titlesById”, 956] ] }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } } var model = { genreLists: { “0”: { name: "Suggestions For You" titlesList: { } }, “1”: { name: "New Releases", titlesList: { } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } } “0”: , length: 75 “0”: , length: 75 [“titlesById”, 956] [“titlesById”, 956] var model = { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } } var model = { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } } var model = { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } } var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); “titlesById”, 956 var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); model[“genreLists”][0]["titlesList"][0][“rating”] = 5 model.setValue(["genreLists", 0, "titlesList", 0, "rating"], 5) model.setValue(["genreLists", 0, "titlesList", 0, "rating"], 5) model.setValue([“genreLists”, 0, "titlesList”, 0, “rating”], 5) model.setValue([“genreLists”, 0, "titlesList”, 0, “rating”], 5) model.setValue([“genreLists”, 0, "titlesList”, 0, “rating”], 5) model.setValue([“genreLists”, 0, "titlesList”, 0, “rating”], 5) model.setValue([ “rating”], 5) model.setValue([ “titlesById” ,956, “rating”], 5) model.setValue([“titlesById”, 956, “rating”], 5) model.setValue([“titlesById”, 956, “rating”], 5) model.setValue([“titlesById”, 956, “rating”], 5) var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); “titlesById”, 956 var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }); var model = new falcor.Model({ cache: { genreLists: { “0”: { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, “1”: { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 5 } } }); model[“genreLists”][0]["titlesList"][0][“rating”] = 5

slide-118
SLIDE 118

var member = new falcor.Model({ location: { country: { $type: “sentinel”, $expires: 0, value: “US” } } }); (new falcor.HttpServer(member)). listen(80);

Expire Immediately

var member = new falcor.Model({ location: { country: { $type: “sentinel”, $expires: 1, value: “US” } } }); (new falcor.HttpServer(member)). listen(80);

Expire Never Expire at Absolute Time

var member = new falcor.Model({ location: { country: { $type: “sentinel”, $expires: 32648963422, value: “US” } } }); (new falcor.HttpServer(member)). listen(80); var member = new falcor.Model({ location: { country: { $type: “sentinel”, $expires: -20000, value: “US” } } }); (new falcor.HttpServer(member)). listen(80);

Expire at Relative Time

slide-119
SLIDE 119

Get as JSON

var member = new falcor.Model({ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }); { “json”:{ name: “Steve McGuire”, location: { address: “344 Seaside” } } } member.get( [“name”], [“location”, “address”]). subscribe(json => console.log(json));

slide-120
SLIDE 120

Get as Path Values

var member = new falcor.Model({ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }); {path: [“name”], value: “Steve McGuire”} {path: [“location”, “country”], value: “US”} member.get( [“name”], [“location”, “country”]). toPathValues() subscribe(pathValue => console.log(jpathValue));

slide-121
SLIDE 121

Get with Selector Function

var member = new falcor.Model({ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }); <div>Steve McGuire lives in US</div> member. get( [“name”], [“location”, “country”], (name, country) => <div>{name} lives in {country}</div>). subscribe(jsx => console.log(jsx));

slide-122
SLIDE 122

Get with Fast Selector Function

var member = new falcor.Model({ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }); <div>Steve McGuire lives in US</div> member. get( [“name”], [“location”, “country”], () => <div>{this.getValueSync([“name”)} lives in {this.getValueSync([“location”, “country”])}</div>). subscribe(jsx => console.log(jsx));

slide-123
SLIDE 123

Introducing Dense JSON

slide-124
SLIDE 124

Selector Fn Arguments get Dense JSON

member. get( [“location”, [“country”, “address”]], (location) => locationx). subscribe(json => console.log(json)); member. get( [“location”, [“country”, “address”]). subscribe(json => console.log(json)); { location: { country: “US”, address: “344 Seaside” } } { country: “US”, address: “344 Seaside” }

slide-125
SLIDE 125

{ location: { country: { $type: “sentinel”, $expires: 0, value: “US” } city: “Pacifica”, } }); var member = new falcor.Model( new falcor.HttpSource(“member.json”));

Falcor Client

var member = new falcor.Model({ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: { $type: “sentinel”, $expires: 0, value: “US” }, city: “Pacifica”, address: “344 Seaside” } }); (new falcor.HttpServer(member)). listen(80);

Falcor Server

, member. get([“location”,[“country”,“city”]], (address) => <div>{city},{country}></div>). subscribe(jsx => updateUI(jsx)); var member = new falcor.Model({ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }); (new falcor.HttpServer(member)). listen(80); var member = new falcor.Model({ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: { $type: “sentinel”, $expires: 0, value: “US” }, city: “Pacifica”, address: “344 Seaside” } }); (new falcor.HttpServer(member)). listen(80); location: { country: { $type: “sentinel”, $expires: 0, value: “US” } city: “Pacifica”, } { location: { city: “Pacifica”, } });

slide-126
SLIDE 126

What about REST/HTTP?

slide-127
SLIDE 127

Heavyweight HTTP

slide-128
SLIDE 128

Easy React Integration

1.Async Rendering 2.Fast Model Diffing

slide-129
SLIDE 129

render: function() { var jsx, jsxReceived = false; var model = new falcor.Model( new falcor.DataSource(“member.json”)); model.get( [“name”], [“location”, “city”], [“location”, “address”], (name, city, address) => { jsx = <p>{name} lives at <span>{address}, {city}</span></p>; if (!jsxReceived) this.forceUpdate(); }). toPromise(); if (jsx) { jsxReceived = true; return jsx; } else { return <img src=“progress.gif” />; } }

Async Component Render

Steve McGuire lives at 344 Seaside, Pacifica

render: function() { var jsx, jsxReceived = false; var model = new falcor.Model( new falcor.DataSource(“member.json”)); model.get( [“name”], [“location”, “city”], [“location”, “address”], (name, city, address) => { jsx = <p>{name} lives at <span>{address}, {city}</span></p>; if (!jsxReceived) this.forceUpdate(); }). toPromise(); if (jsx) { jsxReceived = true; return jsx; } else { return <img src=“progress.gif” />; } } render: function() { var jsx, jsxReceived = false; var model = new falcor.Model( new falcor.DataSource(“member.json”)); model.get( [“name”], [“location”, “city”], [“location”, “address”], (name, city, address) => { jsx = <p>{name} lives at <span>{address}, {city}</span></p>; if (!jsxReceived) this.forceUpdate(); }). toPromise(); if (jsx) { jsxReceived = true; return jsx; } else { return <img src=“progress.gif” />; } } render: function() { var jsx, jsxReceived = false; var model = new falcor.Model( new falcor.DataSource(“member.json”)); model.get( [“name”], [“location”, “city”], [“location”, “address”], (name, city, address) => { jsx = <p>{name} lives at <span>{address}, {city}</span></p>; if (!jsxReceived) this.forceUpdate(); }). toPromise(); if (jsx) { jsxReceived = true; return jsx; } else { return <img src=“progress.gif” />; } } renderData: function(name, city, address) { return <p>{name} lives at <span>{address}, {city}</span></p>; }, renderProgress: function() { return <img src=“progress.gif” />; } getPaths: function() { return [ [“name”], [“location”, “city”], [“location”, “address”] ]; }

slide-130
SLIDE 130

Fast Model Diffing

shouldComponentUpdate: function(nextProps, nextState, nextContext) { return (this.state.generation !== nextState.generation) || (this.state.key !== nextState.key); }

slide-131
SLIDE 131

New, more convenient API.

slide-132
SLIDE 132

One Model Everywhere

slide-133
SLIDE 133

JSON

var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ { id: 956, name: "Friends", rating: 3 } ] }, { name: "New Releases", titlesList: [ { id: 956, name: "Friends", rating: 3 } ] } ] } model[“genreLists”][0]["titlesList"][0][“name”] var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ { id: 956, name: "Friends", rating: 3 } ] }, { name: "New Releases", titlesList: [ { id: 956, name: "Friends", rating: 3 } ] } ] } model[“genreLists”][0]["titlesList"][0][“name”] model[“genreLists”][0]["titlesList"][0][“rating”] = 5 var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ { id: 956, name: "Friends", rating: 3 } ] }, { name: "New Releases", titlesList: [ { id: 956, name: "Friends", rating: 3 } ] } ] } var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ { id: 956, name: "Friends", rating: 5 } ] }, { name: "New Releases", titlesList: [ { id: 956, name: "Friends", rating: 3 } ] } ] } model[“genreLists”][0]["titlesList"][0][“rating”] = 5 var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ { id: 956, name: "Friends", rating: 5 } ] }, { name: "New Releases", titlesList: [ { id: 956, name: "Friends", rating: 3 } ] } ] } var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ { id: 956, name: "Friends", rating: 5 } ] }, { name: "New Releases", titlesList: [ { id: 956, name: "Friends", rating: 3 } ] } ] } var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ ] }, { name: "New Releases", titlesList: [ ] } ] }

JSON Graph

var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ ] }, { name: "New Releases", titlesList: [ ] } ], titlesById: { “956”: { } } } { id: 956, name: "Friends", rating: 3 } { id: 956, name: "Friends", rating: 3 } { id: 956, name: "Friends", rating: 3 } { id: 956, name: "Friends", rating: 3 } { id: 956, name: "Friends", rating: 3 } var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ ] }, { name: "New Releases", titlesList: [ ] } ], titlesById: { “956”: { name: "Friends", rating: 3 } } } name: "Friends", rating: 3 var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ [“titlesById”, 956] ] }, { name: "New Releases", titlesList: [ [“titlesById”, 956] ] } ], titlesById: { “956”: { name: "Friends", rating: 3 } } } { id: 956, name: "Friends", rating: 3 } var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ [“titlesById”, 956] ] }, { name: "New Releases", titlesList: [ [“titlesById”, 956] ] } ], titlesById: { “956”: { name: "Friends", rating: 3 } } } var model = { genreLists: [ { name: "Suggestions For You" titlesList: [ [“titlesById”, 956] ] }, { name: "New Releases", titlesList: [ [“titlesById”, 956] ] } ], titlesById: { “956”: { name: "Friends", rating: 3 } } } var model = { genreLists: { { name: "Suggestions For You" titlesList: [ [“titlesById”, 956] ] }, { name: "New Releases", titlesList: [ [“titlesById”, 956] ] } }, titlesById: { “956”: { name: "Friends", rating: 3 } } } var model = { genreLists: { “0”: { name: "Suggestions For You" titlesList: [ [“titlesById”, 956] ] }, “1”: { name: "New Releases", titlesList: [ [“titlesById”, 956] ] }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } } var model = { genreLists: { “0”: { name: "Suggestions For You" titlesList: { } }, “1”: { name: "New Releases", titlesList: { } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } } “0”: , length: 75 “1”: , length: 75 [“titlesById”, 956] [“titlesById”, 956] var model = { genreLists: { { name: "Suggestions For You" titlesList: { “0”: [“titlesById”, 956], length: 75 } }, { name: "New Releases", titlesList: { “0”: [“titlesById”, 956], length: 75 } }, length: 50 }, titlesById: { “956”: { name: "Friends", rating: 3 } } }

slide-134
SLIDE 134
slide-135
SLIDE 135

In the last 10 years, the web has transformed.

slide-136
SLIDE 136

In the last 10 years, the web has transformed.

slide-137
SLIDE 137

Issue Tracking: REST End Points

/user/{userid} /issue/{issueid} /comment/{commentid} /user/{userid}/issues /issue/{issueid}/comments

slide-138
SLIDE 138

Every user wants to believe that the entire cloud is on their device.

slide-139
SLIDE 139

R#0: GET /genreLists/124 ?pageSize=15 R#1: GET /titles/5412

titleDB.get(5412)

R#2: GET /titles/926

titleDB.get(926)

R#15: GET /titles/891

titleDB.get(891)

...

userDB.lists. get(124). top(15);

... ...

Users Titles

Why not REST?

Phone HTTP Cache

R#15: GET /titles/926

slide-140
SLIDE 140

R#0: GET /issueList? page=15 userDB.lists.get(532) Users Titles

Netflix with RPC

Memory Cache

titleDB.get([ 5412, 926…612 ], “name,status”);

Phone HTTP Cache
slide-141
SLIDE 141

HTTP/REST is for Large Resources

The REST interface is designed to be efficient for large-grain hypermedia data transfer…resulting in an interface that is not optimal for other forms

  • f architectural interaction.
  • Dr Roy T Fielding
slide-142
SLIDE 142

var member = { name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }}; print(member[“location”][“address”]);

JSON

var member = new falcor.Model({cache:{ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }}); print(member[“location”][“address”]);

Falcor

var member = { name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }; print(member[“location”][“address”]); var member = new falcor.Model({cache:{ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }}); member.get( [“location”, “address”], (address) => print(address)). toPromise();

Falcor Server

var member = new falcor.Model({cache:{ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }}); member.get( [“location”, “address”], (address) => print(address)). toPromise(); var member = new falcor.Model({cache:{ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }}); (new falcor.HttpServer(member)). listen(80); var member = new falcor.Model({source: new falcor.HttpSource(“member.json”)}); member.get( [“location”, “address”], (address) => print(address)). toPromise(); member.get( [“location”, “address”]). toPromise(). then(json => response.write(json)); [“location”][“address”]

slide-143
SLIDE 143

var member = new falcor.Model({source: new falcor.HttpSource(“member.json”)}); member.get( [“location”, “address”], (address) => print(address)). toPromise();

Falcor

var member = new falcor.Model({cache:{ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }}); (new falcor.HttpServer(member)). listen(80);

Falcor Server

var member = new falcor.Model({cache:{ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }}); (new falcor.HttpServer(member)). listen(80);

Virtual Falcor Server

var member = new falcor.Model({ router: new falcor.Router([ { route: [“member”,[“name”,”occupation”]], get: (pathSet) => }, { route: [“member”,“location”,[“country”, ”city”, “address”]], get: (pathSet) => } ]) }); (new falcor.HttpServer(member)).listen(80);

var member = new falcor.Model({cache:{ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }}); (new falcor.HttpServer(member)). listen(80);

var member = new falcor.Model({ { route: [“member”,[“name”,”occupation”]], get: (pathSet) => memberDB. exec(`SELECT ${pathSet[1].join(‘,’)} FROM user WHERE id = ${request.cookies.userid}`) }, { route: [“member”,“location”,[“country”, ”city”, “address”]], get: (pathSet) => } ]) }); (new falcor.HttpServer(member)).listen(80); var member = new falcor.Model({ { route: [“member”,[“name”,”occupation”]], get: (pathSet) => memberDB. exec(`SELECT ${pathSet[1].join(‘,’)} FROM user WHERE id = ${request.cookies.userid}`) }, { route: [“member”,“location”,[“country”, ”city”, “address”]], get: (pathSet) => locationServer. getLocation(request.cookies.userid). then(location => ({ member: { location: getProps(location, pathSet[2]) } }) } ]) }); (new falcor.HttpServer(member)).listen(80);

slide-144
SLIDE 144

cache: { location: { address: “344 Seaside” } }}); var member = new falcor.Model({source: new falcor.HttpSource(“member.json”)});

Falcor Client

var member = new falcor.Model({ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }); (new falcor.HttpServer(member)). listen(80);

Falcor Server

member. get([“location”, “address”]). toPromise(); var member = new falcor.Model({ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }); (new falcor.HttpServer(member)). listen(80); , member. get([“location”,[“country”,“city”]]). toPromise(); location: { country: “US”, city: “Pacifica”, } location: { country: “US”, city: “Pacifica”, }

slide-145
SLIDE 145

cache: { location: { address: “344 Seaside” } }}); var member = new falcor.Model({source: new falcor.HttpSource(“member.json”)});

Falcor Client

var member = new falcor.Model({ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }); (new falcor.HttpServer(member)). listen(80);

Falcor Server

member. get([“location”, “address”], (address) => <div>{address}></div>). subscribe(jsx => updateUI(jsx)); var member = new falcor.Model({ name: “Steve McGuire”,

  • ccupation: “Developer”,

location: { country: “US”, city: “Pacifica”, address: “344 Seaside” } }); (new falcor.HttpServer(member)). listen(80); , member. get([“location”,[“country”,“city”]], (address) => <div>{address}></div>). subscribe(jsx => updateUI(jsx)); location: { country: “US”, city: “Pacifica”, } location: { country: “US”, city: “Pacifica”, }

slide-146
SLIDE 146

Virtual Model Path Evaluation

var member = new falcor.Model({ router: new falcor.Router([ { route: ["genreLists", Router.INTEGERS, Router.INTEGERS], pathSet => { /* retrieve data from lists DB */}, }, { route: [“titlesById”, Router.INTEGERS, [“name”, “rating”]], pathSet => {{ /* retrieve data from titles DB */} } ]) }); var member = new falcor.Model({ source: new falcor.HttpSource('member.json’)}); member. getValue([“genreLists”, 0, 0, “name”]). toPromise(); member.get(["genreLists", 0, 0, "name"]) var member = new falcor.Model({ router: new falcor.Router([ { route: ["genreLists", Router.INTEGERS, Router.INTEGERS], ([“genreList”],[0],[0]]) => { /* retrieve data from lists DB */}, }, { route: [“titlesById”, Router.INTEGERS, [“name”, “rating”]], pathSet => {{ /* retrieve data from titlesDB */} } ]) }); var member = new falcor.Model({ router: new falcor.Router([ { route: ["genreLists", Router.INTEGERS, Router.INTEGERS], pathSet => { /* retrieve data from lists DB */}, }, { route: [“titlesById”, Router.INTEGERS, [“name”, “rating”]], pathSet => {{ /* retrieve data from titles DB */} } ]), cache: { genreLists: { “0”: { “0”: [“titlesById”, 926] } } } }); var member = new falcor.Model({ router: new falcor.Router([ { route: ["genreLists", Router.INTEGERS, Router.INTEGERS], pathSet => { /* retrieve data from lists DB */}, }, { route: [“titlesById”, Router.INTEGERS, [“name”, “rating”]], pathSet => {{ /* retrieve data from titles DB */} } ]), cache: { genreLists: { “0”: { “0”: [“titlesById”, 926] } } } }); member.get(["genreLists", 0, 0, "name"]) var member = new falcor.Model({ router: new falcor.Router([ { route: ["genreLists", Router.INTEGERS, Router.INTEGERS], pathSet => { /* retrieve data from lists DB */}, }, { route: [“titlesById”, Router.INTEGERS, [“name”, “rating”]], pathSet => {{ /* retrieve data from titles DB */} } ]), cache: { genreLists: { “0”: { “0”: [“titlesById”, 926] } } } }); “titlesById”, 926 { genreLists: { “0”: { “0”: [“titlesById”, 926] } } } member.get([“titlesById”, 926, “name”]) var member = new falcor.Model({ router: new falcor.Router([ { route: ["genreLists", Router.INTEGERS, Router.INTEGERS], pathSet => { /* retrieve data from lists DB */}, }, { route: [“titlesById”, Router.INTEGERS, [“name”, “rating”]], ([“titlesById”, [926], [“aname”]]) => {{ /* retrieve data */} } ]), cache: { genreLists: { “0”: { “0”: [“titlesById”, 926] } } } }); var member = new falcor.Model({ router: new falcor.Router([ { route: ["genreLists", Router.INTEGERS, Router.INTEGERS], pathSet => { /* retrieve data from lists DB */}, }, { route: [“titlesById”, Router.INTEGERS, [“name”, “rating”]], pathSet => {{ /* retrieve data from titles DB */} } ]), cache: { genreLists: { “0”: { “0”: [“titlesById”, 926] } }, titlesById: { “926”: { name: “Friends” } } } }); var member = new falcor.Model({ router: new falcor.Router([ { route: ["genreLists", Router.INTEGERS, Router.INTEGERS], pathSet => { /* retrieve data from lists DB */}, }, { route: [“titlesById”, Router.INTEGERS, [“name”, “rating”]], pathSet => {{ /* retrieve data from titles DB */} } ]), cache: { genreLists: { “0”: { “0”: [“titlesById”, 926] } }, titlesById: { “926”: { name: “Friends” } } } }); var member = new falcor.Model({ router: new falcor.Router([ { route: ["genreLists", Router.INTEGERS, Router.INTEGERS], pathSet => { /* retrieve data from lists DB */}, }, { route: [“titlesById”, Router.INTEGERS, [“name”, “rating”]], pathSet => {{ /* retrieve data from titles DB */} } ]), cache: { genreLists: { “0”: { “0”: [“titlesById”, 926] } }, titlesById: { “926”: { name: “Friends” } } } }); member.get([“titlesById”, 926, “name”]) { titlesById: { “926”: { “name”: “Friends” } } } cache: { genreLists: { “0”: { “0”: [“titlesById”, 926] } }, titlesById: { “926”: { name: “Friends” } } } var member = new falcor.Model({ source: new falcor.HttpSource('member.json’), cache: { genreLists: { “0”: { “0”: [“titlesById”, 926] } }, titlesById: { “926”: { name: “Friends” } } }); member. getValue([“genreLists”, 0, 0, “name”]). toPromise(); var member = new falcor.Model({ router: new falcor.Router([ { route: ["genreLists", Router.INTEGERS, Router.INTEGERS], pathSet => { /* retrieve data from lists DB */}, }, { route: [“titlesById”, Router.INTEGERS, [“name”, “rating”]], pathSet => {{ /* retrieve data from titles DB */} } ]); “titlesById”, 926