falcor
play

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


  1. Virtual Falcor Server Falcor Falcor Server var member = new falcor.Model({source: var member = new falcor.Model({cache:{ var member = new falcor.Model({cache:{ var member = new falcor.Model({cache:{ var member = new falcor.Model({ var member = new falcor.Model({ var member = new falcor.Model({ router: new falcor.Router([ new falcor.HttpSource( “ member.json ”) }); name: “Steve McGuire” , name: “Steve McGuire”, name: “Steve McGuire”, { { { occupation : “Developer”, occupation: “Developer”, occupation: “Developer”, route: [“member”,[“name”,”occupation”]] , route: [“member”,[“name”,”occupation”]] , route: [“member”,[“name”,”occupation”]] , get: (pathSet) => get: (pathSet) => get: (pathSet) => location: { location: { location: { memberDB . memberDB . country: “US”, country: “US”, country: “US”, exec(`SELECT ${pathSet [1].join(‘,’)} exec(`SELECT ${pathSet [1].join(‘,’) } city: “Pacifica”, city: “Pacifica”, city: “Pacifica”, FROM user FROM user WHERE id = ${request.cookies.userid}`) WHERE id = ${request.cookies.userid}`) address: “344 Seaside” address: “344 Seaside” address: “344 Seaside” }, }, }, } } } { { { route: [“member”,“location”,[“country”, ”city”, “address”]] , route: [“member”,“location”,[“country”, ”city”, “address”]] , route: [“member”,“location”,[“country”, ”city”, “address”]] , }}); }}); }}); get: (pathSet) => get: (pathSet) => get: (pathSet) => locationServer. member.get ( [“location”, “ address ”], (new falcor.HttpServer(member)). (new falcor.HttpServer(member)). (new falcor.HttpServer(member)). getLocation(request.cookies.userid). then(location => ({ (address) => print(address)). listen(80); listen(80); listen(80); member: { location: getProps(location, pathSet[2]) } toPromise(); }) } } } ]) ]) ]) }); }); }); (new falcor.HttpServer(member)).listen(80); (new falcor.HttpServer(member)).listen(80); (new falcor.HttpServer(member)).listen(80);

  2. How do we do it efficiently?

  3. Falcor Optimization • Batching • Caching • Path Optimization

  4. Building Netflix with Falcor

  5. Defining a Model

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

  7. We’re almost ready to move our model into the cloud. http://netflix.com/ member.json

  8. There’s just one problem…

  9. Netflix’s Domain Model is a Graph

  10. JSON is for Trees.

  11. When we convert a graph to a JSON we get duplicates. “Suggestions for You” Genre Lists “New Releases”

  12. Introducing JSON Graph

  13. JSON Graph • Graph format for JSON • Tree with Symbolic Links • Every value type is a resource

  14. JSON JSON Graph var model = { var model = { var model = { var model = { var model = { var model = { var model = { var model = { var model = new falcor.Model({ cache: { var model = new falcor.Model({ cache: { var model = new falcor.Model({ cache: { var model = { var model = { var model = new falcor.Model({ cache: { var model = new falcor.Model({ cache: { var model = new falcor.Model({ cache: { var model = { var model = { var model = { var model = { var model = new falcor.Model({ cache: { var model = new falcor.Model({ cache: { var model = { var model = new falcor.Model({ cache: { var model = new falcor.Model ({ cache: { var model = { var model = { var model = new falcor.Model({ cache: { var model = new falcor.Model({ cache: { var model = new falcor.Model({ cache: { var model = new falcor.Model({ cache: { var model = new falcor.Model({ cache: { var model = { var model = { var model = new falcor.Model({ cache: { genreLists : [ genreLists : [ genreLists: [ genreLists : [ genreLists: [ genreLists: [ genreLists: [ genreLists: { genreLists : { genreLists : { genreLists : { genreLists : { genreLists: { genreLists: { genreLists: [ genreLists: { genreLists : { genreLists: [ genreLists: [ genreLists : { genreLists: { genreLists : { genreLists: { genreLists : { genreLists: { genreLists: [ genreLists : { genreLists : { genreLists: [ genreLists: { genreLists: { genreLists : { genreLists: [ genreLists : { genreLists : { { { { { { { { “0”: { “0”: { { “0”: { “0”: { { { “0”: { “0” : { { “0”: { “0” : { “0” : { “0”: { “0” : { “0” : { “0” : { “0” : { { { “0” : { “0”: { “0” : { “0”: { { “0”: { “0” : { “0” : { name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" name: "Suggestions For You" titlesList: [ titlesList: [ titlesList: [ titlesList: [ titlesList: [ titlesList: [ titlesList: [ titlesList: [ titlesList: [ titlesList: { titlesList: { titlesList : { titlesList : { titlesList: { titlesList: { titlesList: { titlesList: [ titlesList : { titlesList : { titlesList : { titlesList : { titlesList: [ titlesList: { titlesList: [ titlesList: { titlesList: { titlesList : { titlesList: { titlesList: [ titlesList: [ titlesList: [ titlesList : { titlesList : { titlesList: { titlesList: { { { { { { { { “0” : [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , “0” : [“ titlesById ”, 956] , [“ titlesById ”, 956] [“ titlesById ”, 956] “0” : [“ titlesById ”, 956] , “0” : [“ titlesById ”, 956] , [“ titlesById ”, 956] “0” : [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , “0” : [“ titlesById ”, 956] , “0” : [“ titlesById ”, 956] , { “0”: [“ titlesById ”, 956] , [“ titlesById ”, 956] “0”: [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , “0”: , { “0”: [“ titlesById ”, 956] , [ “ titlesById ”, 956 ] “0” : [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , [“ titlesById ”, 956] “0” : [“ titlesById ”, 956] , “ titlesById ”, 956 “ titlesById ”, 956 id: 956, id: 956, id: 956, id: 956, id: 956, id: 956, id: 956, length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 id: 956, id: 956 , name: "Friends", name: "Friends", name: "Friends", name: "Friends", name: "Friends" , name: "Friends", name: "Friends", } } } } } } } } } } } } } } } } } name: "Friends", name: "Friends", rating: 5 rating: 5 rating: 3 rating: 3 rating: 5 rating: 3 rating: 3 }, }, }, }, }, }, }, }, }, }, }, }, }, }, }, }, }, rating: 3 rating: 3 } } } } } } } “1”: { “1”: { “1”: { “1”: { “1”: { “1”: { “1”: { “1”: { “1”: { “1”: { “1”: { “1”: { “1”: { “1”: { “1”: { “1”: { “1”: { } } { ] ] ] ] ] ] ] name: "New Releases", name: "New Releases", } name: "New Releases", ] } name: "New Releases", name: "New Releases", name: "New Releases", name: "New Releases", name: "New Releases", name: "New Releases", } ] name: "New Releases", name: "New Releases", ] ] ] name: "New Releases", name: "New Releases", ] name: "New Releases", name: "New Releases", name: "New Releases", name: "New Releases", ] ] id: 956, }, }, }, } , } , }, } , }, }, }, }, }, }, }, }, }, }, }, titlesList: { titlesList: { titlesList: { titlesList: { titlesList: { titlesList: { titlesList: { titlesList: { titlesList: { titlesList: { titlesList: { titlesList: { titlesList: { titlesList: { titlesList: { titlesList: { titlesList: { name: "Friends", name: "Friends", { { { { { { { { “1”: { “1”: { { { { { “1”: { { “1”: { { “0”: [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , rating: 3 rating: 3 name: "New Releases", name: "New Releases", name: "New Releases", name: "New Releases", name: "New Releases", name: "New Releases", name: "New Releases", name: "New Releases", name: "New Releases", name: "New Releases", name: "New Releases", name: "New Releases", name: "New Releases", name: "New Releases", name: "New Releases", name: "New Releases", name: "New Releases", name: "New Releases", length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 length: 75 } titlesList: [ titlesList: [ titlesList: [ titlesList: [ titlesList: [ titlesList: [ titlesList: [ titlesList: [ } } } titlesList: [ } titlesList: [ } } } } } titlesList: [ } titlesList: [ titlesList: { } } } titlesList: [ } } } titlesList: { titlesList: [ titlesList: { titlesList: [ } { { { { { { { { { }, }, }, }, }, }, }, }, }, }, }, }, }, }, }, }, }, [“ titlesById ”, 956] [ “ titlesById ”, 956 ] [“ titlesById ”, 956] [“ titlesById ”, 956] [“ titlesById ”, 956] “0”: , [“ titlesById ”, 956] “0”: [“ titlesById ”, 956] , “0”: [“ titlesById ”, 956] , id: 956 , id: 956, id: 956, id: 956, id: 956, id: 956, id: 956, id: 956, id: 956, length: 50 length: 50 length: 50 length: 50 length: 50 length: 50 length: 50 length: 50 length: 50 length: 50 length: 50 length: 50 length: 50 length: 50 length: 50 length: 50 length: 50 length: 75 length : 75 length: 75 name: "Friends", name: "Friends", name: "Friends", name: "Friends", name: "Friends", name: "Friends", name: "Friends", name: "Friends", name: "Friends", }, }, }, }, }, }, }, }, }, }, }, }, }, }, }, }, }, rating: 3 rating: 3 rating: 3 rating: 3 rating: 3 rating: 3 rating: 3 rating: 3 rating: 3 titlesById : { titlesById : { titlesById : { titlesById : { titlesById: { titlesById : { titlesById : { titlesById: { titlesById: { titlesById : { titlesById: { titlesById: { titlesById: { titlesById: { titlesById: { titlesById: { titlesById: { } } } } } } } } } “956”: { “956”: { “956” : { “956”: { “956”: { “956” : { “956” : { “956”: { “956”: { “956” : { “956” : { “956”: { “956”: { “956”: { “956”: { “956”: { “956”: { ] ] ] ] ] ] ] } ] name: "Friends", ] name: "Friends", name : "Friends", name: "Friends", name: "Friends", ] ] ] name: "Friends", name: "Friends", ] ] } name: "Friends", name: "Friends", name: "Friends", name: "Friends", ] name: "Friends", name: "Friends", } name: "Friends", name: "Friends", name: "Friends", name: "Friends", } } } } } } } } }, }, } } } } } }, }, } rating: 3 rating: 3 rating: 3 rating: 3 rating: 3 rating: 3 rating: 3 rating: 3 rating : 5 rating: 3 rating: 3 rating: 3 rating: 3 rating: 3 rating: 3 rating : 3 rating: 3 length: 50 } } } length: 50 } } } } } } } } } } length: 50 } length: 50 } } } ] ] ] ] ] ] ] ] } } } } } } } } } }, } }, ], } } }, ], } ] , } , } } } , } } ], ], } } } } } } } }); }); }); }); }); }); }); }); } } }); }); }); }); } ); }); }); }); titlesById: { titlesById : { titlesById: { titlesById: { titlesById: { titlesById: { titlesById: { titlesById: { titlesById: { titlesById: { “956” : { “956”: { “956”: { “956”: { “956”: { “956”: { “956”: { “956”: { “956”: { “956”: { name: "Friends", name: "Friends", name: "Friends", name: "Friends", name: "Friends", name: "Friends", name: "Friends", name: "Friends", name: "Friends", rating: 3 rating: 3 rating: 3 rating: 3 rating: 3 rating: 3 rating: 3 rating: 3 rating: 3 } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } model[“ genreLists ”][0][" titlesList "][0][“rating”] = 5 model.get([ “ titlesById ” , 956 , “name” ]) model.get([ “ genreLists ” , 0 , "titlesList ” , 0 , “name”]) model.get([ “ titlesById ” , 956 , “name”]) model.get([ “ titlesById ” , 956, “name”]) model.get([ model.get([ 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 ([“ titlesById ”, 956, “name”]) model[“ genreLists ”][0][" titlesList "][0][“name”] “ titlesById ” ,956, “name”]) “name”]) model[“ genreLists ”][0][" titlesList "][0][“name”] model[“ genreLists ”][0][" titlesList "][0][“name”] model[ “ genreLists ” ][ 0 ][ "titlesList" ][ 0 ][ “name” ] model[“ genreLists ”][0][" titlesList "][0][“rating”] = 5 model[ “ genreLists ” ][ 0 ][ "titlesList" ][ 0 ][ “rating” ] = 5 model[“ genreLists ”][0][" titlesList "][0][“rating”] = 5 model.setValue([ “ titlesById ” , 956 , “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([ model.setValue([ model.setValue([ “ titlesById ” , 956, “rating”], 5) model.setValue([ “ titlesById ” , 956 , “rating” ], 5) “ titlesById ” ,956, “rating”], 5) “rating”], 5)

  15. 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 } } }

  16. Fine-Grained Resources JSON Graph HTTP/REST [“ genreLists ”, “length”] /genreLists [“ genreLists ”, *, name] [“ titlesById ”, 23432, “name”] /titles/23432 [“ titlesById ”, 23432, “rating” ] [“ titlesById ”, 23432, “description” ] [“ titlesById ”, 23432, “bookmark” ] [“ titlesById ”, 23432, “director” ] // more fields

  17. Idempotent Operations Falcor Model HTTP/REST get GET set PUT

  18. Building a Virtual Model http://netflix.com/ member.json

  19. 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 } } ]) });

  20. 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 }; }); } ]) });

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

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

  23. The World Wide Web is Flat

  24. The Type of the WWW Map<String, Resource>

  25. The WWW is Flat GET http://../genreLists/0/0/name 302 http://../titles/23432/name GET http://../titles/23432/name “Friends”

  26. The WWW is Flat (continued) GET http://../genreLists/0/0/rating 302 http://../titles/23432/rating GET http://../titles/23432/rating 5.0

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

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

  29. Non Idempotent Operations model.call( [“genreList”,0,”add”], [[“titles”, 234234]])

  30. Function Calls • Can invoke function that lives anywhere in Graph • Functions can only be called, not downloaded • Functions can invalidate any resource in cache

  31. Falcor • Cache Consistency • Loose Coupling • Low Latency • Small Message Sizes

  32. 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

  33. Three JavaScript UIs Mobile.js Web.js TV.js

  34. Open-Source Soon • Should be available in the next few months • Looking for help integrating with existing MVC frameworks • @falcorjs

  35. Questions?

  36. JSON Graph and REST [“ videosById ”, 512] /videosById/512 [“ genreLists ”, 0, 0] /genreLists?col=0&row=0 [“ genreLists ” ,{to:5},{to:5}, /genreLists

  37. JSON Graph • Serializable Graph • Fine-Grained

  38. Falcor Client Falcor Server var member = new falcor.Model({source: , new falcor.HttpSource( “ member.json ”)}) ; cache: { var member = new falcor.Model({ var member = new falcor.Model({ name: “Steve McGuire”, name: “Steve McGuire”, occupation: “Developer”, occupation: “Developer”, location: { location: { location: { location: { location: { country: “US”, country: “US”, country: “US”, country: “US”, city: “Pacifica”, city: “Pacifica”, city: “Pacifica”, city: “Pacifica”, address: “344 Seaside ” address: “344 Seaside” address: “344 Seaside” } } } } } }); }); }}); member. member. (new falcor.HttpServer(member)). (new falcor.HttpServer(member)). get( [“location”, “address”] , get( [“location”,[“country”,“city”]] , listen(80); listen(80); (address) => <div>{address}></div>). (address) => <div>{address}></div>). subscribe(jsx => updateUI(jsx)); subscribe(jsx => updateUI(jsx));

  39. / member.json

  40. A Simple Issue Tracking App

  41. Issue Tracking Zip code is not validated Open and Assigned To Me Issue Issue Status Status Description Inconsistent data displayed Inconsistent data displayed Open Open Currently we are not Slow load times due to … Slow load times due to … Closed Closed validating ZIP Codes and we are seeing a lot of Caching in browser is slow.. Caching in browser is slow.. Resolved Resolved invalid data in the Zip code is not validated Zip code is not validated Open Open database. Lib added to build. Lib added to build. Switching profiles does… Switching profiles does… Closed Closed Kim Trott Assigned To 500 error incorrectly… 500 error incorrectly… Resolved Resolved Kim Trott Template not updated… Template not updated… Open Open Font is incorrect on user… Font is incorrect on user… Resolved Resolved Status Add Comment Dialog does not close… Dialog does not close… Open Open Open

  42. Issue Tracking: Domain Model User Issue Comment

  43. Let’s talk about REST.

  44. REST: Issue Tracking End Points /user/{userId} /issue/{issueId} /comment/{commentId} /user/{userId}/issues /issue/{issueId}/comments

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

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

  47. Comment Resource: /comment/612 { { text: “ W hat about the Canadians? Don’t we need to text: “What about the Canadians? Don’t we need to validate postal codes?”, validate postal codes?”, user: “/user/5232” user: “ /user/5232 ” } }

  48. User Issues: /users/5232/issues [ “/issues/926”, “/issues/696”, “/issues/651”, “/issues/2239”, ]

  49. /issues/926/comments [ “/comments/912”, “/comments/555”, “/comments/61”, “/comments/610 ]

  50. 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

  51. Issue Tracking with REST 0 OK “/issues/5412”, No Phone HTTP/1.0 200 OK “/issues/926”, SQ SQ HTTP L Cache L “/issues/6293”, “/issues/6102”, SELECT TOP 15 issues “/issues/9232”, FROM user R#0: GET /users/532/issues? WHERE page=15 “/issues/16”, userId = 532 name: “Inconsistent formatting “/issues/0239”, applied to dates.” , “/issues/7623”, Your Issues Your Issues Zip code is not validated R#1: GET /issues/5412 “/issues/2239”, description: “We need to apply…”, db.issues.get(5412) “/issues/512” , Description Issue Issue Issue Status Status Status status: “Open” , “/issues/523” , Currently we are not Inconsistent … Inconsistent … “/issues/9823” , R#2: GET /issues/926 Open Open dueDate: 956568342, Inconsistent … Open db.issues.get(926) validating ZIP Codes “/issues/615” , Zip code is… Zip code is… Open Open and we are seeing a lot Zip code is… Resolved // more fields… ... ... ... “/issues/2267” , of invalid data in the Caching in us... Caching in us... Resolved Resolved Caching in us... Resolved database. “/issues/612”, Test harness… Test harness… Open Open R#15: GET /issues/891 Test harness… Open db.issues.get(891) Assigned To Switching prof Switching prof Closed Closed Switching prof Closed Kim Trott 500 error not… 500 error not… Resolved Resolved db.issues. set ( 500 error not… Resolved R#16: GET /issues/926 Template not… Template not… Open Open 891, Status Template not… { Open Font is incorr … Font is incorr … Resolved Resolved status: “Resolved”, Resolved Open Font is incorr … // more properties Resolved R#17: PUT /issues/926 Open )); Resolved

  52. REST: Cache Coherence User DB Phone HTTP Cache User User Issue DB Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue Issue

  53. REST = Laaaaaaaattteeennccccyy

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

  55. 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

  56. RPC: Stale Caches

  57. RPC: Gerrymandered “Resource” Issue Issue Issue Issue User Issue issueList Issue Issue Issue Issue Issue Issue Issue

  58. RPC: Tight Coupling Your Issues Issue Status Inconsistent … Open Issue Status Last Comment Zip code is… Open Inconsistent forma… “Could we just…” Open Caching in us... Resolved Zip code is… “Canadian postal?” Open Test harness… Open “Why manage it… Caching in us... Resolved Switching prof Closed Test harness… “Should take a Open 500 error not… Resolved while.” Template not… Open Switching profiles… “Cut loading time…” Closed Font is incorr … Resolved

  59. REST RPC • Cache Consistency • Small Message Size • Loose Coupling • Low Latency

  60. • Distributed Architecture for Web Applications • Support Idempotent Operations and RPC calls • Maximize Cache Utility, Minimize Message Size

More recommend