Skip to content

Instantly share code, notes, and snippets.

@weavejester
Forked from dadair-ca/clojure-closure-help.clj
Last active August 29, 2015 14:27
Show Gist options
  • Save weavejester/b999f5cd1365cd7bc4b7 to your computer and use it in GitHub Desktop.
Save weavejester/b999f5cd1365cd7bc4b7 to your computer and use it in GitHub Desktop.
;; I'd begin by using protocols to define the I/O boundaries of the endpoint.
;; The protocols can be extended to support the database and mailer component
;; when in production, and for testing a mock implementation can be used instead.
(defprotocol UsersDatabase
(authenticate [db credentials])
(create-user [db user]))
(defprotocol Mailer
(send-email [mailer email]))
;; The most straightforward way to pattern the login and register functions
;; would be to have two arguments:
(defn login [{:keys [db]} request] ...)
(defn register [{:keys [db mailer]} request] ...)
;; So the first argument is the map of components map, and the second argument
;; the Ring request map.
(defn accounts-endpoint [{:keys [db] :as components}]
(routes
(POST "/login" []
(-> (partial login components)
(wrap-credentials-pass? db)
(wrap-validates? LoginRequest)))
(POST "/users" []
(-> (partial register components)
(wrap-taken? db)
(wrap-validates? RegisterRequest)))))
;; However, this design involves functions being given more information than
;; they strictly need. We don't need to give every function all the components,
;; nor do they require the entire request map in order to formulate a response.
;; Ideally we want functions to be passed only the information they need. So
;; with that in mind, we can rewrite the function signatures:
(defn login [db credentials] ...)
(defn register [db mailer user] ...)
;; And the endpoint:
(defn accounts-endpoint [{:keys [db mailer]}]
(routes
(POST "/login" {body :body} (login db body))
(POST "/users" {body :body} (register db mailer body))))
;; Like the protocols, this establishes a boundary in the application. The
;; login and register functions are given only the information necessary
;; for their operation.
;; This design does preclude the use of Ring middleware to extend the
;; controller functions, as in your original design. But I'm not sure that
;; middleware is not necessarily the right choice in this case.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment