This is a pretty long topic, one that I happen to be particularly interested in, too. In fact, I did an entire talk about this at ClojureD last year (maybe not the best title):
Where I tried to demo a few patterns I saw. Using exceptions or not. A pipe handling using exceptions would be like:
(defn offer-by-id [request-id]
(safe #(-> {:id request-id}
validate-id
find-offer-by-id
json-response)))
or:

Which is a similar mechanism to what you describe above.