Fork me on GitHub
#core-async
<
2018-09-18
>
nifflor07:09:41

Hello, i have a kind of design question. I am building a small compojure app for collecting BI data. It returns 204 immediately and uses async/chan and a producer/consumer pattern to handle the request asynchronously. Here is a minimal working example (`lein new compojure mwe`, put this in the src/mwe/handler.clj, update dependencies in project.clj and lein ring server headless):

(ns mwe.handler
  (:require [clojure.core.async :as async]
            [compojure.core :refer :all]
            [ring.middleware.defaults :refer [wrap-defaults api-defaults]]))
 
;; instantiate channel
(def channel (ref (async/chan 1000)))

(defn producer-handler
  "Returns 204 and puts request on channel for async processing"
  [request-map]
  (do
    (async/go (async/>! (deref channel) request-map))
    {:status 204}))

(defn consumer
  "Minimal working example of a consumer. 'Processes' the request"
  []
  (async/go-loop
    []
    (when-let [request (async/<! (deref channel))]
      (println request)
      (recur))))

;; instantiate consumer
(def consumer-instance (consumer))

(defroutes app-routes
  (GET "/" [] "Hello there")
  (GET "/collect" r (producer-handler r)))

;; instantiate app
(def app
  (wrap-defaults app-routes api-defaults))
The question i have is the following: The code looks to be pretty much like something i would do in object oriented programming. I instantiate a producer 'object' (the routes/app) a channel 'object' (with a ref) and a consumer 'object' in the form of an infinite loop. Now this works, but I wonder if there is a more clojure-eske way of doing such things. Many toy examples i found on the interwebz instantiate channels on the fly in a let binding and consume them straight away, but i don't see how i would do that in this scenario. I can also live with an 'all is well' answer. It is simply hard to judge if the question is 'Is there a better way' instead of 'How do i do this?'