component library

43
component library https://github.com/stuartsierra/component https://github.com/jordillonch/component-example @jordillonch July 2015

Upload: jordi-llonch

Post on 13-Aug-2015

199 views

Category:

Technology


1 download

TRANSCRIPT

Page 1: Component library

component libraryhttps://github.com/stuartsierra/component

https://github.com/jordillonch/component-example

@jordillonch July 2015

Page 2: Component library

Agenda

• component library introduction

• example

Page 3: Component library

Introduction

Page 4: Component library

Tiny Clojure framework for managing the lifecycle of

software components which have runtime state

Page 5: Component library

Real-world applications need to manage state

Page 6: Component library

It can be seen as a style of dependency injection using

immutable data structures

Page 7: Component library

Components and

Systems

Page 8: Component library

Components

Page 9: Component library

A component is a collection of functions which share

some runtime state

Page 10: Component library

A component is similar in spirit to the definition of an object in Object-Oriented Programming

Page 11: Component library

A component knows how to start and stop the application pieces

that have to manage state

Page 12: Component library

Some examples

• Database access: database connection

• External API service: HTTP connection pool

• Web server: a session store

• In-memory cache: a Clojure Atom or Ref

Page 13: Component library

Systems

Page 14: Component library

Components are composed into systems

Page 15: Component library

A system is a component which knows how to start

and stop other components

Page 16: Component library

It is also responsible for injecting dependencies into the components which need them

Page 17: Component library

Advantages of the Component Model

Page 18: Component library

Large applications often consist of many stateful processes which must be started and stopped in a

particular order.

The component model makes those relationships explicit and declarative

Page 19: Component library

Each component receives references only to the things it needs, avoiding unnecessary

shared state

Page 20: Component library

Easy to swap in "stub" or "mock" implementations of a component for testing purposes, without relying on time-dependent constructs, such as with-redefs

or binding, which are often subject to race conditions in multi-threaded code

Page 21: Component library

Having all state reachable via a single "system" object makes it easy to reach in and inspect any part of the application from

the REPL

Page 22: Component library

Having a coherent way to set up and tear down all the state associated with an

application enables rapid development cycles without restarting the JVM

Page 23: Component library

It can also make unit tests faster and more independent, since the cost of creating and starting a system is low

enough that every test can create a new instance of the system

Page 24: Component library

Disadvantages of the Component Model

Page 25: Component library

It is not easy to retrofit the component model to an existing

application without major refactoring

Page 26: Component library

For small applications, declaring the dependency relationships among

components may actually be more work than manually starting all the components

in the correct order

Page 27: Component library

The system map is too large to inspect visually

Page 28: Component library

The code cannot discover relationships

automatically

Page 29: Component library

Cyclic dependencies are forbidden among

components

Page 30: Component library

Some code

Page 31: Component library

(defrecord Database [host port connection] ;; Implement the Lifecycle protocol component/Lifecycle

(start [component] (println ";; Starting database") ;; In the 'start' method, initialize this component ;; and start it running. For example, connect to a ;; database, create thread pools, or initialize shared ;; state. (let [conn (connect-to-database host port)] ;; Return an updated version of the component with ;; the run-time state assoc'd in. (assoc component :connection conn)))

(stop [component] (println ";; Stopping database") ;; In the 'stop' method, shut down the running ;; component and release any external resources it has ;; acquired. (.close connection) ;; Return the component, optionally modified. Remember that if you ;; dissoc one of a record's base fields, you get a plain map. (assoc component :connection nil)))

(defn new-database [host port] (map->Database {:host host :port port}))

Page 32: Component library

(defrecord ExampleComponent [options cache database scheduler] component/Lifecycle

(start [this] (println ";; Starting ExampleComponent") ;; In the 'start' method, a component may assume that its ;; dependencies are available and have already been started. (assoc this :admin (get-user database "admin")))

(stop [this] (println ";; Stopping ExampleComponent") ;; Likewise, in the 'stop' method, a component may assume that its ;; dependencies will not be stopped until AFTER it is stopped. this))

(defn example-component [config-options] (map->ExampleComponent {:options config-options :cache (atom {})}))

Page 33: Component library

(defn example-system [config-options] (let [{:keys [host port]} config-options] (component/system-map :database (new-database host port) :scheduler (new-scheduler) :app (component/using (example-component config-options) [:database :scheduler]))))

Page 34: Component library

example-system

app

ExampleComponent

database

Database

scheduler

Scheduler

(go)

Page 35: Component library

example-systemexample-system

app

ExampleComponent

database

Database

scheduler

Scheduler

app

ExampleComponent

database

Database

scheduler

Scheduler

(reset)

Page 36: Component library

Our examplehttps://github.com/jordillonch/component-example

Page 37: Component library

Shows how component library works and how use a workflow that let you change code and

avoid the JVM restarting

Page 38: Component library

The example is just an API that exposes one

endpoint to do additions

curl http://localhost:8080/math/sum -v --data "value1=1;value2=2"

Page 39: Component library

my-system

application-api

ApplicationApiComponent

context-math-engine

MathOracleSimple

Page 40: Component library

(defn my-system []

(component/system-map

:context-math-engine (new-context-math-engine-system)

:application-api (component/using (new-application-api) [:context-math-engine])))

Page 41: Component library

Demo time :)

Page 42: Component library

Resources

https://github.com/danielsz/system

https://github.com/stuartsierra/component

https://youtu.be/13cmHf_kt-Q

http://thinkrelevance.com/blog/2013/06/04/clojure-workflow-reloaded

Page 43: Component library

Thanks