Any thoughts on this protocol for client->server communication (think sente):
(ns remote)
(defprotocol Remote
:extend-via-metadata true
(send! [this msg] "Sends a message to the remote. Returns nil")
(tap-recv [this ch])
;;not sure this belongs here, but needed unless we expose ch-recv
(close! [this]))
(defn tap-send! [remote msg ch]
(let [response-ch (tap-recv remote ch)]
(send! remote msg)
response-ch))
(defn to-sente-event [msg]
[(::key msg) msg])
(defn from-sente-msg [{:keys [id ?data ?event] :as event-msg}]
{::key id
::data ?data})
(defn sente->remote [sente]
(let [mult-recv (a/mult (:ch-recv sente))]
(vary-meta sente assoc
`tap-recv (fn [this ch]
(let [t (a/chan 1 (map from-sente-msg))]
(a/pipe (a/tap mult-recv t) ch)))
`send! (fn [this msg]
((:send-fn sente) (to-sente-event msg))))))
;;usage
(send! sente-remote {::key :foo/bar
::data {:my :payload}})
(a/<!! (tap-send! sente-remote
{::key :foo/bar
::data {:my :payload :req-id 123}}
(a/chan 1 (comp (filter #(-> % ::data :req-id (= 123)))
(take 1)))))
I show the wrapping of sente. The idea is we can tap channels to get synchronous responses when needed. So with sente this avoids the need to use reply-fn, the client alone can make a reply. This also has the advantage that we can also implement Remote
for servers we don't control. It also gets around the need that sente has to wrap messages at the boundaries of the remote. There might be tradeoffs that I'm not seeing yet thoughalso open to discussion on naming of the parts of a "Event". sente uses :id
and :?data
. I can never get around thinking of :id
as a reference to an entity and not a unique "type" of a thing, so i use :key
but I don't love that either. :topic
maybe? but that might imply more of a pub/sub setup than the mult interface im going for