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.

Avatar for António Monteiro

António Monteiro

May 19, 2016
Tweet

More Decks by António Monteiro

Other Decks in Programming

Transcript

  1. – 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.”
  2. 1. clients can request the exact total response they need

    2. clients can communicate novelty atomically • without sacrificing relational queries on the server
  3. Checkpoint • Precise requests? • Client • Server • Communicate

    novelty? • Communicate identity back? • data over the wire? • Client-only state • Testing • Caching • Pluggable client / server storage?
  4. Om Next opinions • Single source of truth • Minimize

    flushing to DOM • Abstract asynchrony • No (visible) event model
  5. 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]
  6. Parser • reads & mutations • Runs on the client

    and server • Hydrate queries • no reshaping! • Edge of the system
  7. Creating information • Create temporary information on client • Remote

    mutation hits server • Server replies with mappings • tempids → real ids
  8. Client-only state • First-class support • Storage: merged with remote

    state • Parser distinguishes local / server • knows how to pick remote queries
  9. {: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}}
  10. {: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}}}
  11. 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
  12. Testing • global state + parser = awesome • Pure

    components • f (data) = UI • We can just test the UI data tree!
  13. Property-based testing • example-based • specify input / output pairs

    • property-based • write invariants • generate random input • attempt to falsify invariants • shrinking
  14. Om Next + test.check • queries / mutations are data

    • generate transactions • run against the parser • check invariants in resulting state
  15. 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"
  16. 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
  17. 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 (<! ch-chsk))] (case op :chsk/recv (om/merge! reconciler data) ;; ignore other events :nop) (recur (:event (<! ch-chsk)))))
  18. Server • Clojure preferred / less boilerplate • Other languages

    -> implement parser logic • easier for languages with Transit • Datomic supported by default • other DBs work as well
  19. Project status • very close to beta • documentation •

    github.com/omcljs/om/wiki • awkay.github.io/om-tutorial/ • anmonteiro.com
  20. 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)
  21. Takeaways • we can radically simplify UI programming • regardless

    of library / framework • strive for simple systems • with these 2 properties