Fullstack Clojure - case-esimerkkinä Liikenneviraston Harja-projekti
-
Upload
solita-oy -
Category
Technology
-
view
565 -
download
2
Transcript of Fullstack Clojure - case-esimerkkinä Liikenneviraston Harja-projekti
FULLSTACK CLOJURE KÄYTÄNNÖSSÄKuinka Harja-projekti päästi Clojuren sydämeensä
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
CLOJURE LYHYESTI
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!
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
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
PRAGMAATTINEN› Clojuren tietorakenteet toteuttavat vastaavat Javan
Collection rajapinnat• Clojure maailmassa luotua dataa voi käyttää Javassa sekä
toisin päin
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
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
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
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
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
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
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
YKSINKERTAINEN ESIMERKKI› “with-” makroilla tehdään usein jotain tietyssä kontekstissa
http://webjure.org/mato/
“John Hughes posts ‘Why Functional Programming Matters’ ”
Ferdinand Pauwels
Oil on canvas
1872
(collaboration from Richard Carlsson)
http://classicprogrammerpaintings.com/
CASE: HARJA
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
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
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
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
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”
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
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
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
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ä”
BACKEND: JEESQL
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
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
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
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
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
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
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
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.
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
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
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ä
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
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
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
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
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