exploring four datomic superpowers

131
Lucas Cavalcanti & Edward Wible Exploring Four Datomic Superpowers

Upload: lucas-cavalcanti

Post on 07-Jul-2015

1.889 views

Category:

Software


1 download

DESCRIPTION

https://www.youtube.com/watch?v=7lm3K8zVOdY This session will explore four common problems, and the unique and surprising tools Datomic provides to solve them elegantly: HTTP caching - How to generically generate and validate Last-Modified and If-Modified-Since headers Audit trail - how to extend Datomic’s immutable transaction log to include arbitrary audit related metadata Mobile database sync - trivial implementation of an incremental update API for high latency/low bandwidth clients Authorization - easily determine resource ownership, and centrally isolate users from data they are not allowed to see These problems have certainly been solved before using other databases, but Datomic provides features that make the proposed implementations concise, generic, and purely functional

TRANSCRIPT

Page 1: Exploring four Datomic superpowers

Lucas Cavalcanti & Edward Wible

Exploring Four Datomic Superpowers

Page 2: Exploring four Datomic superpowers

2

Why build a bank from scratch in Brazil using Clojure and Datomic?

Page 3: Exploring four Datomic superpowers

3

Page 4: Exploring four Datomic superpowers

3

Page 5: Exploring four Datomic superpowers

4

1 Audit Trail

Page 6: Exploring four Datomic superpowers

5

Customer joins waiting list for a card01 NOV 10:00

event stream

Page 7: Exploring four Datomic superpowers

5

Customer joins waiting list for a card01 NOV 10:00

Robot 437aae3 approves R$3K limit01 NOV 11:00

event stream

Page 8: Exploring four Datomic superpowers

5

Customer joins waiting list for a card01 NOV 10:00

Robot 437aae3 approves R$3K limit01 NOV 11:00

Mastercard purchase, Starbucks, R$10009 NOV 08:00

event stream

Page 9: Exploring four Datomic superpowers

5

Customer joins waiting list for a card01 NOV 10:00

Robot 437aae3 approves R$3K limit01 NOV 11:00

Mastercard purchase, Starbucks, R$10009 NOV 08:00

Support agent increases limit to R$5K15 NOV 15:00

event stream

Page 10: Exploring four Datomic superpowers

5

Customer joins waiting list for a card01 NOV 10:00

Robot 437aae3 approves R$3K limit01 NOV 11:00

Mastercard purchase, Starbucks, R$10009 NOV 08:00

Support agent increases limit to R$5K15 NOV 15:00

Customer blocks card15 NOV 17:05

event stream

Page 11: Exploring four Datomic superpowers

facts over time

6

01 NOV 10:00

01 NOV 11:00

09 NOV 08:00

15 NOV 15:00

15 NOV 17:05

