building flexible systems
play

Building Flexible Systems with Clojure and Datomic Stuart Sierra - PowerPoint PPT Presentation

Building Flexible Systems with Clojure and Datomic Stuart Sierra Cognitect We dont want to paint ourselves into a corner Clojure Flexible Systems Fact-based Context-free Non-exclusive Observable Fact Based


  1. Building Flexible Systems with Clojure and Datomic Stuart Sierra Cognitect

  2. “We don’t want to 
 paint ourselves 
 into a corner”

  3. Clojure

  4. Flexible Systems ✦ Fact-based ✦ Context-free ✦ Non-exclusive ✦ Observable

  5. Fact Based

  6. Fact Based ✦ Fact = statement about the world ✦ Cannot be invalidated

  7. public class Person { private List<Person> friends; public void addFriend(Person newFriend);

  8. mutable? public class Person { private List<Person> friends; public void addFriend(Person newFriend) { if (friends.length() < 500) friends.add(newFriend); else ... race condition?

  9. Universal Data Collection: Set Structured: Map #{1 7 9 5} {:name "Bob" :age 42} Ordered: Vector ["Alice" "Bob"] Associative: Map {412 "a" Command or stack: List 114 "b"} (println user)

  10. map {:name "Kelly Q." :friends [{:name "Alice"} {:name "Bob"}]} vector of maps

  11. (defn add-friend [person new-friend] (if (< (count (:friends person)) 500) (update person :friends 
 conj new-friend) {:error :too-friendly}))

  12. define name parameters function (defn add-friend [person new-friend] list symbol vector

  13. condition (if (< (count (:friends person)) 500)

  14. condition (if (< (count (:friends person)) 500) (update person :friends 
 conj new-friend) then

  15. {:name "Kelly Q." :friends [{:name "Alice"} {:name "Bob"}]} navigation (update person :friends 
 conj new-friend)

  16. 
 [{:name "Alice"} {:name "Bob"}] conjoin vector conj new-friend) [{:name "Alice"} {:name "Bob"} {:name "Claire"}]}

  17. {:name "Kelly Q." :friends [{:name "Alice"} {:name "Bob"}]} update map (update person :friends 
 conj new-friend) {:name "Kelly Q." :friends [{:name "Alice"} {:name "Bob"} {:name "Claire"}]}

  18. {:name "Kelly Q." :friends [{:name "Alice"} {:name "Bob"}]} universal data structures (update person :friends 
 conj new-friend) {:name "Kelly Q." universal :friends [{:name "Alice"} operations {:name "Bob"} {:name "Claire"}]}

  19. {:name "Kelly Q." :friends [{:name "Alice"} {:name "Bob"}]} (add-friend person {:name "Claire"}) domain rule in terms of universal data and operations {:name "Kelly Q." :friends [{:name "Alice"} {:name "Bob"} {:name "Claire"}]}

  20. (A Few) Universal Operations nth get map concat union keys filter cycle difference vals remove interleave intersection assoc reduce interpose select assoc-in replace distinct subset? update some flatten superset? update-in sort group-by join dissoc shuffle partition project merge reverse split-at index merge-with take split-with rename select-keys drop frequencies

  21. Immutable Values (defn add-friend [person new-friend] (if (< (count (:friends person)) 500) (update person :friends 
 no race conj new-friend) condition ...)) immutable value

  22. value function value

  23. value function value function value Time

  24. identity value function value function value Time

  25. stable live identity value function value function value Time

  26. (let [current-user (atom {:name "Kelly Q." :friends [{:name "Alice" :name "Bob"}]})] current-user {:name "Kelly Q." :friends [{:name "Alice"} {:name "Bob"}]}

  27. dereference (let [friends (:friends @ current-user)] (html [:p "Your " (count friends) " friends"] [:ul (for [friend friends] [:li (:name friend)])])) current-user {:name "Kelly Q." :friends [{:name "Alice"} {:name "Bob"}]}

  28. dereference (let [friends (:friends @ current-user)] (html [:p "Your " (count friends) " friends"] [:ul (for [friend friends] [:li (:name friend)])])) immutable value {:name "Kelly Q." :friends [{:name "Alice"} {:name "Bob"}]}

  29. identity (swap! current-user add-friend {:name "Claire"}) change state pure function current-user {:name "Kelly Q." :friends [{:name "Alice"} {:name "Kelly Q." {:name "Bob"} add-friend :friends [{:name "Alice"} {:name "Claire"}]} {:name "Bob"}]}

  30. connection storage service

  31. (db connection) database connection value storage service

  32. database value (let [db (db connection)] (query db ...) (query db ...) (query db ...)

  33. connection database database database transact transact value value value Time

  34. Universal Data Entity Attribute Value kelly :name "Kelly Q." kelly :friends alice kelly :friends bob

  35. Universal Data in Time Entity Attribute Value Op Tx kelly :name "Kelly Q." add 1000 kelly :friends alice add 1000 kelly :friends bob add 1000

  36. Universal Data in Time Entity Attribute Value Op Tx kelly :name "Kelly Q." add 1000 kelly :friends alice add 1000 kelly :friends bob add 1000 kelly :friends bob retract 1023 kelly :friends claire add 1023

  37. Current State Entity Attribute Value Op Tx kelly :name "Kelly Q." add 1000 kelly :friends alice add 1000 {:name "Kelly Q." kelly :friends bob add 1000 :friends [{:name "Alice"} {:name "Claire"}]} kelly :friends bob retract 1023 kelly :friends claire add 1023

  38. State as-of Last Week Entity Attribute Value Op Tx kelly :name "Kelly Q." add 1000 kelly :friends alice add 1000 kelly :friends bob add 1000 {:name "Kelly Q." kelly :friends bob retract 1023 :friends [{:name "Alice"} {:name "Bob"}]} kelly :friends claire add 1023

  39. (swap! current-user add-friend ...) change state identity pure function (transact connection [[:add-friend ...]])

  40. stable live identity database database database transact transact value value value Time

  41. Fact Based ✦ Fact = statement about the world ✦ Cannot be invalidated ➡ Immutable values ➡ Using uniform data structures ➡ Incorporating time

  42. Context Free

  43. Context Free ✦ Values are complete and self-describing ✦ No out-of-band knowledge required 
 to handle correctly

  44. {:id "1234abcd" :name "T.J."} {:id "abcd" :balance 42.00} {:id 7980 :region "AUS"}

  45. {:customer/id "1234abcd" :customer/name "T.J."} {:billing.customer/id "abcd" :billing.customer/balance 42.00} {:widgetCo.customer/id 7980 :widgetCo/region "AUS"} namespaced keywords

  46. (merge customer billing-customer widgetco-customer) {:customer/id "1234abcd" :customer/name "T.J." :billing.customer/id "abcd" :billing.customer/balance 42.00 :widgetCo.customer/id 7980 :widgetCo/region "AUS"}

  47. clojure.spec (s/def :customer/id (s/and string? #(re-matches #"[0-9a-e]{8}" %))) (s/def :customer/name (s/and string? #(not (str/blank? %)))) (s/def ::Customer (s/keys :req [:customer/id] :opt [:customer/name]]))

  48. (let [user {:customer/name ""}] (s/explain ::Customer user)) val: {:customer/name ""} fails spec: ::Customer predicate: (contains? % :customer/id) In: [:customer/name] val: "" fails spec: :customer/name at: [:customer/name] predicate: (not (blank? %))

  49. (pull db [:customer/name :customer/start-date {:customer/account [:account/id :account/balance]}] customer) {:customer/name "T.J." :customer/start-date #inst"2012-01-24" :customer/account {:account/id 12345 :account/balance 4200}}

  50. (defui UserWidget om.next static om/IQuery (query [this] [:user/name {:user/friends [:user/name]}]) Object (render [this] (let [friends (:user/friends (om/props this))] (html [:p "Your " (count friends) " friends"] [:ul (for [friend friends] [:li (:user/name friend)])]))))

  51. Context Free ✦ Values are complete and self-describing ✦ No out-of-band knowledge required 
 to handle correctly ➡ Use namespaces for globally-unique labels ➡ Let consumers control interactions

  52. Non-exclusive

  53. Non-exclusive ✦ Do not obstruct data evolution ✦ Continue correct operation in the 
 presence of unexpected input

  54. function input input output

  55. function input input output extra extra extra extra

  56. condition (defn gold-status [customer] (if (< 100 
 (:customer/orders customer)) then: augment (assoc customer 
 and return :loyalty/tier :gold) customer)) else: return unchanged

  57. clojure.spec (s/fdef gold-status :args (s/cat :customer (s/keys :req [:customer/id :customer/orders]))) minimal required keys

Recommend


More recommend