You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/pages/blog/tour.md
+12-12Lines changed: 12 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -19,12 +19,12 @@ heroImage: 'nrepl.jpg'
19
19
20
20
I'm currently writing some blog posts about Event Driven Architecture. As part of this series, I recreate some of the key concepts/features in Clojure without using external services.
21
21
22
-
Serendipitously, in the latest Juxt Safari 🐘 session (our internal tech talks), Hak and Jeremy presented Trip: the justreleased library providing datalog in a name-space.
22
+
Serendipitously, in the latest Juxt Safari 🐘 session (our internal tech talks), Hak and Jeremy presented Trip: the just-released library providing datalog in a name-space.
23
23
24
24
For my demo code, I needed:
25
25
26
26
- an easy local way to store events for a data sourcing example i.e an event-store,
27
-
- a way to store records for a topic on a message bus i.e a log-store (using Kafka's terminology for the storage abstraction).
27
+
- a way to store records for a topic on a message bus i.e. a log-store (using Kafka's terminology for the storage abstraction).
28
28
29
29
A perfect opportunity to have a play with Trip as the underlying, but local, back-end 🙂.
30
30
@@ -192,9 +192,9 @@ and
192
192
193
193
Pretty neat all thanks to Trip 😎
194
194
195
-
Note that we use a transaction function in to call append - the reason being thread safety. By default Trip implements its connection as an atom. The append function itself uses the database to discover the latest offsets - if we dereference the connection to get the value and the overall transact retries, then the value may have changed in a competing thread.
195
+
Note that we use a transaction function to call append - the reason being thread safety. By default Trip implements its connection as an atom. The append function itself uses the database to discover the latest offsets - if we dereference the connection to get the value and the overall transact retries, then the value may have changed in a competing thread.
196
196
197
-
It's pretty great we can do this as we explore easily some of the issues seen with partitioning of topics in Kafka. The below test shows different client threads saving to the event-store. Thanks to Clojure atom's isolation properties we will always have a consistent state file. However, we won't actually know which thread wins out and the overall final ordering of the saved documents (we do know a partitions total order). Precisely the problems encountered when working with Kafka partitions.
197
+
It's pretty great we can do this as we explore easily some of the issues seen with the partitioning of topics in Kafka. The below test shows different client threads saving to the event-store. Thanks to Clojure atom's isolation properties we will always have a consistent state file. However, we won't actually know which thread wins out and the overall final ordering of the saved documents. Precisely the problems encountered when working with Kafka partitions.
198
198
199
199
```clojure
200
200
(deftesteventstore-isolation-negative-test
@@ -214,7 +214,7 @@ It's pretty great we can do this as we explore easily some of the issues seen wi
214
214
215
215
## Retrieval
216
216
217
-
Getting the data back out requires some Datalog to pull the documents and some postsorting in Clojure.
217
+
Getting the data back out requires some Datalog to pull the documents and some post-sorting in Clojure.
218
218
219
219
```clojure
220
220
(->> (trip/q '{:find [(pull ?e [*])]
@@ -230,7 +230,7 @@ Getting the data back out requires some Datalog to pull the documents and some p
230
230
(mapv #(dissoc % :db/id:offset)))
231
231
```
232
232
233
-
Tour's `gen-datalog` macro and a helper function aids in the construction of the queries based off of the components of the composite key. The macro generates the Datalog query and the helper function does a postquery transform to tidy things up.
233
+
Tour's `gen-datalog` macro and a helper function aid in the construction of the queries based on the components of the composite key. The macro generates the Datalog query and the helper function does a post-query transform to tidy things up.
234
234
235
235
```clojure
236
236
(defmacrogen-datalog
@@ -256,7 +256,7 @@ Tour's `gen-datalog` macro and a helper function aids in the construction of the
256
256
nil))))))
257
257
```
258
258
259
-
The macro is passed an `offset-type` which is a keyword representing its namesake Clojure operation e.g.`:nthrest` provides all documents after an offset, `:take` before.
259
+
The macro is passed an `offset-type` which is a keyword representing its namesake Clojure operation e.g.`:nthrest` provides all documents after an offset, `:take` before.
260
260
261
261
And the post-query transform is simply
262
262
@@ -273,7 +273,7 @@ And the post-query transform is simply
273
273
274
274
## Trivial Read Functions
275
275
276
-
For the example blog code we can now write our respective read functions as easily as the write functions.
276
+
For the example blog code, we can now write our respective read functions as easily as the write functions.
277
277
278
278
For the event-store we need to return in order all events for a particular aggregate and remove the offset.
279
279
@@ -285,7 +285,7 @@ For the event-store we need to return in order all events for a particular aggre
285
285
(mapv #(dissoc % :offset))))
286
286
```
287
287
288
-
And for our message bus requirements we can mimic a poll which fetches a message at an offset and all after it (nthrest).
288
+
And for our message bus requirements, we can mimic a poll that fetches a message at an offset and all after it.
289
289
290
290
```clojure
291
291
(defnpoll
@@ -296,9 +296,9 @@ And for our message bus requirements we can mimic a poll which fetches a message
296
296
297
297
## Trip's Protocol's To the Rescue
298
298
299
-
In DDD we might want our event-store to both save the events it receives and to publish those events to message bus in the same "transaction". This way we are assured that subscribers to the published events will be eventually consistent.
299
+
In DDD we might want our event-store to both save the events it receives and publish those events to a message bus in the same "transaction". This way we are assured that subscribers to the published events will be eventually consistent.
300
300
301
-
Thankfully Trip supports changing out the underlying operations for a connection. The intention here is to allow people to extend Trip in new and interesting ways. Tour uses it to pivot to `refs` instead of `atoms` to manage connections. This way we can get transactional behaviour for free.
301
+
Thankfully Trip supports changing out the underlying operations for a connection. The intention here is to allow people to extend Trip in new and interesting ways. Tour uses it to pivot to `refs` instead of `atoms` to manage connections. This way, via Clojure's native support for transactions, we have transactional behaviour for free.
302
302
303
303
```clojure
304
304
(defncreate-conn [] (ref (trip/empty-db)))
@@ -391,4 +391,4 @@ The following test exercises our implementation - saving event-a's to partition
391
391
(logstore/poll (trip/db conn) "event-topic"20))))
392
392
```
393
393
394
-
One thing to note here is that we use a single document database to serve both our stores - echos of the ideas in Atomic Architecture.
394
+
One thing to note here is that we use a single Trip database to serve both our stores - in line with the [shared state](https://www.juxt.pro/blog/atomic-architecture/) idea from Atomic Architecture.
0 commit comments