Embracing Change - MBLTDev 2015

D200a17dd269fd4001bacb11662dab4b?s=47 Kyle Fuller
November 17, 2015

Embracing Change - MBLTDev 2015

Building applications can quickly become frustrating when you need to adapt to business changes. You'll often need to re-write portions of your application. Learn how you can avoid deploying updates to your application by decoupling your application from the web API. Transform your app to be simpler and more robust by keeping business logic where it belongs, on the server.

D200a17dd269fd4001bacb11662dab4b?s=128

Kyle Fuller

November 17, 2015
Tweet

Transcript

  1. EMBRACING CHANGE KYLE FULLER

  2. None
  3. None
  4. POLLS

  5. None
  6. None
  7. None
  8. None
  9. None
  10. None
  11. HOW DOES IT WORK?

  12. GET /questions

  13. DELETE /questions/{id}

  14. GET /questions/{id}

  15. POST /questions/{id}/choices/{choice_id}

  16. POST /questions

  17. APPLICATION WAS BUILT FROM AN OUT-OF-BAND SPECIFICATION

  18. TIGHT COUPLING

  19. WE WANT TO CHANGE SOMETHING.

  20. YOUR BOSS FINDS YOU LATE ON A FRIDAY AFTERNOON

  21. None
  22. I'D LIKE TO...

  23. > Add validation to create question with a minimum of

    2 choices
  24. > Add a minimum or maximum length to questions

  25. > Change delete so only admins can delete questions

  26. > Change delete so you can only delete your own

    questions
  27. > Change delete so you can't delete the initial questions

  28. > Change vote so you can only vote once per

    question
  29. > Change vote so any new votes override old votes

    on the same question
  30. > Add a new report question feature

  31. THIS WILL TAKE SOME TIME...

  32. BUSINESS LOGIC

  33. I HAVE TO REWRITE X

  34. None
  35. "ANTICIPATING CHANGE IS ONE OF THE CENTRAL THEMES OF REST"

  36. REPRESENTATIONAL STATE TRANSFER

  37. OUR API https://polls.apiblueprint.org/

  38. OUR API > Questions (affordance)

  39. QUESTIONS > questions > create (affordance)

  40. QUESTION > question > choices > delete (affordance) > report

    (affordance)
  41. CHOICE > choice > votes (count) > vote (affordance)

  42. SEMANTICS NOT IMPLEMENTATION DETAILS

  43. None
  44. $ curl https://polls.apiblueprint.org/ {"questions_url": "/questions", "url": "/"}

  45. HAL application/hal+json

  46. None
  47. HAL $ curl https://polls.apiblueprint.org/ -H 'Accept: application/hal+json' { "_links": {

    "questions": { "href": "/questions" }, "self": { "href": "/" } } }
  48. HAL $ curl https://polls.apiblueprint.org/questions/1 -H 'Accept: application/hal+json' { "question": "Favourite

    programming language?", "_links": {"self": {"href": "/questions/1"}}, "_embed": { "choices": [ { "_links": {"self": {"href": "/questions/1/choices/1"}}, "choice": "Swift", "votes": 483 } ] } }
  49. $ curl https://polls.apiblueprint.org/questions -H 'Accept: application/hal+json' { "_links": { "first":

    {"href": "/questions"}, "self": {"href": "/questions"}, "next": {"href": "/questions?page=2"} "last": {"href": "/questions?page=2"}, }, }
  50. SIREN application/siren+json

  51. None
  52. SIREN { "actions": [ { "name": "vote", "href": "/questions/1/choices/3", "method":

    "POST" } ] }
  53. SIREN { "actions": [ { "name": "create", "method": "POST", "type":

    "application/json", "href": "/questions", "fields": [ { "name": "question", "type": "text", "title": "Question" }, { "name": "choices", "type": "array[text]", "title": "Choices" } ] } ] }
  54. HYPERDRIVE

  55. API BLUEPRINT + HYPERDRIVE

  56. API BLUEPRINT ## Questions Collection [/questions] ### Create a new

    question [POST] + Relation: create + Request (application/json) + Attributes + question (string, required) + choices (array[string], required) + Response 201 (application/json) + Attributes (Question)
  57. HYPERDRIVE hyperdrive.enter("https://polls.apiblueprint.org/") { result }

  58. REPRESENTOR

  59. DEMO

  60. hyperdrive.enter("http://localhost:8000/") // follow the 'questions' transition .flatMap { hyperdrive.request($0.transitions["questions"]) }

    // Follow the 'create' transition .flatMap { hyperdrive.request($0.transitions["create"], attributes: [ "question": "types of egg", "choices": ["hard boiled eggs", "soft boiled eggs"] ]) } // Select the first choice .map { $0.representors["choices"]!.first! } // Follow the 'vote' transition .flatMap { hyperdrive.request($0.transitions["vote"]) }
  61. DEMO

  62. /// Returns whether the user may create a question var

    canCreateQuestion: Bool { return representor.transitions["create"] != nil }
  63. /// Returns whether the user may delete the question at

    the given index func canDeleteQuestion(index: Int) -> Bool { let transition = questions?[index].transitions["delete"] return transition != nil }
  64. None
  65. import JSONSchema let schema = Schema([ "type": "object", "properties": [

    "question": ["type": "string"], "choices": ["type": "array", "minItems": 2], ], ]) schema.validate([ "question": "Favourite Conference?", "choices": [ "MBLTDev 2015" ] ])
  66. > the-hypermedia-project / Hyperdrive > kylef / Starship, RxHyperdrive, JSONSchema.swift

    > apiaryio / polls-app, polls-api
  67. > kylefuller > kyle @ fuller.li > fuller.li / slides