[<customer> :customer/id #uuid “b2c90…”]

[<account> :account/customer <customer>][<account> :account/limit 3000][<card> :card/account <account>][<card> :card/status :card.status/active]

[<purchase> :purchase/card <card>][<purchase> :purchase/amount 100][<purchase> :purchase/merchant “Starbucks”]

[<account> :account/limit 5000][<account> :account/limit 3000]

[<card> :card/status :card.status/blocked][<card> :card/status :card.status/active]

Page 12: Exploring four Datomic superpowers

facts over time

6

01 NOV 10:00

01 NOV 11:00

09 NOV 08:00

15 NOV 15:00

15 NOV 17:05

[<customer> :customer/id #uuid “b2c90…”]

[<account> :account/customer <customer>][<account> :account/limit 3000][<card> :card/account <account>][<card> :card/status :card.status/active]

[<purchase> :purchase/card <card>][<purchase> :purchase/amount 100][<purchase> :purchase/merchant “Starbucks”]

[<account> :account/limit 5000][<account> :account/limit 3000]

[<card> :card/status :card.status/blocked][<card> :card/status :card.status/active]

entity attribute value

Page 13: Exploring four Datomic superpowers

7

Page 14: Exploring four Datomic superpowers

7

don’t lose data

Page 15: Exploring four Datomic superpowers

7

don’t lose data

what changed

when

Page 16: Exploring four Datomic superpowers

7

don’t lose data

what changed

when

as-of since

Page 17: Exploring four Datomic superpowers

7

don’t lose data

what changed

when

as-of since

fork merge

Page 18: Exploring four Datomic superpowers

8

What was the initial limit for the card?

Page 19: Exploring four Datomic superpowers

8

What was the initial limit for the card?

At the time the Starbucks transaction occurred, which fraud triggers would have activated?

Page 20: Exploring four Datomic superpowers

8

What was the initial limit for the card?

At the time the Starbucks transaction occurred, which fraud triggers would have activated?

How long did the customer spend on each stage of the acquisition funnel?

Page 21: Exploring four Datomic superpowers

8

What was the initial limit for the card?

At the time the Starbucks transaction occurred, which fraud triggers would have activated?

How long did the customer spend on each stage of the acquisition funnel?

How frequently do we see amount changes on Starbucks transactions?

Page 22: Exploring four Datomic superpowers

9

What sequence of events resulted in the Starbucks transaction being persisted?

Page 23: Exploring four Datomic superpowers

9

What sequence of events resulted in the Starbucks transaction being persisted?

iOShttp-inkafka-outkafka-in

iZb iZb.jnAiZb.jnA.9CdiZb.jnA.9Cd.l9A

Page 24: Exploring four Datomic superpowers

9

What sequence of events resulted in the Starbucks transaction being persisted? correlation-id

iOShttp-inkafka-outkafka-in

iZb iZb.jnAiZb.jnA.9CdiZb.jnA.9Cd.l9A

Page 25: Exploring four Datomic superpowers

Who was responsible for the credit limit increase?

9

What sequence of events resulted in the Starbucks transaction being persisted? correlation-id

iOShttp-inkafka-outkafka-in

iZb iZb.jnAiZb.jnA.9CdiZb.jnA.9Cd.l9A

Page 26: Exploring four Datomic superpowers

Who was responsible for the credit limit increase?

9

What sequence of events resulted in the Starbucks transaction being persisted? correlation-id

iOShttp-inkafka-outkafka-in

iZb iZb.jnAiZb.jnA.9CdiZb.jnA.9Cd.l9A

#uuid “b2c90…”“[email protected]”:kafka-LIMIT-CHANGED:robot-437aae3

Page 27: Exploring four Datomic superpowers

Who was responsible for the credit limit increase?

9

What sequence of events resulted in the Starbucks transaction being persisted? correlation-id

iOShttp-inkafka-outkafka-in

iZb iZb.jnAiZb.jnA.9CdiZb.jnA.9Cd.l9A

user#uuid “b2c90…”“[email protected]”:kafka-LIMIT-CHANGED:robot-437aae3

Page 28: Exploring four Datomic superpowers

Who was responsible for the credit limit increase?

9

What sequence of events resulted in the Starbucks transaction being persisted?

Why was the customer’s card blocked?

correlation-id

iOShttp-inkafka-outkafka-in

iZb iZb.jnAiZb.jnA.9CdiZb.jnA.9Cd.l9A

user#uuid “b2c90…”“[email protected]”:kafka-LIMIT-CHANGED:robot-437aae3

Page 29: Exploring four Datomic superpowers

Who was responsible for the credit limit increase?

9

What sequence of events resulted in the Starbucks transaction being persisted?

Why was the customer’s card blocked?

correlation-id

iOShttp-inkafka-outkafka-in

iZb iZb.jnAiZb.jnA.9CdiZb.jnA.9Cd.l9A

:fraud-preventative:recurring-scheduled:late-payment:data-migration

user#uuid “b2c90…”“[email protected]”:kafka-LIMIT-CHANGED:robot-437aae3

Page 30: Exploring four Datomic superpowers

Who was responsible for the credit limit increase?

9

What sequence of events resulted in the Starbucks transaction being persisted?

Why was the customer’s card blocked?

correlation-id

iOShttp-inkafka-outkafka-in

iZb iZb.jnAiZb.jnA.9CdiZb.jnA.9Cd.l9A

reason:fraud-preventative:recurring-scheduled:late-payment:data-migration

user#uuid “b2c90…”“[email protected]”:kafka-LIMIT-CHANGED:robot-437aae3

Page 31: Exploring four Datomic superpowers

(defn block-card [card reason] (d/transact (conn) [[:db/add card :card/status :card.status/blocked]

]))

10

storing transaction metadata

Page 32: Exploring four Datomic superpowers

(defn block-card [card reason] (d/transact (conn) [[:db/add card :card/status :card.status/blocked]

]))

{:db/id (d/tempid :db.part/tx) :audit/user "[email protected]" :audit/cid "iZb.jnA.9Cd.l9A" :audit/tags :fraud-preventative}

10

storing transaction metadata

Page 33: Exploring four Datomic superpowers

11

[<customer> :customer/id #uuid “b2c90…”] 01 NOV 10:00

[<account> :account/customer <customer>][<account> :account/limit 3000M][<card> :card/account <account>][<card> :card/status :card.status/active]

01 NOV 11:00

[<purchase> :purchase/card <card>][<purchase> :purchase/amount 100M][<purchase> :purchase/merchant “Starbucks”]

09 NOV 08:00

[<account> :account/limit 5000M][<account> :account/limit 3000M]15 NOV 15:00

[<card> :card/status :card.status/blocked][<card> :card/status :card.status/active]15 NOV 17:05

transactions over time with metadata

Page 34: Exploring four Datomic superpowers

11

[<customer> :customer/id #uuid “b2c90…”] 01 NOV 10:00

[<account> :account/customer <customer>][<account> :account/limit 3000M][<card> :card/account <account>][<card> :card/status :card.status/active]

01 NOV 11:00

[<purchase> :purchase/card <card>][<purchase> :purchase/amount 100M][<purchase> :purchase/merchant “Starbucks”]

09 NOV 08:00

[<account> :account/limit 5000M][<account> :account/limit 3000M]15 NOV 15:00

[<card> :card/status :card.status/blocked][<card> :card/status :card.status/active]15 NOV 17:05

transactions over time with metadata

Page 35: Exploring four Datomic superpowers

12

querying transaction metadata

(defn who-blocked-the-card? [card] (d/q '{:find [?user ?cid ?tags] :in [$ ?status ?card] :where [[?card :card/status ?status ?tx] [?tx :audit/user ?user] [?tx :audit/cid ?cid] [?tx :audit/tags ?tags]]} db :card.status/blocked card))

Page 36: Exploring four Datomic superpowers

12

querying transaction metadata

(defn who-blocked-the-card? [card] (d/q '{:find [?user ?cid ?tags] :in [$ ?status ?card] :where [[?card :card/status ?status ?tx] [?tx :audit/user ?user] [?tx :audit/cid ?cid] [?tx :audit/tags ?tags]]} db :card.status/blocked card))

[?card :card/status ?status ?tx]entity attribute value txn

Page 37: Exploring four Datomic superpowers

12

querying transaction metadata

(defn who-blocked-the-card? [card] (d/q '{:find [?user ?cid ?tags] :in [$ ?status ?card] :where [[?card :card/status ?status ?tx] [?tx :audit/user ?user] [?tx :audit/cid ?cid] [?tx :audit/tags ?tags]]} db :card.status/blocked card))

Page 38: Exploring four Datomic superpowers

12

querying transaction metadata

(defn who-blocked-the-card? [card] (d/q '{:find [?user ?cid ?tags] :in [$ ?status ?card] :where [[?card :card/status ?status ?tx] [?tx :audit/user ?user] [?tx :audit/cid ?cid] [?tx :audit/tags ?tags]]} db :card.status/blocked card))

Page 39: Exploring four Datomic superpowers

12

querying transaction metadata

(defn who-blocked-the-card? [card] (d/q '{:find [?user ?cid ?tags] :in [$ ?status ?card] :where [[?card :card/status ?status ?tx] [?tx :audit/user ?user] [?tx :audit/cid ?cid] [?tx :audit/tags ?tags]]} db :card.status/blocked card))

Page 40: Exploring four Datomic superpowers

13

2 Authorization

Page 41: Exploring four Datomic superpowers

GET /purchases/1337/comments

should we return data?

Page 42: Exploring four Datomic superpowers

GET /purchases/1337/comments

200 OK 401 Unauthorized

should we return data?

Page 43: Exploring four Datomic superpowers

customer

account

card purchase

payment

comment

GET /purchases/1337/comments

200 OK 401 Unauthorized

should we return data?

Page 44: Exploring four Datomic superpowers

customer

account

card purchase

payment

comment

GET /purchases/1337/comments

200 OK 401 Unauthorized

should we return data?

Page 45: Exploring four Datomic superpowers

customer

account

card purchase

payment

comment

GET /purchases/1337/comments

200 OK 401 Unauthorized

should we return data?

Page 46: Exploring four Datomic superpowers

15

explicit relationship traversal

(defn owns? [customer-id purchase-id db] (d/q '{:find [?pur .] :in [$ ?customer-id ?purchase-id] :where [[?pur :purchase/id ?purchase-id] [?pur :purchase/account ?acc] [?acc :account/customer ?cus] [?cus :customer/id ?customer-id]]} db customer-id purchase-id))

Page 47: Exploring four Datomic superpowers

15

explicit relationship traversal

(defn owns? [customer-id purchase-id db] (d/q '{:find [?pur .] :in [$ ?customer-id ?purchase-id] :where [[?pur :purchase/id ?purchase-id] [?pur :purchase/account ?acc] [?acc :account/customer ?cus] [?cus :customer/id ?customer-id]]} db customer-id purchase-id))

Page 48: Exploring four Datomic superpowers

generic relationship traversal

16

recursive rule (logical OR)

Page 49: Exploring four Datomic superpowers

generic relationship traversal

16

recursive rule (logical OR)

(def owner-rules '[[(owns? ?cus-id ?e) [?e :customer/id ?cus-id]] [(owns? ?cus-id ?e) [?e ?ref-attr ?r] (owns? ?cus-id ?r)]])

Page 50: Exploring four Datomic superpowers

generic relationship traversal

16

recursive rule (logical OR)

(def owner-rules '[[(owns? ?cus-id ?e) [?e :customer/id ?cus-id]] [(owns? ?cus-id ?e) [?e ?ref-attr ?r] (owns? ?cus-id ?r)]])

Page 51: Exploring four Datomic superpowers

generic relationship traversal

16

recursive rule (logical OR)

(def owner-rules '[[(owns? ?cus-id ?e) [?e :customer/id ?cus-id]] [(owns? ?cus-id ?e) [?e ?ref-attr ?r] (owns? ?cus-id ?r)]])

Page 52: Exploring four Datomic superpowers

generic relationship traversal

16

recursive rule (logical OR)

(def owner-rules '[[(owns? ?cus-id ?e) [?e :customer/id ?cus-id]] [(owns? ?cus-id ?e) [?e ?ref-attr ?r] (owns? ?cus-id ?r)]])

Page 53: Exploring four Datomic superpowers

generic relationship traversal

16

recursive rule (logical OR)

(def owner-rules '[[(owns? ?cus-id ?e) [?e :customer/id ?cus-id]] [(owns? ?cus-id ?e) [?e ?ref-attr ?r] (owns? ?cus-id ?r)]])

Page 54: Exploring four Datomic superpowers

generic relationship traversal

16

recursive rule (logical OR)

(def owner-rules '[[(owns? ?cus-id ?e) [?e :customer/id ?cus-id]] [(owns? ?cus-id ?e) [?e ?ref-attr ?r] (owns? ?cus-id ?r)]])

Page 55: Exploring four Datomic superpowers

generic relationship traversal

16

recursive rule (logical OR)

query using rule

(def owner-rules '[[(owns? ?cus-id ?e) [?e :customer/id ?cus-id]] [(owns? ?cus-id ?e) [?e ?ref-attr ?r] (owns? ?cus-id ?r)]])

Page 56: Exploring four Datomic superpowers

generic relationship traversal

16

recursive rule (logical OR)

query using rule

(def owner-rules '[[(owns? ?cus-id ?e) [?e :customer/id ?cus-id]] [(owns? ?cus-id ?e) [?e ?ref-attr ?r] (owns? ?cus-id ?r)]])

(owns? customer-id [:purchase/id id] db)

(defn owns? [customer-id entity db] (d/q '{:find [?e .] :in [$ ?cus-id ?e %] :where [(owns? ?cus-id ?e)]} db customer-id entity owner-rules))

Page 57: Exploring four Datomic superpowers

generic relationship traversal

16

recursive rule (logical OR)

query using rule

(def owner-rules '[[(owns? ?cus-id ?e) [?e :customer/id ?cus-id]] [(owns? ?cus-id ?e) [?e ?ref-attr ?r] (owns? ?cus-id ?r)]])

(owns? customer-id [:purchase/id id] db)

(defn owns? [customer-id entity db] (d/q '{:find [?e .] :in [$ ?cus-id ?e %] :where [(owns? ?cus-id ?e)]} db customer-id entity owner-rules))

Page 58: Exploring four Datomic superpowers

17

Recursion rules!

But can we go further?

Can we prevent queries from returning entities owned by

other customers?

Page 59: Exploring four Datomic superpowers

all owned entities

18

query binding attribute

Page 60: Exploring four Datomic superpowers

all owned entities

18

query binding attribute

(defn owns? [customer-id entity db] (d/q '{:find [?e .] :in [$ ?cus-id ?e %] :where [(owns? ?cus-id ?e)]} db customer-id entity] owner-rules))

Page 61: Exploring four Datomic superpowers

all owned entities

18

query binding attribute

(defn owns? [customer-id entity db] (d/q '{:find [?e .] :in [$ ?cus-id ?e %] :where [(owns? ?cus-id ?e)]} db customer-id entity] owner-rules))

Page 62: Exploring four Datomic superpowers

all owned entities

18

query binding attribute

attribute unbound

(defn owns? [customer-id entity db] (d/q '{:find [?e .] :in [$ ?cus-id ?e %] :where [(owns? ?cus-id ?e)]} db customer-id entity] owner-rules))

Page 63: Exploring four Datomic superpowers

all owned entities

18

query binding attribute

attribute unbound

(defn owns? [customer-id entity db] (d/q '{:find [?e .] :in [$ ?cus-id ?e %] :where [(owns? ?cus-id ?e)]} db customer-id entity] owner-rules))

(defn owned-entities [customer-id db] (d/q '{:find [[?e ...]] :in [$ ?cus-id %] :where [(owns? ?cus-id ?e)]} db customer-id owner-rules))

Page 64: Exploring four Datomic superpowers

all owned entities

18

query binding attribute

attribute unbound

(defn owns? [customer-id entity db] (d/q '{:find [?e .] :in [$ ?cus-id ?e %] :where [(owns? ?cus-id ?e)]} db customer-id entity] owner-rules))

(defn owned-entities [customer-id db] (d/q '{:find [[?e ...]] :in [$ ?cus-id %] :where [(owns? ?cus-id ?e)]} db customer-id owner-rules))

Page 65: Exploring four Datomic superpowers

all owned entities

18

query binding attribute

attribute unbound

(defn owns? [customer-id entity db] (d/q '{:find [?e .] :in [$ ?cus-id ?e %] :where [(owns? ?cus-id ?e)]} db customer-id entity] owner-rules))

(defn owned-entities [customer-id db] (d/q '{:find [[?e ...]] :in [$ ?cus-id %] :where [(owns? ?cus-id ?e)]} db customer-id owner-rules))

Page 66: Exploring four Datomic superpowers

filtered db (as value)

19

(defn owned-db [customer-id db] (let [owned (set (owned-entities customer-id db))] (d/filter db (fn [_ [e a v t :as datom]] (when (or (= e t) (contains? owned e)) datom)))))

Page 67: Exploring four Datomic superpowers

filtered db (as value)

19

(defn owned-db [customer-id db] (let [owned (set (owned-entities customer-id db))] (d/filter db (fn [_ [e a v t :as datom]] (when (or (= e t) (contains? owned e)) datom)))))

Page 68: Exploring four Datomic superpowers

filtered db (as value)

19

(defn owned-db [customer-id db] (let [owned (set (owned-entities customer-id db))] (d/filter db (fn [_ [e a v t :as datom]] (when (or (= e t) (contains? owned e)) datom)))))

Page 69: Exploring four Datomic superpowers

filtered db (as value)

19

(defn owned-db [customer-id db] (let [owned (set (owned-entities customer-id db))] (d/filter db (fn [_ [e a v t :as datom]] (when (or (= e t) (contains? owned e)) datom)))))

Page 70: Exploring four Datomic superpowers

filtered db (as value)

19

(defn owned-db [customer-id db] (let [owned (set (owned-entities customer-id db))] (d/filter db (fn [_ [e a v t :as datom]] (when (or (= e t) (contains? owned e)) datom)))))

Page 71: Exploring four Datomic superpowers

filtered db (as value)

19

filtered db will not contain datoms from other owners!

(defn owned-db [customer-id db] (let [owned (set (owned-entities customer-id db))] (d/filter db (fn [_ [e a v t :as datom]] (when (or (= e t) (contains? owned e)) datom)))))

Page 72: Exploring four Datomic superpowers

passing a filtered db is transparent for queries

20

(defn all-purchases [account-id db] (d/q '{:find [[(pull ?p [*]) ...]] :in [$ ?acc] :where [[?p :purchase/account ?acc]]} db [:account/id account-id]))

(all-purchases account-id db) (all-purchases account-id (owned-db customer-id db))

Page 73: Exploring four Datomic superpowers

passing a filtered db is transparent for queries

20

(defn all-purchases [account-id db] (d/q '{:find [[(pull ?p [*]) ...]] :in [$ ?acc] :where [[?p :purchase/account ?acc]]} db [:account/id account-id]))

(all-purchases account-id db) (all-purchases account-id (owned-db customer-id db))

Page 74: Exploring four Datomic superpowers

21

We’re Mobile!

Bandwidth is costly

Page 75: Exploring four Datomic superpowers

22

3 HTTP Cache

Page 76: Exploring four Datomic superpowers

23

GET /accounts/1234/purchases200 OKLast-Modified: Fri, 14 Nov 2014 14:28:50 UTC{"purchases": [...]}

http last modified header

Page 77: Exploring four Datomic superpowers

23

If-Modified-Since: Fri, 14 Nov 2014 14:28:50 UTCGET /accounts/1234/purchases304 Not Modified

GET /accounts/1234/purchases200 OKLast-Modified: Fri, 14 Nov 2014 14:28:50 UTC{"purchases": [...]}

http last modified header

Page 78: Exploring four Datomic superpowers

24

How to keep track of a good last-modified date for a URL?

Page 79: Exploring four Datomic superpowers

24

How to keep track of a good last-modified date for a URL?

The last time any owned entity changed!

Page 80: Exploring four Datomic superpowers

If no customer-owned data changed, 304

25

(defn last-modified [customer-id db] (d/q '{:find [(max ?time) .] :in [$ ?cus-id %] :where [(owns? ?cus-id ?e) [?e ?a ?v ?tx] [?tx :db/txInstant ?time]]} db customer-id owner-rules))

Page 81: Exploring four Datomic superpowers

If no customer-owned data changed, 304

25

(defn last-modified [customer-id db] (d/q '{:find [(max ?time) .] :in [$ ?cus-id %] :where [(owns? ?cus-id ?e) [?e ?a ?v ?tx] [?tx :db/txInstant ?time]]} db customer-id owner-rules))

Page 82: Exploring four Datomic superpowers

If no customer-owned data changed, 304

25

(defn last-modified [customer-id db] (d/q '{:find [(max ?time) .] :in [$ ?cus-id %] :where [(owns? ?cus-id ?e) [?e ?a ?v ?tx] [?tx :db/txInstant ?time]]} db customer-id owner-rules))

Page 83: Exploring four Datomic superpowers

If no customer-owned data changed, 304

25

(defn last-modified [customer-id db] (d/q '{:find [(max ?time) .] :in [$ ?cus-id %] :where [(owns? ?cus-id ?e) [?e ?a ?v ?tx] [?tx :db/txInstant ?time]]} db customer-id owner-rules))

Page 84: Exploring four Datomic superpowers

If no customer-owned data changed, 304

25

(defn last-modified [customer-id db] (d/q '{:find [(max ?time) .] :in [$ ?cus-id %] :where [(owns? ?cus-id ?e) [?e ?a ?v ?tx] [?tx :db/txInstant ?time]]} db customer-id owner-rules))

Page 85: Exploring four Datomic superpowers

If no customer-owned data changed, 304

25

(defn last-modified [customer-id db] (d/q '{:find [(max ?time) .] :in [$ ?cus-id %] :where [(owns? ?cus-id ?e) [?e ?a ?v ?tx] [?tx :db/txInstant ?time]]} db customer-id owner-rules))

Page 86: Exploring four Datomic superpowers

If no customer-owned data changed, 304

25

(defn last-modified [customer-id db] (d/q '{:find [(max ?time) .] :in [$ ?cus-id %] :where [(owns? ?cus-id ?e) [?e ?a ?v ?tx] [?tx :db/txInstant ?time]]} db customer-id owner-rules))

Page 87: Exploring four Datomic superpowers

If no customer-owned data changed, 304

25

(defn last-modified [customer-id db] (d/q '{:find [(max ?time) .] :in [$ ?cus-id %] :where [(owns? ?cus-id ?e) [?e ?a ?v ?tx] [?tx :db/txInstant ?time]]} db customer-id owner-rules))

Page 88: Exploring four Datomic superpowers

If no customer-owned data changed, 304

25

(defn last-modified [customer-id db] (d/q '{:find [(max ?time) .] :in [$ ?cus-id %] :where [(owns? ?cus-id ?e) [?e ?a ?v ?tx] [?tx :db/txInstant ?time]]} db customer-id owner-rules))

Page 89: Exploring four Datomic superpowers

26

Cache hits are awesome, but what about cache misses?

Page 90: Exploring four Datomic superpowers

27

4 Mobile Sync

Page 91: Exploring four Datomic superpowers

desired hypermedia from the API

28

GET /accounts/1223/purchases200 OK{"purchases": [...] "_links": { "updates": "https://...?since=2014-11-10T11:12:13Z" }}

Page 92: Exploring four Datomic superpowers

serve purchases added or modified since our last sync

29

(defn updated-purchases [account-id db since] (d/q '{:find [[(pull ?pur [*]) ...]] :in [$ $since ?acc] :where [[?pur :purchase/account ?acc] [$since ?pur]]} db (d/history (d/since db since)) [:account/id account-id]))

(updated-purchases account-id db #inst "2014-11-14"))

Page 93: Exploring four Datomic superpowers

serve purchases added or modified since our last sync

29

(defn updated-purchases [account-id db since] (d/q '{:find [[(pull ?pur [*]) ...]] :in [$ $since ?acc] :where [[?pur :purchase/account ?acc] [$since ?pur]]} db (d/history (d/since db since)) [:account/id account-id]))

(updated-purchases account-id db #inst "2014-11-14"))

Page 94: Exploring four Datomic superpowers

serve purchases added or modified since our last sync

29

(defn updated-purchases [account-id db since] (d/q '{:find [[(pull ?pur [*]) ...]] :in [$ $since ?acc] :where [[?pur :purchase/account ?acc] [$since ?pur]]} db (d/history (d/since db since)) [:account/id account-id]))

(updated-purchases account-id db #inst "2014-11-14"))

Page 95: Exploring four Datomic superpowers

serve purchases added or modified since our last sync

29

(defn updated-purchases [account-id db since] (d/q '{:find [[(pull ?pur [*]) ...]] :in [$ $since ?acc] :where [[?pur :purchase/account ?acc] [$since ?pur]]} db (d/history (d/since db since)) [:account/id account-id]))

(updated-purchases account-id db #inst "2014-11-14"))

Page 96: Exploring four Datomic superpowers

serve purchases added or modified since our last sync

29

(defn updated-purchases [account-id db since] (d/q '{:find [[(pull ?pur [*]) ...]] :in [$ $since ?acc] :where [[?pur :purchase/account ?acc] [$since ?pur]]} db (d/history (d/since db since)) [:account/id account-id]))

(updated-purchases account-id db #inst "2014-11-14"))

Page 97: Exploring four Datomic superpowers

serve purchases added or modified since our last sync

29

(defn updated-purchases [account-id db since] (d/q '{:find [[(pull ?pur [*]) ...]] :in [$ $since ?acc] :where [[?pur :purchase/account ?acc] [$since ?pur]]} db (d/history (d/since db since)) [:account/id account-id]))

(updated-purchases account-id db #inst "2014-11-14"))

Page 98: Exploring four Datomic superpowers

serve purchases added or modified since our last sync

29

(defn updated-purchases [account-id db since] (d/q '{:find [[(pull ?pur [*]) ...]] :in [$ $since ?acc] :where [[?pur :purchase/account ?acc] [$since ?pur]]} db (d/history (d/since db since)) [:account/id account-id]))

(updated-purchases account-id db #inst "2014-11-14"))

Page 99: Exploring four Datomic superpowers

30

Bonus

Page 100: Exploring four Datomic superpowers

31

5 Future DBs

Page 101: Exploring four Datomic superpowers

(defn db-with-virtual-charges [purchases db] (:db-after (d/with db (mapcat purchase->virtual-charges purchases))))

generating a virtual db

32

Page 102: Exploring four Datomic superpowers

(defn db-with-virtual-charges [purchases db] (:db-after (d/with db (mapcat purchase->virtual-charges purchases))))

generating a virtual db

32

Page 103: Exploring four Datomic superpowers

(defn db-with-virtual-charges [purchases db] (:db-after (d/with db (mapcat purchase->virtual-charges purchases))))

generating a virtual db

32

Page 104: Exploring four Datomic superpowers

(defn db-with-virtual-charges [purchases db] (:db-after (d/with db (mapcat purchase->virtual-charges purchases))))

generating a virtual db

32

Page 105: Exploring four Datomic superpowers

(defn db-with-virtual-charges [purchases db] (:db-after (d/with db (mapcat purchase->virtual-charges purchases))))

generating a virtual db

32

Page 106: Exploring four Datomic superpowers

33

6 Testing

Page 107: Exploring four Datomic superpowers

db testing - locally scoped

34

(defn virtual-db [updates db] (:db-after (d/with db updates)))(let [one-account {...} one-card {...} one-purchase {...} wrong-purchase {...} db (virtual-db [one-account one-card one-purchase wrong-purchase] db)] (my-weird-db-query db) => [one-purchase])

Page 108: Exploring four Datomic superpowers

db testing - locally scoped

34

(defn virtual-db [updates db] (:db-after (d/with db updates)))(let [one-account {...} one-card {...} one-purchase {...} wrong-purchase {...} db (virtual-db [one-account one-card one-purchase wrong-purchase] db)] (my-weird-db-query db) => [one-purchase])

Page 109: Exploring four Datomic superpowers

db testing - locally scoped

34

(defn virtual-db [updates db] (:db-after (d/with db updates)))(let [one-account {...} one-card {...} one-purchase {...} wrong-purchase {...} db (virtual-db [one-account one-card one-purchase wrong-purchase] db)] (my-weird-db-query db) => [one-purchase])

Page 110: Exploring four Datomic superpowers

db testing - locally scoped

34

(defn virtual-db [updates db] (:db-after (d/with db updates)))(let [one-account {...} one-card {...} one-purchase {...} wrong-purchase {...} db (virtual-db [one-account one-card one-purchase wrong-purchase] db)] (my-weird-db-query db) => [one-purchase])

Page 111: Exploring four Datomic superpowers

db testing - locally scoped

34

(defn virtual-db [updates db] (:db-after (d/with db updates)))(let [one-account {...} one-card {...} one-purchase {...} wrong-purchase {...} db (virtual-db [one-account one-card one-purchase wrong-purchase] db)] (my-weird-db-query db) => [one-purchase])

Page 112: Exploring four Datomic superpowers

db testing - locally scoped

34

(defn virtual-db [updates db] (:db-after (d/with db updates)))(let [one-account {...} one-card {...} one-purchase {...} wrong-purchase {...} db (virtual-db [one-account one-card one-purchase wrong-purchase] db)] (my-weird-db-query db) => [one-purchase])

Page 113: Exploring four Datomic superpowers

db testing - vector of datoms

35

(fact "on last-modification-for" (let [customer-id (uuid) db [[42 :account/customer-id customer-id 99] [99 :db/txInstant #inst "2014-10-10T12:00:00.909Z" 99] [1337 :purchase/account 42 100] [1337 :purchase/id (uuid) 100] [1337 :purchase/merchant-name "Coiso" 100] [100 :db/txInstant #inst "2014-10-11T12:00:00.888Z" 100] [9001 :line-item/account 42 101] [9001 :charge/id (uuid) 101] [9001 :line-item/precise-amount 100.00M 101] [101 :db/txInstant #inst "2014-10-12T13:00:00.777Z" 101] [45 :account/customer-id (uuid) 102] [102 :db/txInstant #inst "2014-10-15T12:00:00.909Z" 102]]] (last-modification-for customer-id db) => #nu/time "2014-10-12T13:00:00.777Z"))

Page 114: Exploring four Datomic superpowers

db testing - vector of datoms

35

(fact "on last-modification-for" (let [customer-id (uuid) db [[42 :account/customer-id customer-id 99] [99 :db/txInstant #inst "2014-10-10T12:00:00.909Z" 99] [1337 :purchase/account 42 100] [1337 :purchase/id (uuid) 100] [1337 :purchase/merchant-name "Coiso" 100] [100 :db/txInstant #inst "2014-10-11T12:00:00.888Z" 100] [9001 :line-item/account 42 101] [9001 :charge/id (uuid) 101] [9001 :line-item/precise-amount 100.00M 101] [101 :db/txInstant #inst "2014-10-12T13:00:00.777Z" 101] [45 :account/customer-id (uuid) 102] [102 :db/txInstant #inst "2014-10-15T12:00:00.909Z" 102]]] (last-modification-for customer-id db) => #nu/time "2014-10-12T13:00:00.777Z"))

Page 115: Exploring four Datomic superpowers

36

7 Schema Extension

Page 116: Exploring four Datomic superpowers

schema is data!

37

(d/transact conn [{:db/valueType :db.type/string, :db.install/_attribute :db.part/db, :db/id (d/tempid :db.part/db), :db/cardinality :db.cardinality/one, :db/ident :customer/name }])

Page 117: Exploring four Datomic superpowers

:nubank/transform :pii

schema is data!

37

(d/transact conn [{:db/valueType :db.type/string, :db.install/_attribute :db.part/db, :db/id (d/tempid :db.part/db), :db/cardinality :db.cardinality/one, :db/ident :customer/name }])

Page 118: Exploring four Datomic superpowers

38

8 Sharding Reads

Page 119: Exploring four Datomic superpowers

39

sharding to the transactor

customers

notification

auth

processor

acquisition

accounts

Page 120: Exploring four Datomic superpowers

39

sharding to the transactor

customers

notification

auth

processor

acquisition

accounts

Page 121: Exploring four Datomic superpowers

customers

1 2 …

39

sharding to the transactor

customers

notification

auth

processor

acquisition

accounts

Page 122: Exploring four Datomic superpowers

customers

1 2 …

39

sharding to the transactor

customers

notification

auth

processor

acquisition

accounts

shard-specific peer cache

1

Page 123: Exploring four Datomic superpowers

customers

1 2 …

39

sharding to the transactor

customers

notification

auth

processor

acquisition

accounts

shard-specific peer cache

1

inter-shard ACID transactions

2

Page 124: Exploring four Datomic superpowers

40

9 Db Aggregation

Page 125: Exploring four Datomic superpowers

41

data science aggregation

customers

notification

auth

processor

acquisition

accounts

data science

Page 126: Exploring four Datomic superpowers

42

querying multiple databases

(defn multi-join [customer-id cus-db acc-db acq-db ntf-db] (d/q '{:find [...] :in [$cus $acc $acq $ntf ?cus-id] :where [[$cus ?cus :customer/id ?cus-id] [$acc ?acc :account/customer-id ?cus-id] [$acq ?ar :request/customer-id ?cus-id] [$ntf ?evt :event/customer-id ?cus-id] [...]]} cus-db acc-db acq-db ntf-db customer-id))

Page 127: Exploring four Datomic superpowers

42

querying multiple databases

(defn multi-join [customer-id cus-db acc-db acq-db ntf-db] (d/q '{:find [...] :in [$cus $acc $acq $ntf ?cus-id] :where [[$cus ?cus :customer/id ?cus-id] [$acc ?acc :account/customer-id ?cus-id] [$acq ?ar :request/customer-id ?cus-id] [$ntf ?evt :event/customer-id ?cus-id] [...]]} cus-db acc-db acq-db ntf-db customer-id))

Page 128: Exploring four Datomic superpowers

43

1 Audit Trail

2 Authorization

3 HTTP Cache

4 Mobile Sync

5 Future DBs

6 Testing

7 Schema Extension

8 Sharding Reads

9 Db Aggregation

Page 129: Exploring four Datomic superpowers

44

We’re hiring

Page 130: Exploring four Datomic superpowers

45

We’re hiring

Page 131: Exploring four Datomic superpowers

Lucas Cavalcanti & Edward Wible@[email protected]@nubank.com.br

Thanks!