Fullstack Clojure - case-esimerkkinä Liikenneviraston Harja-projekti

51
FULLSTACK CLOJURE KÄYTÄNNÖSSÄ Kuinka Harja-projekti päästi Clojuren sydämeensä

Transcript of Fullstack Clojure - case-esimerkkinä Liikenneviraston Harja-projekti

Page 1: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

FULLSTACK CLOJURE KÄYTÄNNÖSSÄKuinka Harja-projekti päästi Clojuren sydämeensä

Page 2: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

KUKA?› Tatu Tarvainen, Software Architect @Solita

• Innokas sulkuhenkilö ja pitkän linjan lisper• Liikenneviraston Harja-projektissa pääarkkitehtinä

• tai pääsyyllisenä, riippuen miten tuotaantoonmeno sujuu

• tatut twitterissä ja githubissa

Page 3: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

CLOJURE LYHYESTI

Page 4: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

CLOJURE› Lisp kieliperheen moderni inkarnaatio

• Ajoalustava toimii JVM• Ensimmäinen ja parhaiten tuettu

• Toimii myös selaimissa ClojureScript• Suosittu nykyään webbikehityksessä erinomaisten React bindingien myötä

• Sekä .NET ympäristössä ClojureCLR• Ei niin aktiivisesti tuettu, mutta käytetty mielenkiintoisissa kohteissa• Arcadia = Unity + ClojureCLR

• Huolenaihe: kielen suunnittelijalla ei ole juuri partaa!

Page 5: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

CLOJURE› Stabiili ja pragmaattinen kieli› 1.0 julkaistiin heinäkuussa 2009› Nyt versiossa 1.8.0 › Lisp kielenä sisältää kaikki tutut ominaisuudet:

• REPL kehitys• Voimakkaat makrot: kehitä omat rakenteesi, jos kieli ei niitä

tue• Koodi on dataa

Page 6: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

PRAGMAATTINEN› Helppo pääsy isäntäympäristön (JVM tai JS) palveluihin

• Ei tarvitse kirjoittaa kaikkia maailman kirjastoja uusiksi, jos hyvä sellainen löytyy jo alustalle

Page 7: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

PRAGMAATTINEN› Clojuren tietorakenteet toteuttavat vastaavat Javan

Collection rajapinnat• Clojure maailmassa luotua dataa voi käyttää Javassa sekä

toisin päin

Page 8: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

CLOJURE REPL› REPL = READ EVAL PRINT LOOP› Lisp tulkin ylimmän tason toiminnallisuus:› 1. Lue syötteestä yksi “form” (S-expression)› 2. Evaluoi form› 3. Tulosta evaluoitu arvo› 4. Toista alusta

Page 9: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

CLOJURE REPL› REPL kehitys on tuttu kaikista Lisp kielistä, mutta nykyään

jo arkipäivää monissa muissakin kielissä› Mahdollistaa dynaamisen ohjelmoinnin, jossa ohjelmoija

“keskustelee” ajossa olevan ohjelman kanssa ja muokkaa sitä

› Huomattavasti nopeampi feedback loop kuin perinteisessä: “kirjoita, käännä, käynnistä” loopissa

› Ajossa olevan ohjelman tila on tarkasteltavissa ja muokattavissa

Page 10: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

FUNKTIONAALINEN OHJELMOINTI?› Painotetaan dataa ja funktioita sekä niillä tehtäviä

abstraktioita› Vertaa imperatiiviseen, jossa konetta käsketään tekemään

joukko asioita• Funktionaalisessa sanotaan mitä halutaan saada tulokseksi• Deklaratiivinen ohjelmointimalli• Matemaattinen funktion malli

Page 11: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

FUNKTIONAALINEN› Funktionaalisessa yritetään välttää tai minimoida muuttuva

tila ja sivuvaikutukset• Muuttuva jaettu tila (“mutable shared state”) ja

sivuvaikutukset (“side effects”) ovat kaiken pahan alku ja juuri• Ohjelmasi ovat nyt tilakoneita• Mahdotonta sanoa pinon tai parametrien perusteella mitä tapahtuu

