No Pain - No Gain

Using Clojure for Web Applications

6
Cities

>25
Countries

>1000
Services

>2000
Employees

But why Clojure?

Simplicity

Functional

Interop

LISP

(+ 1 2)
=> 3

Immutability / Pure Funcs / Small Modules
Polyglot programming: <=> &&

Easy since 1958 / REPL / Collections & Lists

Higher Order Functions

  • (map cook [🐮 🥔 🐔 🌽])

    => [ 🍔 🍟 🍗 🍿 ]

  • (filter isVegan [🍔 🍟 🍗 🍿])

    => [ 🍟 🍿 ]

  • (reduce eat [🍔 🍟 🍗 🍿])

    => 💩

http://www.globalnerdy.com/2016/06/23/map-filter-and-reduce-explained-using-emoji/
nil
Powered by https://github.com/viebel/klipse
42   1.3   4/3 Numbers
"f"   "oo"   "bar" Characters & Strings
:foo   :bar Keywords
[]   '()   #{}   {} Collections (Vector, List, Set, Map)
nil   true   false Scalar values

          (def x 42)
          x
          => 42
          
          (defn square [num] (* num num))          
          (square 4)
          => 16

          (map square [1 2 3])
          => (1 4 9)
          
          (reduce + (map square [1 2 3]))
          ;(reduce (+ [1 2 3]))
          => 14
        
->
Request
->

Web Server

Routing Database HTML/CSS/JS
<-
Response
<-
Ring
Pedestal
Pedestal
Korma
Hiccup
Ring
Component

public class Server {

private URL url;

private Socket connection;

public Server(URL url)

public void start() {...}

public void shutdown() {...}

}

 

static config

run state

constructor

lifecycle

lifecycle

(ns my-app.foo)

(def url "...")

(def connection (atom nil))

(defn connection []
(swap! connection "..."))

(defn shutdown! []
(swap! connection "..."))

 

not instanceable

global singletons

global effects
 

global effects

Component

  • Immutable data structure
  • Managed lifecycle of stateful objects
  • Relationships to other components

          (defn system [env]
          )
        

          (defn system [env]
            (component/system-map
            :service-map (build-service-map env)
          )
        

          (defn system [env]
            (component/system-map
            :service-map (build-service-map env)
          
            :web (component/using
                  (pedestal/new-pedestal)
                  [:db :service-map]))
          )
        

          (defn system [env]
            (component/system-map
            :service-map (build-service-map env)
          
            :db (component/using
                  (postgres/new-database)
                  [:db-config])
          
            :web (component/using
                  (pedestal/new-pedestal)
                  [:db :service-map]))
          )
        

          (defn system [env]
            (component/system-map
            :service-map (build-service-map env)

            :db-config db-config                  ;; {...}
          
            :db (component/using
                  (postgres/new-database)
                  [:db-config])
          
            :web (component/using
                  (pedestal/new-pedestal)
                  [:db :service-map]))
          )
        

Pedestal

Libraries for backend & API first

          (ns hello
            (:require [io.pedestal.http :as http]
                      [io.pedestal.http.route :as route]))
          )
        

          (ns hello
            (:require [io.pedestal.http :as http]
                      [io.pedestal.http.route :as route]))

          (defn start []
            (http/start (create-server))
          )
        

          (ns hello
            (:require [io.pedestal.http :as http]
                      [io.pedestal.http.route :as route]))

          (defn create-server []
            (http/create-server
             {::http/routes routes
              ::http/type   :jetty
              ::http/port   8890}))

          (defn start []
            (http/start (create-server))
          )
        

          (ns hello
            (:require [io.pedestal.http :as http]
                      [io.pedestal.http.route :as route]))

          (def routes
            (route/expand-routes
             #{["/greet" :get respond-hello :route-name :greet]}))

          (defn create-server []
            (http/create-server
             {::http/routes routes
              ::http/type   :jetty
              ::http/port   8890}))

          (defn start []
            (http/start (create-server))
          )
        

          (ns hello
            (:require [io.pedestal.http :as http]
                      [io.pedestal.http.route :as route]))

          (defn respond-hello [request]
            {:status 200 :body "Hello, world!"})

          (def routes
            (route/expand-routes
             #{["/greet" :get respond-hello :route-name :greet]}))

          (defn create-server []
            (http/create-server
             {::http/routes routes
              ::http/type   :jetty
              ::http/port   8890}))

          (defn start []
            (http/start (create-server))
          )
        

Korma

It's just tasty SQL for Clojure!

          (defn delete! [id]
            (kc/delete article (ky/where {:id id}))))
          
          (defn reset! [id]
            (kc/exec-raw (format "UPDATE articles SET count = 0 WHERE id = %s" id)))
        

          (defn delete! [id]
            (kc/delete article (ky/where {:id id}))))
          
          (defn reset! [id]
            (kc/exec-raw (format "UPDATE articles SET count = 0 WHERE id = %s" id)))
        

Hiccup

  • Representing HTML
  • Vectors for elements
  • Maps for attributes

          (defn button->buy-more []
            [:a.btn.btn-success.buy-more-btn
             {:type "button"
              :href "/"}
             [:img.buy-more-btn-img {:src "img/shopping-cart.svg"
                                     :alt "cart"}]
             "Buy More"
            ]
          )
        

Ring

  • Simple
  • Abstracts HTTP
  • Functions for handling
    parameters, cookies and more

          (defn inc-article [{:keys [form-params]}]
            (article/inc! (:id form-params))
            (ring-resp/redirect "/")
          )
        
->
Request
->

Web Server

Route DB HTML/CSS/JS
<-
Response
<-
Ring
Pedestal
Pedestal
Korma
Hiccup
Ring
Component
https://github.com/tbsschroeder/clojure-webshop-app

📧 tobias.schroeder@metronom.com

🏙️ GatherTown 🌆
and : @tbsschroeder
@wearemetronom