Fork me on GitHub
Fredy Gadotti17:09:01

Hey folks, what is the recommended folder structure for a medium application using Pedestal? I saw in the book Microservices with Clojure an approach that I felt a little bit strange of using everything as interceptors. Here is the official from the book:

;; Tabular routes
(def routes #{["/alerts/email"
               :post (conj common-interceptors `auth `core/validate
                           `core/send-email `gen-events)
               :route-name :alert-email]
               :post (conj common-interceptors `auth `core/validate
                           `core/send-sms `gen-events)
               :route-name :alert-sms]})
Validation interceptor:
(def validate
  {:name ::validate

   (fn [context]
     (if-let [params (-> context :request :form-params)]
       ;; validate and return a context with tx-data
       ;; or terminated interceptor chain
       (prepare-valid-context context)
        (assoc context
               :response {:status 400
                          :body "Invalid parameters"}))))

   (fn [context ex-info]
     (assoc context
            :response {:status 500
                       :body (.getMessage ex-info)}))})
Business logic interceptor:
(def send-email
  {:name ::send-email

   (fn [context]
     (let [tx-data (:tx-data context)
           msg (into {} (filter (comp some? val)
                                {:from ""
                                 :to (:to tx-data)
                                 :cc (:cc tx-data)
                                 :subject (:subject tx-data)
                                 :body (:body tx-data)}))
           result (postal/send-message
                   {:host ""
                    :port 465
                    :ssl true
                    :user ""
                    :pass "resetme"}
       ;; send email
       (assoc context :response
              {:status 200
               :body (jp/generate-string result)})))

   (fn [context ex-info]
     (assoc context
            :response {:status 500
                       :body (.getMessage ex-info)}))})
In OO languages usually I would organize my application in something like Hexagonal Architecture, MVC or any other well spread pattern. Do you have any suggestion for organization?

Thomas Moerman18:09:30

Project #polylith is gaining traction in the clojure community as an opinionated approach to structure a clj codebase.

Fredy Gadotti18:09:05

Thank you Thomas, I will take a look!


yeah, I was going to suggest polylith myself


we are migrating to it at work


That's a bit of an open-ended question. It's like anything else, use a structure that grows with you. I would probably look for distinct sub-domains and have a namespace for each (for example, for E-commerce it might be product-catalog, product-detail, shopping-card, check-out, and so forth). I would also look for cross-cutting concerns such as auth and have a namespace for that.

Fredy Gadotti18:09:33

Thank you Howard! My initial idea was split by feature like you suggested. I prefer do it in a way where I put other files near to where they are used instead of just split by responsibilities. E.g: avoid having folders for controllers, services, adapters and so on.


My only advice is grow on demand. Don't start your project creating tons of namespaces "that will be important", like *.interceptos *.handlers.*entity *.routes... Start on a single file, once you see that there is a collection of things that make sense to have it own namespace, create it. give it a good name, not like .utils or .things-that-i-dont-like

☝️ 1

In Pedestal, everything ultimately ends up as an Interceptor (even routes!), but for many cases, there are helpers; for example, lets you define a Ring-style handler for the end of your interceptor chain.