• Funktiot ovat yksinkertaisia, kaikki mikä niiden toimintaan vaikuttaa on se mitä parametreissä tulee sisään• Tiedät ettei mikään ulkopuolinen “taikuus” vaikuta saamaasi tulokseen• Tiedät ettei mitään muuta tapahdu kuin tuloksen laskeminen

Page 12: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

TIETORAKENTEET› Clojuren hieno innovaatio on tuoda immutable

tietorakenteet arkipäivään ja oletukseksi› Tietoa ei voi muokata sen luonnin jälkeen› Kun lisään mäppiin {“hei” 42} uuden arvon avaimelle,

luodaan uusi mäp, joka sisältää vanhan avaimen ja arvon sekä uuden

› Tehokkuus pysyy “structural sharing” avulla hyvänä› https://en.wikipedia.org/wiki/Persistent_data_structure

› http://www.infoq.com/presentations/Value-Identity-State-Rich-Hickey

Page 13: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

RAJAPINNOISTA› Funktionaalinen tyylissä korostuu kaksi asiaa: funktiot ja

data› Abstraktiot voidaan rakentaa näiden päälle› Clojure-ohjelmoinnissa käytetään usein geneerisiä mäppejä

tiedon välittämiseen› Data on geneeristä, kaikki normaalit tietorakenteita

käsittelevät funktiot toimivat› http://dev.solita.fi/2015/06/30/functional-interfaces-and-components.html

Page 14: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

MAKROT› Lispien perinteinen vahvuus ovat makrot, jotka ovat

käytännössä pieniä domain spesifisiä kääntäjiä› Makroa kutsutaan kääntämisen aikana ja se ottaa sisään

parametrit, joita ei evaluoida (toisin kuin funktioissa) ja palauttaa uuden koodirakenteen kääntäjälle

› Voi olla mielivaltaisen kompleksinen ja tehdä mitä tahansa laskentaa käännösaikana

Page 15: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

YKSINKERTAINEN ESIMERKKI› “with-” makroilla tehdään usein jotain tietyssä kontekstissa

Page 16: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

http://webjure.org/mato/

Page 17: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

“John Hughes posts ‘Why Functional Programming Matters’ ”

Ferdinand Pauwels

Oil on canvas

1872

(collaboration from Richard Carlsson)

http://classicprogrammerpaintings.com/

Page 18: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

CASE: HARJA

Page 19: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

MITÄ?› Liikenneviraston Harja-projekti

• ”Liikenneviraston vaylien kunnossapidon seurannan ja raportoinnin tietojarjestelma Harja”

• ”Iso” projekti: 6 kehittajaa talla hetkella• Clojure backend ja ClojureScript SPA frontend sekä erillinen

mobile web SPA laadunseurannan kenttätyöhön• Asioiden näyttäminen kartalla suuressa roolissa• JSON API urakoitsijoille sekä paljon integraatioita

liikenneviraston muihin järjestelmiin

Page 20: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti
Page 21: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti
Page 22: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

HARJA YHTEYDET› Useita liitoksia liikenneviraston muihin järjestelmiin ja prosesseihin

• SAMPO (taloushallinto)• Tierekisteri ja paikkatietojärjestelmä• TLOIK (ilmoitukset ja kuittaukset)• TURI (turvallisuusraportit)• TILU (tieluvat)• YHA (POT-lomake)

› On JMS-jonoja ja HTTP-kutsuja… ei onneksi mitään sen kummallisempaa

Page 23: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

HARJA YHTEYDET› Lisäksi Harja tarjoaa urakoitsijoille API:n, jonka kautta

urakoitsija mm.• Hakee omien urakoidensa tiedot (kuten suunnitellut tehtävät)• Raportoi tehtävien toteumat ja kaluston sijainnit

› Harja kytkeytyy muihin järjestelmiin, joko Liikenneviraston Sonja-väylän kautta (Sonic ESB, JMS jonot) tai HTTP- palveluiden kautta

Page 24: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti
Page 25: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

YLEISKUVA TEKNOLOGIAPINOSTA› Tietokanta

