Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Om Next: motivation, architecture, ecosystem

Om Next: motivation, architecture, ecosystem

In this talk, I explore the motivation behind Om Next's design, its architecture and touch a little bit on the young ecosystem that it has already brought into existence.

António Monteiro

May 19, 2016
Tweet

More Decks by António Monteiro

Other Decks in Programming

Transcript

  1. Om Next
    Motivation, architecture,
    ecosystem
    Paris Clojure User Group
    @anmonteiro90

    View Slide

  2. View Slide

  3. View Slide

  4. REST: expectations
    • define logical “resources”
    • identified by URIs
    • clients request them

    View Slide

  5. Luke Wroblewski

    View Slide

  6. REST: reality

    View Slide

  7. – Roy T. Fielding, PhD
    “The trade-off, though, is that a
    uniform interface degrades efficiency,
    since information is transferred in a
    standardized form rather than one
    which is specific to an application's
    needs.”

    View Slide

  8. View Slide

  9. View Slide

  10. View Slide

  11. 1. clients can request the exact
    total response they need
    2. clients can communicate
    novelty atomically
    • without sacrificing relational
    queries on the server

    View Slide

  12. View Slide

  13. Quiescent
    om.core

    View Slide

  14. View Slide

  15. View Slide

  16. View Slide

  17. Checkpoint
    • Precise requests?
    • Client
    • Server
    • Communicate
    novelty?
    • Communicate
    identity back?
    • data over the wire?
    • Client-only state
    • Testing
    • Caching
    • Pluggable client /
    server storage?

    View Slide

  18. om.next

    View Slide

  19. Om Next opinions
    • Single source of truth
    • Minimize flushing to DOM
    • Abstract asynchrony
    • No (visible) event model

    View Slide

  20. View Slide

  21. [:person/name]

    View Slide

  22. (defui Person
    static om/IQuery
    (query [this]
    [:person/name])
    Object
    (render [this]
    ...))

    View Slide

  23. Query expressions

    View Slide

  24. :person/name
    ‘(:person/friends {:sort :asc})
    {:person/address
    [:address/street :address/zip]}

    View Slide

  25. ‘[:id :value {:children ...}]
    {:feed/items
    {:feed/photo [:id :caption :href]
    :feed/post [:id :title :content]
    :feed/graphic [:id :title :img]}}

    View Slide

  26. (increment/users!)
    (delete/friend!
    {:me 1
    :friend 2})

    View Slide

  27. Parser

    f (query) = data
    [:person/name]
    {:person/name “António”}

    View Slide

  28. Parser
    (defmethod read :default
    [{:keys [state]} key _]
    ;; Optimistic update
    {:value (get @state key)
    :remote true})
    (parser {:state state} [:person/name])
    ;; => {:person/name “António”}
    (parser {:state state} [:person/name]
    :remote)
    ;; => [:person/name]

    View Slide

  29. Parser
    • reads & mutations
    • Runs on the client and server
    • Hydrate queries
    • no reshaping!
    • Edge of the system

    View Slide

  30. Demo

    View Slide

  31. [(person/add!
    {:person/name “António”
    :person/address
    {:address/street
    “Hochschulstraße”
    :address/zip
    “01069”}})]

    View Slide

  32. [(delete/friend! {:me 1
    :friend 2})
    :friends/list]
    Re-read this key

    View Slide

  33. Creating information
    • Create temporary information
    on client
    • Remote mutation hits server
    • Server replies with mappings
    • tempids → real ids

    View Slide

  34. View Slide

  35. Client-only state
    • First-class support
    • Storage: merged with remote
    state
    • Parser distinguishes local / server
    • knows how to pick remote
    queries

    View Slide

  36. View Slide

  37. Normalization

    View Slide

  38. {:people [{:person/name “Alice”
    :person/age 25}
    {:person/name “Bob”
    :person/age 34}]
    :favorites [{:person/name “Bob”
    :person/age 34}]}

    View Slide

  39. {:people [[:person/by-name “Alice”]
    {:person/name “Bob”
    :person/age 34}]
    :favorites [{:person/name “Bob”
    :person/age 34}]
    :person/by-name
    {“Alice” {:person/name “Alice”
    :person/age 25}}

    View Slide

  40. {:people [[:person/by-name “Alice”]
    [:person/by-name “Bob”]]
    :favorites [[:person/by-name “Bob”]]
    :person/by-name
    {“Alice” {:person/name “Alice”
    :person/age 25}
    “Bob” {:person/name “Bob”
    :person/age 34}}}

    View Slide

  41. Internals

    View Slide

  42. Shared
    data
    Root component
    (defui Root
    static om/IQuery
    (query [this]
    [ ...])
    Object
    (render [this]
    ...))
    Parser
    (om/parser
    {:read read
    :mutate mutate})
    Indexer
    {:people [{:person/name “Alice”
    :person/age 25}
    {:person/name “Bob”
    :person/age 34}]
    :favorites [{:person/name “Bob”
    :person/age 34}]}
    App state
    Remotes
    Remote-send
    logic
    Transaction
    history
    Batching logic
    (async)
    rendering loop
    State merging
    logic
    Component
    instrumentation

    View Slide

  43. View Slide

  44. Testing
    • global state + parser = awesome
    • Pure components

    f (data) = UI
    • We can just test the UI data tree!

    View Slide

  45. Property-based testing
    • example-based
    • specify input / output pairs
    • property-based
    • write invariants
    • generate random input
    • attempt to falsify invariants
    • shrinking

    View Slide

  46. Om Next + test.check
    • queries / mutations are data
    • generate transactions
    • run against the parser
    • check invariants in resulting state

    View Slide

  47. Demo

    View Slide

  48. Testing recap
    1. Generate random transactions
    2. Shrink failures
    3. Use minimal failure to
    reproduce bugs

    View Slide

  49. 1. Generate random transactions
    2. Shrink failures
    3. Use minimal failure to
    reproduce bugs
    Testing recap

    View Slide

  50. More Om Next

    View Slide

  51. Caching
    github.com/omcljs/om/wiki/Remote-Synchronization-Tutorial
    (defmethod read :feed/items
    [{:keys [state ast query]} k _]
    (let [st @state]
    {:value (om/db ->tree query (get st k) st)
    :dynamic ...
    :static …}))
    (let [query (parser env query :static)
    json (t/write w query)
    hash (.substring (sha-256 json) 0 16)]
    (str "/api/" hash))
    ;; => "/api/02e397cc1447d688"

    View Slide

  52. Custom storage
    (ns om-tutorial.core
    (:require [datascript.core :as d]))
    (def conn (d/create-conn {}))
    (om/reconciler
    {:state conn
    :parser
    (om/parser {:read read
    :mutate mutate})})
    github.com/omcljs/om/wiki/DataScript-Integration-Tutorial

    View Slide

  53. Streaming
    (:require-macros [cljs.core.async.macros :refer [go]])
    (:require [om.next :as om]
    [taoensso.sente :as s]
    [cljs.core.async :refer [(def ch-chsk
    (:ch-recv (s/make-channel-socket! “/ws"
    {:type :auto})))
    (go (loop [[op data] (:event ((case op
    :chsk/recv (om/merge! reconciler data)
    ;; ignore other events
    :nop)
    (recur (:event (

    View Slide

  54. Server
    • Clojure preferred / less boilerplate
    • Other languages -> implement parser
    logic
    • easier for languages with Transit
    • Datomic supported by default
    • other DBs work as well

    View Slide

  55. Project status
    • very close to beta
    • documentation
    • github.com/omcljs/om/wiki
    • awkay.github.io/om-tutorial/
    • anmonteiro.com

    View Slide

  56. Shameless plugs
    • github.com/
    • anmonteiro/aemette
    • anmonteiro/devcards-om-next
    • ladderlife/om-css
    • ladderlife/cellophane

    View Slide

  57. Server-side rendering
    (ns server-rendering.core
    (:require
    #?(:clj [cellophane.next :as om]
    :cljs [om.next :as om])))
    (def component
    (om/add-root! reconciler Root nil))
    (dom/render-to-str component)

    View Slide

  58. Takeaways
    • we can radically simplify UI
    programming
    • regardless of library / framework
    • strive for simple systems
    • with these 2 properties

    View Slide

  59. github.com/anmonteiro/paris-meetup

    View Slide

  60. Questions?

    View Slide