No Description

Harlan Iverson 876b0d4ea5 progress on dispatch 9 years ago
clojure-objc-sample ed949232f3 initial import 9 years ago
core 876b0d4ea5 progress on dispatch 9 years ago
ios-client ed949232f3 initial import 9 years ago
pubsub-service ed949232f3 initial import 9 years ago
resources ed949232f3 initial import 9 years ago
web-client 876b0d4ea5 progress on dispatch 9 years ago
MIT-LICENSE.txt ed949232f3 initial import 9 years ago
README.md 876b0d4ea5 progress on dispatch 9 years ago
project.clj 876b0d4ea5 progress on dispatch 9 years ago
project.org 876b0d4ea5 progress on dispatch 9 years ago

README.md

Clojure Stack

An industry grade project using clojure everywhere.

To be presented at ForwardJS 4.

Purpose

A baseline Clojure stack that runs on any and all platforms, that is nice to develop and horizontally scalable.

What's here?

  • Leiningen based multi-module project setup with modules for each platform
    • API via bidi with REPL control
    • Web via clojurescript / re-frame
    • iOS via clojure-objc / lein-objcbuild
    • Androd via clojure-android / lein-droid
    • Core via component
  • Integration with Vagrant and Marathon
  • Scalable production deploment configuration
  • Development environment mirroring production environment
  • Jenkins configuration
  • No bullshit (pull request if you disagree)

Philosophy

Claims

  • Continuous Delivery shortens the feedback loop
  • Using a single notation everywhere makes it easy to switch contexts
  • Developing in a production-like environment minimizes surprises
  • Automation is the best process documentation
  • Strong typing need not be obtrusive and static, and is necessary for any evolving system
  • EDN minimally represents all fundamental data structures; the end game of text formats
  • Clojure is easy to read and understand, and is not quite LISP (it's EDN)
  • Investment in Open Source Software leads to better software product outcomes

Choices

Clojure

The fundamental data structures representing code and data; nothing more, nothing less.

  • Same language on and between all platforms
  • core.async channels and CSP concurrency everywhere
  • EDN everywhere, successor to JSON

re-frame

Based on reagent, which using a special atom as the client side sate. Stacks a nice pattern for composable reactive processing of UI actions. Plays well with CQRS and Doman Driven Design.

component

A separation of concerns model for use on all platforms.

We use a custom lifecycle dispatcher that uses multimethods, as suggested by the author's documentation.

Actually this style is much slower than Protocol based dispatch, but very nice to work with.

(ns my.component
  (:require [clojure.tools.logging :refer [debugf infof]
            [component :refer [react-to] :as c]))
  
(defmulti react)

; namespaced keywords for dispatch
; (when (= *ns* 'my.component) (= ::my-message :my.component/my-message))

(defmethod react :component/start [component]
  (debugf "Starting my.component"))
  
(defmethod react :component/stop [component]
  (debugf "Stopping my.component"))

(defmethod react ::my-message [component event]
  (infof "Got a event, what are we going to do about it? %s" event))

; alternative syntax (experimental)
(react-to ::my-message [event]
  (infof "Got an event, what are we going to do about it? %s" event))

A comparable protocol-based approach loses some of its niceness, but may win in dispatch speed. A macro could be constructed to use an in-memory map based dispatch (ie. constant lookup time).

(ns my.component
  (:require [clojure.tools.logging :refer [debugf infof]
            [component :refer [event-key Lifecycle]]))
  
(defrecord Reactor [dispatch]
  Lifecycle
  (start [component]
    (debugf "Starting my.component"))
  (stop [component]
    (debugf "Stopping my.component"))
    
  React
  (react [component event]
    (when-let [handler (get dispatch (event-key event))]
      (handler component event))))


(defn- my-message [component event]
  (infof "Got an event, what are we going to do about it? %s" event)

(def component (->Reactor {::my-message my-message}))

;(react component event)

Marathon deployment

Deploy non-Docker packages. Nothing in particular against Docker, but I don't see the case for it with Marathon and debootstrap.

Ansible

familiar with it. yaml basaed DSL. as simple as you want it to be. downside? not edn.

Ubuntu

APT debootstrap

Tool suggestions

IntelliJ Idea with Cursive (free licenses for OSS!)

emacs with clojure-mode and org-mode

homebrew + cask

Thanks

Thanks to all the authors and contributors of the projects used and literature referenced.