• PostgreSQL + PostGIS

› Backend• Clojure + HTTP-kit + jeesql

› Frontend• ClojureScript + Reagent +

less css

› Integraatiot• MULE Clojure

› Hostaus• Linux palvelimet Solita

Cloud Services toimesta

› Deploy• Jenkins + Ansible

Page 26: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

TIETOKANTA› PostgreSQL tietokanta (9.5 versio) sekä PostGIS laajennos› Harja sisältää paljon geometriadataa, joka tuodaan omaan

tietokantaan geometrioiksi• Hallintayksiköt (ELY-alueiden rajat)• Hoidon alueurakat• Pohjavesialueet• Sillat• Tieverkko

› Valtaosa Harjan käsittelemistä asioista sisältää “paikan”

Page 27: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

BACKEND› Ei sovelluspalvelinta, oma

Clojure überjar• http-kit • jeesql kyselyt, ei ORMeja• Component kirjasto• Tarjoaa palvelut frontendille

käyttäen transit muotoa, Clojure dataa sisään ja ulos• Palvelin on hyvin yksinkertainen

• Toistaiseksi tilaton, ei edes sessioita, koska Livin edustaproxy hoitaa session ja välittää meille käyttäjätiedon headereissa.

• Tällä hetkellä: 37 palvelukomponenttia, jotka tarjoavat 137 eri palvelua

• Palvelukutsut nimellä, esim. :hae-urakan-valitavoitteet

Page 28: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

BACKEND: COMPONENT› Stuart Sierran component kirjasto https://github.com/stuartsierra/

component› Tilanhallinnan elinkaari› Kaikki järjestelmän komponentit (tietokanta, http-palvelin, jne) sekä

palvelut ja integraatiot toteuttavat component Lifecycle › Komponentit saavat start metodissa sisään omat riippuvuutensa,

alustavat mitä tarvitsevat ja palauttavat uuden version itsestään› Stop metodissa komponentti sulkee mitä on käynnistänyt ja palauttaa

uuden version itsestään

Page 29: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

BACKEND: COMPONENT HYÖDYT › Jaetut riippuvuudet (kuten tietokanta) eivät ole globaaleja, vaan kaikki

komponentit saavat parametrit • Esim. Raportoinnin muuttaminen käyttämään read-only replicaa

tietokannasta on yksinkertainen muutos sen riippuvuuksiin. Itse komponentin ei tarvitse tietää minkä tietokannan se sai.

› Kirjasto laskee oikean järjestyksen asioiden alustukselle ja sulkemiselle› Testattavuus ja kehitys

• järjestelmä on nopea käynnistää uudelleen ilman REPLin sammuttamista• Helppo käynnistää järjestelmä (tai pieni osa siitä) testejä varten

Page 30: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

BACKEND: JEESQL (O.S. YESQL)› Tietokantakyselyt kirjoitetaan suoraan SQL:a käyttäen

• Oikea työkalu oikeaan tarpeeseen

› Ei ORM tai muita abstraktioita • Kysely palauttaa listan mäppejä, joissa sarakkeet ovat avaimina

› Tietokantakyselyt kirjoitetaan omaan .sql tiedostoon, josta jeesql luo funktiot

› Tiedät tarkalleen mitä saat ja voit testata kyselyitä SQL-tulkissa› Voit käyttää kaikkea mitä PostgreSQL tarjoaa eikä tarvitse kärsiä

“huonosta työkalun generoimasta kyselystä”

Page 31: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

BACKEND: JEESQL

Page 32: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

FRONTEND› ClojureScript Reagent sovellus (+ {less} CSS, +

OpenLayers 3)• Twitter bootstrap CSS pohjana, mutta ei ulkoisia JS widgettejä

kartan lisäksi• Paljon omia yhteisiä komponentteja, kuten oma grid

(muokattava taulukko) ja lomake• Määritellään rivin / lomakkeen kentät, niiden tyypit ja validointisäännöt

