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

More Decks by António Monteiro

Other Decks in Programming


  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