• Näytettäviä gridejä ja muokattavia lomakkeita on todella paljon, investointi alkaa maksaa itsensä takaisin. Tässä olisi potentiaalisesti uudelleenkäytettävää koodia muihinkin reagent projekteihin

Page 33: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

FRONTEND: REAGENT› ClojureScript wrapper Facebookin React.js UI-kirjastolle

• Tarjoaa vain UI:n (model->view), enemmän kirjasto kuin framework

• Yksisuuntainen data-flow

› Mahdollisuus käyttää koko React.js ekosysteemin muita komponentteja

› Hyvä suorituskyky, erityisesti immutable tietorakenteiden kanssa• Halpa samuusvertailu uudelleenrenderöinnin tarpeen

päättelyyn

Page 34: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

REAGENT TILANKÄSITTELY› Paljon atomeja ja reaktioita eri nimiavaruuksissa

• Rerender toimii yleensä hyvin, kunhan sen ymmärtää• Oppimiskäyrä vaatii elinkaaren ymmärtämistä

• Milloin kutsun funktiota vs. palautan vektorin, jonka ensimmäinen elementti on funktio

• Milloin ja minkä tiedon yli on ok sulkea (rookie mistake: render sulkee komponentin luontivaiheen parametrien yli)

• Reaktiot • Ajattelumallina, kuten Excel kaavat (mutta kaikki funktionaalisen

ohjelmoinnin voima)• Määrittelet tiedon riippuvuudet ja ne automaattisesti päivittyvät

Page 35: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

GRID JA LOMAKE› Toteutimme Harjaan oman muokattavan

gridin ja lomakekomponentin• Perusideana skeemamääritys kentälle• Lomake ja taulukko muodostuvat

automaattisesti kenttien skeemamääritysten perusteella

• Yksinkertainen dataan perustuva rajapinta: optiot, kenttien skeemat ja itse näytettävä data ovat tavallista Clojure dataa

Page 36: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti
Page 37: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti
Page 38: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

REAKTIOT› Reaktio ja asynkroninen reaktio

• Atomi, jonka tila riippuu kaavasta tai asynkronisen haun (esim. Palvelinkutsun) palauttamasta tiedosta

• Voidaan käyttää kuten atomia• Komponentti voi renderöitäessä lukea sen ja päivittyä sen

muuttuessa• Komponentti ei välitä mistä ja milloin data tulee

Page 39: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

FRONTEND: KOMMUNIKAATIO› Pääosin kommunikaatio tapahtuu AJAX kutsuilla, joissa

palvelua kutsutaan nimellä ja antamalla payload (RPC:n kaltainen, mutta core.async kanavien päällä)

› Palvelukutsu palauttaa kanavan› Käytössä ei tarvitse tietää palvelun polkua, vain nimi› Annat nimen ja dataa, saat kanavan, josta voit lukea

vastauksen

Page 40: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

FRONTEND: KOMMUNIKAATIO› Transit tietomuotona

• JSON päälle enkoodattu rikkaampi informaatiomalli• Voidaan välittää JSONin lisäksi, esim. Keywordit, päivämäärät,

setit • Laajennettavissa

• Voit määritellä omalle tyypille itse miten haluat sen kirjoitettavan ja luettavan

Page 41: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

INTEGRAATIOT› Alkuperäinen suunnitelma oli hoitaa kaikki integraatiot

MULEn päällä • Vakioratkaisu, jonka pyörittämisestä talon sisällä paljon

kokemuksia

› Meidän kokemus MULEsta on ollut huono• Kehittäminen jatkuvaa taistelua outouksien kanssa, jyrkkä

oppimiskäyrä• MULEn vaikeuksien takia ja sen erot muusta käytetystä

teknologiasta aiheuttivat sen, että kaikki kehittäjät eivät koskeneet siihen.

Page 42: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

INTEGRAATIOTUNNELIN PÄÄSSÄ VALOA› Koska MULEn päällä oli suuria

ongelmia, päädyttiin suosimaan Clojure backendiä myös uusissa integraatioissa• Ensimmäisenä JMS-

komponentti Sonja-väylään liittymiseksi

• Huomattava parannus kehityskokemuksessa (korvin kuultava kiroilun väheneminen)

› Integraatiologiikka selkeästi luettavaa Clojurea (XML flow tiedostojen sijasta) ja riippuvuudet component kirjastolla

› Ei JAXB koodigenerointia ja annotaatioita, XML sanoman saa hyvin kätevästi Hiccup muodossa

Page 43: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti
Page 44: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

INTEGRAATIOT: API› Oma JSON API, joka on kuvattu RAMLilla sekä payloadit

ovat JSON Schema draft 4 mukaisia• Työkalut hieman “huteria”• Kirjoitettiin oma json-schema validaattori, jolla saatiin APIn

käyttäjille hyvät virheviestit, jos payload ei ole validi: puuttuvat kentät, väärän tyyppiset arvot ilmoitetaan selkokielellä ja JSON-puun polun kanssa• https://github.com/tatut/json-schema

Page 45: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

KOKEMUKSIA› Alusta lähtien oli tiedossa, että Clojure-projektia ollaan

tekemässä• ClojureScript ja Reagent ei ollut “esivalittu”• Fronttiteknologiaa mietittiin ja esim. Angular oli vaihtoehto• Tiimi (alussa minä ja Jarno) oli kuitenkin valmis oppimaan

uutta ja lähtemään paremman vaihtoehdon pariin• Erittäin tyytyväisiä teknologiavalintaan• Reagent #21 top 100 Clojure libraries listalla ja sen ympärille on tullut

yhteisöä ja omaa ekosysteemiä

Page 46: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

FULLSTACK SYNERGIA› Kommunikaatio on helppoa kun molemmissa päissä käsitellään

Clojure dataa (ei JSON)• Frontilta voidaan kutsua palvelua nimellä (k/post! :tallenna-

havainto {…havainnon tiedot…})• Palvelin palauttaa suoraan Clojure dataa

› Skeemoja (prismatic schema) ja yleistä logiikkaa voidaan jakaa suoraan Clojuren ja ClojureScriptin välillä• .cljc käytössä, vielä vähäistä mutta kasvussa• Korvattaneen clojure.spec määrityksillä kunhan 1.9 on ulkona

Page 47: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

FULLSTACK SYNERGIA› Sama kieli ja samat tietorakenteet helpottaa ajattelua

• Transit on hyvä muoto tiedon välitykseen

› Melko suuri järjestelmä• *.cljs rivimäärä: 23k (+ ~2000 riviä omia .less määrittelyjä)• *.clj rivimäärä: 29k (joista 9k rivejä testeissä)• *.sql rivimäärä: 7,5k• *.cljc rivimäärä: 4,2k jaettuja utilityjä ja domain asioita

› Ei kuitenkaan “isomorphic <kieli>” • Selkeä ero ajoympäristön mukaan, jota ei yritetä piilottaa

Page 48: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

MILLOIN CLOJURE?› Aina, mutta etenkin

• jos tiimi on halukas oppimaan uutta• jos funktionaalinen ajattelutapa on tuttu• jos haluaa järkevämmän kielen myös selaimeen• Pitää nopeasti edit/feedback syklistä ja välittömästä

palautteesta• Figwheel live-reload selaimessa on

Page 49: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

KIPUKOHTIA› Uudelleenkäytettäviä kolmannen osapuolen komponentteja on oli

vähän › Kolmannen osapuolen JS-kirjastot joita ei ole paketoitu

ClojureScript käyttöön on tuskaa (Google Closure advanced compilation externit)

› Clojurea ei opi yhdessä yössä• Vaatii jonkun, jolta voi matalalla kynnyksellä kysyä ja parikoodata

ongelmien yli• Noin 2kk, että uusi kaveri on itsenäisesti tuottava

Page 50: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti

ONE MORE THING› Harjan lähdekoodi on nyt avointa (30.5.2016 lähtien)› Julkaistu githubissa liikenneviraston organisaatiossa› EUPL 1.2 lisenssi› https://github.com/finnishtransportagency/harja

Page 51: Fullstack Clojure  - case-esimerkkinä Liikenneviraston Harja-projekti