Tatu Puskala Mikropalveluarkkitehtuuri toiminnanohjausjärjestelmän toteutuksessa Vaasa 2020 Tekniikan ja innovaatiojohtamisen yksikkö Ohjelmistotekniikka, diplomityö 2 VAASAN YLIOPISTO Tekniikan ja innovaatiojohtamisen yksikkö Tekijä: Tatu Puskala Tutkielman nimi: Mikropalveluarkkitehtuuri toiminnanohjausjärjestelmän toteu- tuksessa Tutkinto: Diplomi-insinööri Oppiaine: Ohjelmistotekniikka Työn valvoja: Jouni Lampinen Työn ohjaaja: Magnus Sundell Valmistumisvuosi: 2020 Sivumäärä: 71 TIIVISTELMÄ: Mikropalveluarkkitehtuuri on ohjelmistoarkkitehtuuri, jossa tietojärjestelmät koostuvat pienistä itsenäisesti toimivista osista, jotka yhdessä toteuttavat järjestelmän toiminnan. Tämän diplomi- työn aiheena on mikropalveluiden kokeilu pienen kehitystiimin ohjelmistoprojektissa. Työ suo- ritetaan vaasalaiselle ohjelmistoalan yritykselle. Työn tarkoituksena on kartoittaa arkkitehtuurin vaatimia teknologioita ja toimintatapoja sekä selvittää, kannattaako arkkitehtuuria hyödyntää yrityksen tämänhetkisissä ja tulevissa ohjelmistoprojekteissa. Työ suoritettiin testaamalla mikropalveluarkkitehtuuria pilvipalveluna toteutettavan toiminnan- ohjausjärjestelmän toteutuksessa. Projekti aloitettiin perinteisellä monoliittisella arkkitehtuu- rilla, joka oli tarkoitus siirtää myöhemmin mikropalvelupohjaiseksi. Suunnitteluratkaisuissa py- rittiin myös huomioimaan tuleva siirtymä mikropalvelupohjaiseen arkkitehtuurin. Projektin ede- tessä mikropalveluarkkitehtuurista luovuttiin ja sovellus toteutettiin monoliittisena. Jatkokehi- tystä varten kartoitettiin suunnitelma järjestelmän jatkokehityksestä mikropalvelupohjaiseksi. Järjestelmästä toteutettiin demoversio, jossa yksi tietojärjestelmän osa eristettiin omaksi mik- ropalvelukseen. Projektin kohteena ollut toiminnanohjausjärjestelmä siirtyi ensimmäisen asiakkaan käyttöön monoliittisena järjestelmänä. Lisäksi toteutettiin suunnitelma sovelluksen jatkokehittelystä mik- ropalvelupohjaiseksi sekä testiversio mikropalvelujärjestelmästä. Mikropalveluarkkitehtuuri on aikaa vievä menetelmä etenkin siihen tottumattomalle kehitystiimille. Arkkitehtuuriin siirtymi- sen alkuvaiheen kustannukset ovat korkeat. Mikropalveluiden hyödyt tulevat esiin pääasiassa vasta laajoissa ja pitkäaikaisissa ohjelmistoprojekteissa. Edellytyksenä mikropalveluarkkitehtuu- rin käyttämiselle voidaan pitää yrityksen näkökulmasta valmiutta korkeisiin alkuvaiheen kustan- nuksiin sekä riittävän laajan toimialan omaavaa sovellusta. Mikropalveluita ei suositella käytet- täväksi yrityksen tämänhetkisessä tilanteessa. AVAINSANAT: Ohjelmistoarkkitehtuuri, mikropalveluarkkitehtuuri, toiminnanohjausjärjes- telmä 3 Sisällys 1 Johdanto 8 2 Taustaa 11 2.1 Monoliittinen arkkitehtuuri 12 3 Mikropalveluarkkitehtuuri 15 3.1 Mikropalveluarkkitehtuurin vahvuudet 16 3.2 Mikropalveluarkkitehtuurin heikkoudet 18 3.3 Milloin mikropalvelut 20 4 Käytännön toteutus 24 4.1 Monoliittisella arkkitehtuurilla aloittaminen 24 4.2 Suoraan mikropalveluilla aloittaminen 25 4.3 Palvelujen mallinnus 26 4.4 Monoliitin pilkkominen 29 4.5 Integraatio 31 4.6 Testaus 33 4.7 Julkaiseminen 34 4.8 Valvonta 37 5 Sovelluksen tämänhetkinen tilanne 39 5.1 Sovelluksen vaatimukset ja ominaisuudet 39 5.2 Sovelluksen rakenne 41 5.2.1 Organization 41 5.2.2 OrganizationProxy 42 5.2.3 Employment 43 5.2.4 TimeLog 44 5.2.5 TimeRule 45 5.2.6 DrivingLog 46 5.2.7 Identity 47 5.2.8 Permission 47 5.3 CQRS 48 4 5.4 Docker-kontit 50 5.5 Jatkuva integraation ja toimitus 51 5.6 Nykytilanne 52 6 Monoliitin pilkkominen ja demoversio 54 6.1 Rakenteen analyysi 54 6.2 Lähestymiskohdat 56 6.3 Suunnitelma 58 6.4 Demoversio 58 7 Tulokset 61 8 Johtopäätökset 65 Lähteet 68 5 Termit ACID Atomicity, Consistency, Isolation, Durability, eli atomisuus eheys, eristyneisyys ja pysyvyys. Tietokantatransaktioiden ominaisuudet, joilla pyritään varmistamaan tieto- kannan eheys myös vikatilanteissa. API Application Programming Interface, Ohjelmistorajapinta, määritelmä, jonka mu- kaan ohjelmat voivat kommunikoida keskenään. Back end Palvelimen puolella toimiva osa sovellusta. Callback-funktio Ohjelmointikielen funktio, joka annetaan parametriksi toiselle funk- tiolle. CD Continuous Delivery, Jatkuva toimitus, menetelmä, joka mahdollistaa sovelluksen julkaisuemisen tuotantoon helposti. CI Continuous Integration, Jatkuva integraatio, menetelmä, jossa jokainen versiohal- linnan tietovarastojen säiltytyspaikkaan tehty muutos aiheuttaa testien ajamisen. CQRS Command Query Responsibility Segregation, suunnitteluperiaate, jossa sovelluk- sen luku- ja kirjoitusmallit erotetaan toisistaan. CRUD Create, Read, Update and Delete, relatatiotietokannan perusoperaatiot: tietuei- den luominen, lukeminen, päivittäminen sekä poistaminen. Docker Työkalu sovellusten luomiseen, julkaisemiseen ja ajamiseen ohjelmistokon- teissa. Docker-kontti Docker-työkalun käyttämä ohjelmistokontti. 6 Front end Sovelluksen käyttäjän puolella toimiva osa sovellusta. HTTP Hypertext Transfer Protocol, selainten ja WWW-palveluiden käyttämä viestintä- protokolla. Hypervisor Ohjelmisto, laiteohjelmisto, tai laitteisto, joka luo ja ajaa virtuaalikoneita. Laravel Avoimen lähdekoodin web-ohjelmointiin tarkoitettu PHP-pohjainen sovel- luskehys. Lumen Laravel-pohjainen erityisesti mikropalveluiden toteuttamiseen tarkoitettu sovelluskehys. Ohjelmistokontti, kontti Standardoitu ympäristö, jossa sovellus voidaan ajaa. ORM Object Relational Mapping, ohjelmointitekniikka, joka mahdollistaa datan lukemi- sen ja kirjoittamisen tietokantaan olioperusteisen ohjelmointikielen olioiden kautta. PHP PHP: Hypertext Preprocessor, erityisesti verkkosovellusten kehitykseen soveltuva komentokieli. REST Representational State Transfer, arkkitehtuuri verkkopalveluiden rajapinnoille. SaaS Software as a Service, keskitetyssä tuotantoympäristössä toimiva sovellus, jota tarjotaan asiakkaille palveluna. 7 Kuviot Kuva 1. Esimerkki monoliittisen järjestelmän arkkitehtuurista komponentti- ja liitinnäkymästä. (Annett 2014) ....................................................................................... 12 Kuva 2. Tavanomainen monoliittisen ohjelman rakenne (Fowler, 2016) ....................... 13 Kuva 3. Mikropalvelupohjaisen ja monoliittisen arkkitehtuurin vaikutus tuottavuuteen (Fowler, 2015a). .............................................................................................................. 20 Kuva 4. Saman sovelluksen kaksi mallia rajatuissa konteksteissaan (Fowler, 2014). ..... 27 Kuva 5. Karkeajakoinen ’Warehouse’ rajattu konteksti piilottaa sisäiset mikropalveluna ulkomaailmalle (Newman, 2015a) .................................................................................. 28 Kuva 6. Virtuaalikoneiden ja konttien erot (Merkel, 2014). ........................................... 36 Kuva 7. Mikropalveluiden valvontajärjestelmä (Richardson, 2018). .............................. 38 Kuva 8. Organization-moduulin rakenne. ....................................................................... 41 Kuva 9. OrganizationProxy-moduulin rakenne. .............................................................. 42 Kuva 10. Employment-moduulin rakenne. ..................................................................... 43 Kuva 11. TimeLog-moduulin yksinkertaistettu malli ...................................................... 44 Kuva 12. TimeRule-moduulin yksinkertaistettu malli. .................................................... 45 Kuva 13. DrivingLog-moduulin domain-malli. ................................................................ 46 Kuva 14. Permission-moduulin rakenne. ........................................................................ 47 Kuva 15. Sovelluksen kirjoitusmalli. ............................................................................... 49 Kuva 16. Sovelluksen lukumalli. ..................................................................................... 50 Kuva 17. CI/CD-ketjun tuotantotyökulku. ...................................................................... 52 8 1 Johdanto Ohjelmistokehitys on nuori ja nopeasti muuttuva ala. Ohjelmistoille esitetään yhä kasva- via vaatimuksia. Teknologian kehitys auttaa omalta osaltaan vastaamaan kasvaviin haas- teisiin. Skaalautuvien, hajautettujen järjestelmien toteuttaminen on nykyään huomatta- vasti helpompaa kuin aiemmin. Toisaalta jatkuvasti uudistuvan teknologian mahdolli- suuksien tehokas hyödyntäminen vaatii erilaisia toimintatapoja myös ohjelmistokehityk- sessä. Perinteisessä monoliittisessa arkkitehtuurissa sovellus ajetaan yhdessä prosessissa usein yhteen tietokantaan integroituna. Monoliittisen arkkitehtuurin ongelmat tulevat esiin pitkäaikaisissa, suurikokoisissa projekteissa. Hyviä suunnitteluperiaatteita noudattaen- kin isojen projektien kehityskustannukset kasvavat ja toimintavarmuus heikkenee (New- man, 2015). Teknologian kehittyessä ja kehitystiimien eläessä vanhaan teknologiaan si- dotut pitkäikäiset ohjelmistoprojektit paisuvat helposti vaikeasti muutettaviksi, raken- teeltaan epäkiinteiksi (uncohesive) ohjelmiksi, joiden ylläpidosta ja päivittämisestä tulee jatkuva taakka. Mikropalveluarkkitehtuuri on ohjelmistoarkkitehtuurin suunta, joka pyrkii ratkaisemaan monoliittisen arkkitehtuurin ongelmia. Mikropalveluarkkitehtuurissa ohjelmat koostu- vat mikropalveluista. Mikropalvelut ovat hienojakoisia, itsenäisesti julkaistavia ja omassa prosessissaan suoritettavia ohjelmia (Lewis, 2015). Mikropalvelut kommunikoivat stan- dardoituja data- ja viestintäprotokollia sekä julkaistavia rajapintoja käyttäen. (Tyszbero- wics, 2018). Mikropalveluajattelun ytimessä on skaalautuvuus sekä sovelluskehityksen ketteryys. It- senäisten ja hienojakoisten mikropalvelujen skaalaus on tehokkaampaa kuin monoliitti- sen sovelluksen (Newman, 2015a). Eavesin (2014) mukaan hyvin toteutetun mikropalve- lun täydelleen uusiksi kirjoittamiseen tarvittava aika mitataan viikoissa. Laajan mikropal- velupohjaisen ohjelmiston muuttaminen ja ylläpito on näin ollen huomattavasti halvem- paa kuin monoliittisen. 9 Tämä työ tehdään toimeksiantona vaasalaiselle ohjelmistoalan yritykselle Black Label By- tes Oy:lle. Työn tekijä on työsuhteessa yritykseen. Yritys on toiminut alalla muutaman vuoden. Yrityksen sovelluskehitystiimi koostuu neljästä työntekijästä, joista kaksi jäsentä on kokeneempia ja toiset kaksi vähemmän kokeneita sovelluskehittäjiä. Tiimillä ei ole ai- kaisempaa kokemusta mikropalveluista. Työssä keskitytään mikropalveluarkkitehtuuriin toiminnanohjausjärjestelmän toteuttamisprojektissa. Järjestelmä toteutetaan modulaa- risena, eri asiakkaiden tarpeisiin mukautettavana pilvipalveluna. Projektia käytetään mikropalveluarkkitehtuurin kokeilualustana. Työn alkuperäisenä tavoitteena oli tuottaa ensimmäinen versio case-projektina käyte- tystä toiminnanohjausjärjestelmästä mikropalvelupohjaisena. Tavoitteesta kuitenkin luovuttiin aikataulupaineiden sekä mikropalveluarkkitehtuurin kannattavuuden epävar- muuden takia. Sen sijaan selvitettiin monoliittisena kehitetyn järjestelmän mikropalve- lupohjaiseksi siirtämisen edellytykset ja kannattavuus. Selvityksen lisäksi kokonaisen mikropalvelupohjaisen järjestelmän kehittämisen sijaan tavoitteeksi otettiin pienimuo- toisen demoversion toteuttaminen. Demoversiossa pieni osa monoliittisena toteutettua toiminnanohjausjärjestelmää erotetaan omaksi mikropalvelukseen. Työn tutkimuskysymykset ovat: 1. Kannattaisiko yrityksen ottaa käyttöön mikropalveluarkkitehtuuri case-projektissa tai samankaltaisissa tulevissa projekteissa? 2. Mitä käytännön asioita on huomioitava mikropalvelupohjaista järjestelmää kehittä- essä? Työn toteutus voidaan jakaa kahteen vaiheeseen. Ensimmäisessä vaiheessa suunnitteilla oli vielä koko sovelluksen toteutus mikropalvelupohjaisena. Tässä vaiheessa toiminnan- ohjausjärjestelmää kehitettiin aluksi monoliittisena pyrkien kuitenkin siihen, että se olisi 10 helposti siirrettävissä mikropalvelupohjaiseksi. Mikropalveluarkkitehtuuria varten otet- tiin käyttöön myös useita sen edellyttämiä infrastruktuuriratkaisuja. Kun ajatuksesta to- teuttaa järjestelmä tuotantoon mikropalvelupohjaisena luovuttiin, koodikantaa ei ollut enää tarvetta pitää helposti mikropalvelupohjaiseksi siirrettävänä. Toinen vaihe tehtiin vajaa vuosi sen jälkeen, kun järjestelmä päätettiin toteuttaa mono- liittisena. Mahdollisen siirtymän edellytykset kartoitetaan. Sovelluksesta toteutetaan myös demoversio, jossa yksi osa palvelua irrotetaan omaksi mikropalvelukseen. Demo- versiossa havainnollistetaan ja kokeillaan arkkitehtuurin vaatimia teknisiä ratkaisuja. Työn teoriaosuus käsittää luvut kaksi, kolme ja neljä. Luvussa kolme käsitellään yleistä teoriaa mikropalveluista, niiden hyvistä ja huonoista puolista sekä tilanteista, jolloin mik- ropalveluita kannattaa hyödyntää. Luvussa neljä käsitellään mikropalveluarkkitehtuurin käytännön toteutukseen ja vaatimuksiin liittyvää teoriaa. Luvussa viisi esitellään projek- tin ensimmäisessä vaiheessa tehty työ. Luvussa kuusi esitellään suunnitelma järjestel- män siirtämisestä mikropalveluksi sekä mikropalvelupohjaisen järjestelmän demoversio. Luvuissa seitsemän ja kahdeksan esitellään työn tulokset ja tärkeimmät johtopäätökset. 11 2 Taustaa IEEE:n standardi määrittelee ohjelmistoarkkitehtuurin järjestelmän korkean tason orga- nisaatioksi, joka koostuu järjestelmän osista, näiden osien välisistä suhteista ja järjestel- män suunnittelun ja evoluution periaatteista (IEEE 2000). Käytännössä termiä käytetään vaihtelevasti kontekstin mukaan. Koskimiehen ja Mikkosen (2005) mukaan arkkitehtuuri ei välttämättä suoraan määritä koodikannan rakennetta, vaan se voi olla myös olla hyvin korkean tason dokumentti järjestelmän suunnitteluperiaatteista. Fowler (2003) pitää oh- jelmistoarkkitehtuuria sovelluskehittäjien jaettuna näkemyksenä järjestelmän suunnit- telusta. Tähän näkemykseen kuuluu se, miten järjestelmä jaetaan komponentteihin ja miten komponentit ovat vuorovaikutuksessa keskenään. Arkkitehtuurin määrittävät komponentit koostuvat käytännössä usein pienemmistä komponenteista, mutta arkki- tehtuuri ei määritä näitä. Clement, Garlan, Bass ja Stafford (2010) määrittelvät ohjelmistoarkkitehtuurille abstrak- tiotason mukaan eri näkymiä. Näkymät koostuvat elementeistä ja näiden välisestä vuo- rovaikutuksesta. Koodikannan rakennetta määrittelevää näkymää kutsutaan moduu- linäkymäksi. Diplomityössä keskitytään ohjelmistoarkkitehtuuriin pääasiassa komponentti ja liitin -ta- son näkymässä. Tässä näkymässä komponentit ovat järjestelmän ajonaikaisia prosesseja ja liittimet kuvaavat näiden komponenttien välistä kommunikaatiota (Clement et al., 2010). Kuvassa 1 on kuvattu monoliittisen sovelluksen back end -puoli komponentti ja liitin -tason näkymässä. https://www.sanakirja.org/search.php?id=21702&l2=3 12 2.1 Monoliittinen arkkitehtuuri Kuva 1. Esimerkki monoliittisen järjestelmän arkkitehtuurista komponentti- ja liitinnäkymästä. (Annett 2014) Monoliittisessa järjestelmässä (myöh. monoliitti) järjestelmän oma koodikanta toimii yh- tenä kokonaisuutena (Fowler, 2016). Kuvassa 2 havainnollistetaan tavanomaisen mono- liittisen sovelluksen rakennetta, joka on jaettu front end ja back end -puoleen sekä tie- tokantaan. Front end ja back end yhdistetään yleensä samaan koodikantaan. Front end -puoli lähettää pyyntöjä back end -puolelle, joka toteuttaa ohjelman varsinaisen logiikan. Erillinen tietokanta suoritetaan omassa prosessissaan. Mikäli järjestelmä ei tarvitse eril- listä tietokantaa, voidaan myös tiedon varastointi suorittaa samassa prosessissa muistia käyttäen. 13 Kuva 2. Tavanomainen monoliittisen ohjelman rakenne (Fowler, 2016) Monoliittinen arkkitehtuuri on perinteinen tapa toteuttaa ohjelmistoja. Kehitystyökalut on suunnattu monoliittisten ohjelmien toteuttamiseen (Richardson, 2013). Kehittäjät ovat myös tottuneet ohjelmoimaan monoliittisia ohjelmia. Muut monoliittisen arkkiteh- tuurin edut liittyvät sen yksinkertaisuuteen. Testaus on helppoa, sillä testausta varten tarvitsee suorittaa vain yksi ohjelma. Monoliitin julkaisu on yksinkertaista, suoritettava tiedosto tai kansio vain kopioidaan palvelimelle. Ohjelmia voidaan skaalata vertikaalisesti tai horisontaalisesti. Horisontaalisessa skaa- lauksessa ohjelma ajetaan tehokkaammalla palvelimella (Fowler, 2016). Monoliitin hori- sontaalinen skaalaus toteutetaan ajamalla kopioita ohjelmasta eri palvelimilla. Ainoa tar- vittava lisäosa on kuormantasaaja, joka jakaa pyynnöt eri palvelimille (Richardson, 2013). Monoliitin ongelmat tulevat esille useimpien ohjelmien elinkaaren aikana. (Fowler, 2016). Muutosten tekeminen vaikeutuu koodikannan kasvaessa. Laajassa koodikannassa pienikin muutos voi aiheuttaa odottamattomia ongelmia. Toiminnan varmistaminen vaa- tii suurta määrää integraatiotestejä. Ongelmia pyritään ehkäisemään käyttämällä suun- nitteluperiaatteita, jotka kasvattavat koodin kiinteyttä (cohesion) (Newman, 2015a). Kiin- teään rakenteeseen pyritään abstraktioilla ja ryhmittämällä toisiinsa koodin liittyvät osat moduuleihin. Rodgerin (2018) mukaan lähes kaikissa ohjelmistoprojekteissa joudutaan aika-ajoin jous- tamaan hyvistä suunnitteluperiaatteista, esimerkiksi aikataulupaineiden takia. Tästä ai- heutuvaa eroa suunnitellusta ohjelmiston tasosta käytäntöön verrattuna kutsutaan tek- 14 niseksi velaksi. Velka olisi tarkoitus kuroa umpeen refaktoroimalla. Käytännössä pitkäai- kaisissa monoliittisissa projekteissa tekninen velka kuitenkin kasautuu ja vaikeuttaa en- tisestään ohjelmiston jatkokehitystä. Ohjelmiston kasvaessa kehittäjien on yhä vaikeampi ymmärtää laajaa, paljon riippuvuus- suhteita sisältävää koodikantaa (Richardson, 2018). Uusilla kehittäjillä kestää kauan päästä projektiin sisään. Rakennetta heikosti ymmärtävät kehittäjät tekevät huonoja suunnitteluratkaisuja, jotka vaikeuttavat koodikannan ymmärtämistä entisestään. Tämä johtaa kierteeseen, joka tekee kehitystyöstä projektin edetessä aina vain hitaampaa. Richardsonin (2018) mukaan monoliitin skaalaus on tehotonta. Ohjelman osien vaatimat resurssit vaihtelevat. Jotkin osat saattavat tarvita suurta muistimäärää, kun jotkut taas vaativat tehokkaampaa suoritinta. Vertikaalinen skaalaus joudutaan siis tekemään näi- den eniten resursseja syövien ohjelman osien ehdoilla. Horisontaalisessa skaalauksessa koko ohjelma joudutaan kopioimaan muutaman enemmän resursseja vaativan osan ta- kia. Monoliittisen ohjelman julkaisussa koko ohjelma joudutaan julkaisemaan kerralla (Richardson, 2018). Suuret, paljon muutoksia sisältävät julkaisut ovat riskialttiita ja hi- taita. Newmanin (2015a) mukaan tämä johtaa pidempiin julkaisuväleihin. Julkaisuvälien kasvaessa julkaisujen koot kasvavat entisestään, mikä taas kasvattaa riskejä entisestään. Monoliittisen ohjelman ongelmat liittyvät siis kokoon ja monimutkaisuuteen. Ongelmat eivät tule esille pienissä ohjelmissa ja projektien alkuvaiheessa. Koodikannan kasvaessa kasvavat myös sekä ohjelman kehitys- että ylläpitokustannukset. 15 3 Mikropalveluarkkitehtuuri Rodger (2018) määrittelee ajattelutavan ohjelmasta komponenteista koottavaksi koko- naisuudeksi mikropalveluajattelun takana olevaksi perusideaksi. Mikropalveluarkkiteh- tuurissa nämä komponentit ovat mikropalveluita. Jokainen näistä mikropalveluista voi- daan julkaista erikseen, ja se toimii omassa prosessissaan (Newman, 2015a). Mikropal- velut kommunikoivat keskenään API-kutsuilla verkon yli. Lewisin ja Fowlerin (2014) mukaan mikropalveluarkkitehtuurille ei ole yksityiskohtaista määritelmää. He kuitenkin määrittelevät ominaisuuksia, jotka yhdistävät useimpia mik- ropalvelupohjaisia arkkitehtuureja: Palveluiden ja kehitystiimien jakaminen toimialan mukaan. Monissa organisaatioissa kehitystiimit jaetaan teknologioiden mukaan esimerkiksi front end-, back end- ja tieto- kantatiimeihin. Mikropalveluarkkitehtuurissa kehitystiimit vastaavat koko palvelustaan. Myös palvelut jaetaan mallinnettavien toimialan toiminnallisuuksien mukaan. Ohjelmien ajattelu tuotteina eikä projekteina. Ohjelmistokehitystä ei ajatella projektina, jolla on alku ja selvä loppu. Kehittäjät ovat vastuussa ohjelmasta koko sen elinkaaren ajan. Evolutiivinen suunnittelu. Ohjelmalle ei suunnitella etukäteen tiukkaa arkkitehtuuria, vaan ohjelmaa ajatellaan jatkuvasti kehittyvänä prosessina. Muutos nähdään olennai- sena osana ohjelmistojen kehitystä. Ohjelmat kehitetään helposti muutettavaksi. Hajautettu datan hallinta. Tavoitteena on, että jokainen mikropalvelu sisältää oman tie- tokantansa. Mikropalvelut voivat myös käyttää eri tietokantateknologioita. Hajautettu hallinnointi. Pyrkimyksenä on, että palveluiden sisäistä toteutusta ei hallita. Mikropalveluiden kehittäjät voivat vapaasti valita teknologian, jolla palvelu toteutetaan. 16 Kevyet kommunikaatiomekanismit. Mikropalveluiden välinen kommunikaatio pyritään toteuttamaan yksinkertaisilla ja kevyillä mekanismeilla. Ohjelman ydinlogiikka toteute- taan palveluissa. Infrastruktuurin automaatio. Suuren ja monimutkaisen mikropalveluiden verkon integ- roiminen helpottuu huomattavasti automatisoituja infrastruktuuriratkaisuja käyttämällä. Jatkuva integraatio (CI, continous integration) ja toimitus (CD, continous delivery) yksin- kertaistavat mikropalveluiden julkaisemista ja testaamista. Arkkitehtuurin käyttäjät hyö- dyntävät automatisoituja ratkaisuja myös helpottamaan mikropalveluekosysteemin pyö- rittämistä tuotannossa. Vikatilanteihin varautuminen. Ohjelman koostuessa erillisistä mikropalveluista tulee varmistaa, että yhden palvelun rikkoutuminen vaikuttaa mahdollisimman vähän muiden palveluiden toimintaan. Tämä edellyttää ylimääräistä testausta ja tuotannossa toimivien palveluiden valvontaa. 3.1 Mikropalveluarkkitehtuurin vahvuudet Conwayn lain mukaan järjestelmän rakenne vastaa sen rakentajien organisaation raken- netta (Conway, 1968). Mikropalveluarkkitehtuurissa palvelujen pienikokoisuus mahdol- listaa palvelujen kehityksen rajoittamisen yhteen kehitystiimiin (Newman, 2015a). Pie- nen kehitystiimin sisäisen kommunikaation vaivattomuus mahdollistaa jäsenten jatku- van ja hienojakoisen vuorovaikutuksen. Tällä tavalla palveluiden sisäisestä ohjelmakoo- dista tulee kiinteää ja palveluista löyhästi toisiinsa kytkettyjä. Ohjelman jakaminen pienikokoisiin mikropalveluihin auttaa pitämään koodikannan yk- sinkertaisena ja ymmärrettävänä (Richardson, 2018; Rodger, 2018). Mikropalvelut kote- loivat (encapsulate) koodikantansa. Ainoa tapa kutsua mikropalvelua on verkon kautta. 17 Arkkitehtuuri estää modulaarisen rakenteen rikkomisen ja ehkäisee teknisen velan ker- tymistä. Mikropalvelut siis nopeuttavat kehitystyötä ja tätä kautta vähentävät kehitys- työn kustannuksia laajoissa ohjelmistoprojekteissa. Edellä mainitut tekijät tekevät myös mikropalvelupohjaisista järjestelmistä helposti muunneltavia. Kynnys pienikokoisten palveluiden uudelleenkirjoittamiseen tai poistami- seen on huomattavasti matalampi kuin monoliittisessa arkkitehtuurissa (Newman, 2015a). Arkkitehtuuri helpottaa ohjelmistojen muuttuviin vaatimuksiin vastaamista (Rodger, 2018). Mikropalvelut mahdollistavat usean eri teknologian käyttämisen samassa ohjelmistossa (Newman, 2015a). Teknologinen heterogeenisuus antaa kehittäjille vapauden käyttää parhaiten osaamiaan ja parhaiten kuhunkin palveluun sopivia teknologioita. Esimerkiksi yksi palvelu voi käyttää dokumenttitietokantaa ja funktionaalista ohjelmointikieltä, toi- nen relaatiotietokantaa ja olioperusteista ohjelmointikieltä. Käytännössä monet yrityk- set ovat rajoittaneet tätä vapautta. Mikropalvelut skaalautuvat tehokkaasti (Newman, 2015a). Hienojakoisista mikropalve- luista koostuva järjestelmä voidaan skaalata kopioimalla vain eniten resursseja syövät palvelut. Toisaalta Fowler (2015c) ei pidä tätä hienojakoista skaalaamista erityisen hyö- dyllisenä. Newman käyttää esimerkkinä skaalauksesta Netflixiä ja Giltiä, jotka ovat erit- täin suuria yrityksiä. Voidaan olettaa, että suurin osa skaalaamisen tehostamisen hyö- dyistä jää Netflixin kaltaisille erittäin suurten käyttäjämäärien ohjelmistojen kehittäjille. Kuten edellä mainitaan, mikropalveluarkkitehtuuri edellyttää uudenlaisiin vikatilantei- siin varautumista. Kuitenkin mikropalvelut estävät katastrofaaliset vikatilanteet, joissa koko ohjelman toiminta pysähtyy yhden ohjelmointivirheen takia (Newman, 2015a). Vaikka yksi palvelu menisikin rikki, muut palvelut pystyvät enimmäkseen jatkamaan toi- mintaansa. 18 Richardsonin (2018) mukaan mikropalveluarkkitehtuurin suurin hyöty on isojen ja moni- mutkaisten ohjelmien jatkuvan julkaisun mahdollistaminen. Mikropalvelut pystytään jul- kaisemaan itsenäisesti. Pienikokoiset julkaisut nopeuttavat julkaisun yhteydessä ajetta- via automaattisia testejä. Mikäli julkaisun jälkeen mikropalvelussa ilmenee ongelmia, pystytään palvelu palauttamaan nopeasti edelliseen versioon (Newman, 2015a). Nopeat ja vähemmän riskialttiit julkaisut nopeuttavat valmiitten toimintojen siirtämistä tuotan- toon. 3.2 Mikropalveluarkkitehtuurin heikkoudet Suuri osa mikropalveluarkkitehtuurin huonoista puolista liittyy hajautettujen järjestel- mien mukanaan tuomaan monimutkaisuuteen (Newman, 2015a; Fowler, 2015c; Richardson, 2018). Hajautetuissa järjestelmissä on enemmän osia, jotka voivat mennä rikki. Hajautetut järjestelmät joutuvat ottamaan huomioon verkon tuomat haasteet (Rotem- Gal-Oz, 2006). Verkko on epäluotettava: fyysisen laitteiston vikatilanteet voivat johtaa viestien katoamiseen tai viivästymiseen. Verkossa esiintyy viivettä ja verkon yli tapah- tuva kommunikaatio tuo mukanaan turvallisuusongelmia. Mikropalvelujen välisiin kutsuihin perustuva kommunikaatio on metodikutsuja hitaam- paa ja edellyttää erillisen kommunikaatiomekanismin käyttöä (Lewis ja Fowler, 2014; Richardson, 2018). Lewisin ja Fowlerin mukaan verkon viiveen ongelmat tulevat vahvasti esiin juuri mikropalveluarkkitehtuurissa palveluiden suuren määrän takia. Hienojakoiset mikropalvelut tekevät paljon kutsuja toistensa välillä. Kommunikaation hitauden ongel- mia pystytään vähentämään käyttämällä karkeajakoisimpia päätepisteitä, mikä vähentää verkon yli tehtävien kutsujen määrää. Tämä kuitenkin asettaa rajoitteita mikropalvelui- den kehittäjille. 19 Koska data on varastoitu useaan tietokantaan, tietokantojen integriteettiä ei voida var- mistaa transaktioilla (Fowler, 2015c). Hajautetusta tietomallista ja verkon epäluotetta- vuudesta johtuen kehittäjät joutuvat turvautumaan ennen pitkää saavutettavaan ehey- teen (eventual consistency). Ennen pitkää saavutettavassa eheydessä taataan datan kor- kea saatavuus luopumalla tietokannan ACID-periaatteen tarjoamasta kovasta eheydestä (strong consistency). Kovasti eheässä tietokannassa tietokantakyselyt palauttavat aina uusimman version datasta, kun taas ennen pitkää saavutettavassa eheydessä tietokan- taan tehdyt muutokset näkyvät ennemmin tai myöhemmin. Ennen pitkää saavutettava eheys voi johtaa viiveisiin järjestelmän toiminnassa, mikä joudutaan ottamaan huomi- oon järjestelmää kehitettäessä. Toisistaan riippuvaisten mikropalveluiden julkaisu vaatii koordinointia (Richardson, 2018). Useita mikropalveluja käyttävien ominaisuuksien julkaisu edellyttää näiden pal- veluiden julkaisemista samalla kertaa. Mikropalveluita päivitettäessä tulee ottaa huomi- oon palveluiden vanhoista versioista riippuvaiset palvelut (Newman, 2015a). Päivitys voi aiheuttaa ongelmia vanhasta versiosta riippuvaisissa palveluissa. Näiden ongelmien vuoksi järjestelmän toiminnan varmistaminen julkaisujen yhteydessä edellyttää mikro- palveluiden välistä integraatiotestausta ja versioinnin hallinnoimista. Operationaalinen kompleksisuus kasvaa suuren palvelumäärän julkaisemisen, ylläpidon ja hallinnoinnin myötä (Lewis ja Fowler, 2014). Mikropalveluarkkitehtuuri siirtää moni- mutkaisuuden itse mikropalveluista niiden välisiin yhteyksiin. Useita palveluita koskevien vikatilanteiden selvitys on hankalaa. Monimutkaisuutensa vuoksi mikropalveluiden hal- linto ja ylläpito edellyttää automaatiota. Järjestelmän pilkkominen mikropalveluiksi on haastavaa (Richardson, 2018). Väärin mää- ritellyt mikropalveluiden rajat johtavat tiukasti toisiinsa kytkettyihin mikropalveluihin. Tällainen järjestelmä kärsii sekä monoliittisen että mikropalvelupohjaisen järjestelmän ongelmista. 20 3.3 Milloin mikropalvelut Brooksin (1987) mukaan ohjelmistokehityksessä ei ole yhtä yksittäistä ratkaisua, joka kasvattaisi sovellusten tuottavuutta, luotettavuutta ja yksinkertaisuutta merkittävästi. Mikropalveluarkkitehtuuri ei ole poikkeus tästä säännöstä (Richardson, 2018). Mikropal- veluarkkitehtuuria harkittaessa tulee punnita edellä mainittuja arkkitehtuurin hyviä ja huonoja puolia. Hyödyt ja haitat painottuvat eri tavoin yrityksen kehitystiimistä ja kehi- tettävästä ohjelmasta riippuen. Mikropalveluarkkitehtuuri pyrkii ratkaisemaan monoliittisen arkkitehtuurin ongelmia. Nämä ongelmat eivät kuitenkaan esiinny kaikissa monoliittisissa järjestelmissä, joten on olennaista selvittää, millaiset järjestelmät hyötyvät mikropalvelupohjaisesta toteutuk- sesta. Kuva 3. Mikropalvelupohjaisen ja monoliittisen arkkitehtuurin vaikutus tuottavuuteen (Fowler, 2015a). 21 Fowlerin (2015a) mukaan mikropalveluarkkitehtuuria ei tule edes harkita, jos järjestelmä ei ole niin laaja, että sen hallitseminen monoliittisena ei tuota suuria vaikeuksia. Mikro- palveluarkkitehtuurin tuomat vaihtoehtoiskustannukset ylittävät sen hyödyt suurim- massa osassa järjestelmiä. Kuvassa 3 verrataan monoliittisen ja mikropalveluarkkiteh- tuurin vaikutuksia tuottavuuteen sovelluksen kompleksisuuden mukaan: yksinkertai- semmissa sovelluksissa monoliitti on nopeampi, mutta monimutkaisuuden kasvaessa mikropalveluarkkitehtuuri on nopeampi. Mikropalveluarkkitehtuuria harkittaessa tulisi toteutettavan järjestelmän toimiala tun- tea hyvin (Newman, 2015a). Riski palvelurajojen väärin määrittelystä ennestään tunte- mattomalla toimialalla on suuri. Koska mikropalveluarkkitehtuurin skaalauksen tehokkuus perustuu palvelujen hienoja- koisuuteen, saavutettava hyöty monoliittiin verrattuna riippuu ohjelman koosta sekä kuormituksen jakautumisesta. Mitä tasaisemmin järjestelmän kuormitus jakautuu eri osien kesken, sitä vähemmän mikropalvelut tehostavat skaalautumista. Pienikokoista monoliittia skaalattaessa ylimääräinen kopioitava osa jää pieneksi. Mikropalveluarkkitehtuurin mahdollistama nopea julkaisu hyödyttää SaaS-pohjaisia jär- jestelmiä (Singleton, 2016). Päivitykset saadaan viiveettä tuotantojärjestelmään. SaaS- pohjaisessa järjestelmässä kehittäjät pystyvät kontrolloimaan tuotantoympäristöä ja jul- kaisuprosessia (Newman, 2015b). Tämä mahdollistaa järjestelmän asennuksen automa- tisoinnin toimintavarmasti. Mikäli järjestelmä toimitetaan ja ajetaan asiakkaiden omissa tuotantoympäristöissä, menetetään jatkuvan julkaisun edut. Mikropalvelupohjaisen jär- jestelmän asentaminen ja ylläpito eri asiakkaiden vaihtelevissa tuotantoympäristöissä on vaikeaa. 22 Singletonin (2016) mukaan arkkitehtuurin käyttäminen ei ole pääsääntöisesti kannatta- vaa alle 60 kehittäjän organisaatioissa. Mikäli kehitystiimi on niin pieni, että sitä ei kan- nata jakaa edelleen pienempiin tiimeihin, menetetään arkkitehtuurin organisaatiotason hyödyt. Lisäksi suurissa, pienempiin tiimeihin jaetuissa kehitystiimeissä koodin yhdistä- minen julkaisua varten aiheuttaa usein lisätyötä. Yhdistämisen konflikteja ratkaistaessa on myös mahdollista, että jokin menee vikaan. Kehitystiimin kannalta mikropalveluarkkitehtuurissa on jyrkkä oppimiskäyrä (Buchgeher, 2017). Toisaalta verkkopalvelupohjaisuus ja suuri yrityskoko eivät ole elinehto mikropalveluark- kitehtuurin kannattavuudelle, mikäli muut tekijät puoltavat arkkitehtuuria. Buchgeher (2017) esittää tästä esimerkkinä AMS Engineering -yrityksen kehittämän laboratorioau- tomaatiojärjestelmän. Järjestelmä toimitettiin erikseen jokaiselle asiakkaalle, jotka vas- tasivat asennuksen jälkeisestä järjestelmän ylläpidosta. Järjestelmältä edellytetyt vaati- mukset vaihtelivat suuresti asiakkaiden välillä. Mikropalveluarkkitehtuuriin päädyttiin sen tarjoaman suuren joustavuuden ja muokattavuuden takia. AMS Engineeringin kehitystiimi koostui kahdeksasta sovelluskehittäjästä. Sovelluksesta luotiin pilottiversio, jossa otettiin käyttöön tarvittava infrastruktuuri ja luotiin muutama mikropalvelu. Pilottiversion toteutti kolmen kehittäjän tiimi. Kehittäjät olivat kokeneita, mutta heillä ei ollut aikaisempaa kokemusta mikropalveluarkkitehtuurista. Esimerkissä huomattavaa on arkkitehtuurin yritykselle aiheuttamat alkuvaiheen kustannukset. Yritys arvioi pelkästään vaaditun infrastruktuurin asentamisen vaatineen 2 500 työtuntia. Toi- saalta tämän jälkeen ensimmäisen liiketoiminta-arvoa tuottaneen mikropalvelun kehit- tämiseen meni vain 100 tuntia. 23 Edellä mainitusta esimerkistä havaitaan, että suuri osa mikropalveluihin liittyvistä kus- tannuksista sijoittuu arkkitehtuurin käyttöönoton alkuvaiheeseen. Kun palveluiden jul- kaisun, valvonnan ja hallinnon automaatio on asennettu, sitä ei tarvitse tehdä enää uu- destaan. 24 4 Käytännön toteutus Mikropalvelupohjaisen järjestelmän kehittämiseen on kaksi lähestymistapaa (Newman, 2015a). Järjestelmän voi toteuttaa joko alusta alkaen mikropalvelupohjaisena tai aloittaa monoliittisella arkkitehtuurilla, joka siirretään myöhemmin mikropalvelupohjaiseksi. Mikropalveluarkkitehtuurin toteutus voidaan jakaa itse mikropalveluiden mallintami- seen sekä arkkitehtuurin tukena käytettävän infrastruktuurin käyttöönottoon. Mikropal- veluiden mallintamiseen kuuluu palvelurajojen määrittäminen sekä mahdollinen palve- lujen sisäisen mallinnus sekä mahdollinen palveluiden sisäisen toteutuksen hallinto. Inf- rastruktuuriin kuuluu palveluiden viestinvälitys, testaus, julkaisu sekä valvonta. 4.1 Monoliittisella arkkitehtuurilla aloittaminen Fowlerin (2015b) mukaan monoliittisella arkkitehtuurilla aloittaminen on riskittömämpi toteutustapa. Monoliitin kehitys on alkuvaiheessa nopeaa ja monimutkaisuuden kasva- essa järjestelmä voidaan pilkkoa pala palalta mikropalveluihin. Useita mikropalveluita kattava refaktoroiminen on vaikeampaa kuin monoliitin sisällä re- faktoroiminen (Fowler, 2015b; Newman, 2015a). Sovelluksen kehityksen alussa vallitseva käsitys järjestelmän vaatimuksista ja niiden painopisteistä muuttuu usein kehittäjien tun- temuksen toimialasta kasvaessa. Aloittamalla monoliittisella arkkitehtuurilla järjestelmä on mikropalvelupohjaiseksi siirrettäessä kypsempi. Tämä auttaa määrittelemään mikro- palveluiden rajat tarkemmin. Koska monoliitilla aloitettaessa sama järjestelmä toteutetaan osittain kahteen kertaan, lähestymistapa vie pitkällä tähtäimellä enemmän aikaa kuin suoraan mikropalveluilla aloittaminen (Newman, 2015b). Siirtymä mikropalveluihin voidaan toteuttaa vaiheittain tai kerralla (Fowler, 2015b). Jos monoliitti aiotaan jakaa kerralla mikropalveluihin, on monoliittia kehitettäessä erityisen 25 tärkeää huomioida modulaarinen rakenne sekä ohjelmointirajapinnoissa että tietokan- noissa- Tämä tekee siirtymästä helpompaa ja vähemmän aikaa vievää. Vaiheittain siirty- minen jättää usein järjestelmän ytimen monoliittiseksi, vaikka uusi kehitys ja muutokset tapahtuvatkin mikropalveluissa. Monoliitti voidaan myös hylätä kokonaan ja mikropalvelupohjainen järjestelmä toteut- taa puhtaalta pöydältä (Fowler, 2015b). Monoliitti voidaan kehittää nopeasti, kun tule- vaa siirtymää ei tarvitse ottaa huomioon. Tämä lähestymistapa saattaa olla järkevä, mi- käli järjestelmä on saatava nopeasti tuotantoon. Lähestymistapa vastaa Tilkovin (2015) seuraavassa luvussa esittämää tapaa. Pienemmän riskin ja siirtymän vaiheittaisuuden vuoksi monoliitilla aloittamista voidaan suositella kehitystiimeille, joilla ei ole aikaisempaa kokemusta arkkitehtuurista. Newma- nin (2015b) mukaan vaiheittainen siirtyminen mikropalveluihin antaa kuvan myös kehit- täjien valmiudesta hallinnoida mikropalveluita. Jos jo muutaman mikropalvelun julkaisu ja ylläpito tuottaa vaikeuksia, täysin mikropalvelupohjainen ratkaisu tulee aiheuttamaan ongelmia. 4.2 Suoraan mikropalveluilla aloittaminen Tilkov (2015) ei suosittele monoliitilla aloittamista. Hänen mukaansa monoliitin kehittä- mien johtaa väistämättä paljon moduulien välisiä riippuvuussuhteita sisältävään koodi- kantaan, jonka pilkkominen mikropalveluiksi on hyvin vaikeaa. Hän myös pitää mikropal- veluarkkitehtuurin suurimpana hyötynä modulaarisen rakenteen rikkomisen vaikeuden. Tästä syystä hänen mukaansa on siirtyminen mikropalveluarkkitehtuuriin turhaa, mikäli kehittäjät pystyvät jo toteuttamaan kiinteän modulaarisen monoliitin. Tilkov on samaa mieltä Newmanin (2015a) kanssa sovelluksen toimialan tuntemuksen tärkeydestä ennen mikropalveluarkkitehtuurin käyttämistä. Hänen mukaansa ideaalitilanteessa mikropal- velupohjaista sovellusta edeltää saman sovelluksen monoliittinen versio. 26 Kuten alaluvussa 4.1 todetaan, suoraan mikropalveluarkkitehtuurilla aloitettaessa palve- lurajojen määritys on epävarmempaa. Jotta mikropalveluiden väliseltä refaktoroinnilta vältyttäisiin, kehityksen alkuvaiheessa on järkevää tehdä karkeajakoisempia mikropalve- luita, jotka voidaan myöhemmin palvelurajojen vakiintuessa jakaa pienempiin mikropal- veluihin (Newman, 2015a). Korkean oppimiskäyrän ja käyttöönottokustannusten vuoksi sovelluksen kehittäminen on aluksi hidasta, jos kehitystiimillä ei ole aiempaa kokemusta mikropalveluista. Sitoutu- minen kehityksen alusta asti entuudestaan tuntemattomaan arkkitehtuuriin on hyvin ris- kialtista. Mikäli kehittäjillä on aikaisempaa kokemusta mikropalveluarkkitehtuurista ja tarvittava infrastruktuuri on jo otettu käyttöön aikaisemmissa järjestelmissä, suoraan mikropalve- luilla aloittaminen voi olla järkevää. Aloittamalla suoraan mikropalveluarkkitehtuurilla vältetään monoliitin jakamisesta aiheutuva ylimääräinen työ. 4.3 Palvelujen mallinnus Palvelurajoja määrittäessä tavoitteena on pyrkiä tekemään palveluista mahdollisimman kiinteitä ja löyhästi kytkettyjä muihin palveluihin (Newman, 2015a). Mikropalvelujen mallintaminen suositellaan useimmiten tekemään toimialan toiminnallisuuksien mu- kaan (Newman, 2015a; Lewis ja Fowler, 2014). 27 Kuva 4. Saman sovelluksen kaksi mallia rajatuissa konteksteissaan (Fowler, 2014). Evansin (2003) toimialakohtaisessa suunnittelussa (domain driven design) sovelluksesta luodaan malleja, jotka vastaavat taustalla olevan digitoitavan toimialan prosesseja. So- vellus kehitetään niin, että sen luokat ja metodit vastaavat mallia. Laajat sovellukset koostuvat useista malleista. Rajatut kontekstit (bounded contexts) määrittävät rajat, joissa mallit pätevät ja ne osat malleista, jotka pätevät mallin ulkopuolella. Mallit pysty- vät vuorovaikuttamaan kontekstinsa ulkopuolella vain toisiin konteksteihin määriteltyjen rajapintojen kautta. Kuvassa 4 esitetään kaksi rajattua kontekstia ja niiden väliset raja- pinnat. Rajattujen kontekstien etsiminen auttaa mikropalvelujen rajojen määrittämisessä (New- man, 2015a; Lewis ja Fowler, 2014; Dehghani, 2018). Rajattuja konteksteja määritettä- essä on hyvä jakaa sovellus aluksi isoihin ja karkeajakoisiin konteksteihin (Newman, 2015a). Saaduista konteksteista voidaan edelleen etsiä pienempiä rajattuja konteksteja, kunnes saavutettu rakeisuus (granularity) vastaa toivottavaa mikropalvelujen kokoa. 28 Kuva 5. Karkeajakoinen ’Warehouse’ rajattu konteksti piilottaa sisäiset mikropalveluna ulkomaa- ilmalle (Newman, 2015a) Karkeajakoisemmat rajatut kontekstit, jotka on jaettu edelleen hienojakoisempiin mikro- palveluihin, voivat toimia julkisivuna palveluille, joista se koostuu (Newman, 2015a). Ku- vassa 5 on esitetty tällainen karkeajakoinen rajattu konteksti, joka sisältää useamman mikropalvelun. Karkeajakoiselle rajatulle kontekstille määritetään yksi rajapinta. Tällä ta- valla ulkopuolisten mikropalvelujen ei tarvitse välittää kontekstien sisäisistä palveluista. Newmanin mukaan tämä lähestymistapa on järkevä, mikäli kehitystiimi vastaa koko kar- keajakoisesta rajatusta kontekstista. Mikäli eri tiimit vastaavat kontekstin sisäisistä pal- veluista, kannattaa ne toteuttaa ylemmän tason palveluina. Tämä johtuu Conwayn lain mukaisesta kehittäjien organisoinnin vaikutuksesta ohjelman rakenteeseen. Rajattuja konteksteja määritettäessä on tärkeää seurata taustalla olevan mallinnettavan toimialan toiminnallisuuksia eikä kontekstien välillä jaettavaa dataa (Newman, 2015a). Datan pohjalta mallintaminen johtaa suureen määrään aneemisia CRUD-pohjaisia palve- luita (Newman, 2015a; Dehghani, 2018). Mallintamalla järjestelmä toimialan mukaan siitä tulee helpommin muunneltava, sillä muutokset rajoittuvat usein toimialojen sisälle. 29 Richardson (2018) ja Tyszberowicz, Heinrich, Liu ja Liu (2018) suosittelevat mikropalve- luiden määrittelyn aloittamista käyttötapausten analysoimisella. Käyttötapauksista saa- daan pääteltyä järjestelmän operaatiot ja tilan muuttujat, ts. toiminnan verbit ja sub- stantiivit, joilla pystytään rakentamaan mallit ja niiden rajatut kontekstit. Mikropalvelut voidaan myös mallintaa teknisen toiminnallisuuden mukaan (Newman, 2015a). Newman ei suosittele tätä lähestymistapaa. Mikropalvelujen koolle ei ole tiukkaa määritelmää (Newman, 2015a; Richardson 2018; Lewis ja Fowler, 2014). 4.4 Monoliitin pilkkominen Kuten alaluvussa 4.1 kerrotaan, voidaan monoliitti pilkkoa joko kerralla tai inkrementaa- lisesti. Fowler (2015b), Newman (2015a) ja Dehghani (2018) pitävät vaiheittaista lähes- tymistapaa parhaiten toimivana. Mikäli monoliittia ei ole jaettu osiin toimialan mukaan tai mikäli periaatteista on lipsuttu ja teknistä velkaa on kerääntynyt liikaa, ensimmäinen vaihe monoliitin pilkkomisessa on rajattujen kontekstien etsiminen (Dehghani, 2018). Kun sovelluksen rajatut kontekstit on löydetty, monoliitti kannattaa refaktoroida toimialakohtaisesti jaetuksi modulaariseksi sovellukseksi. Vasta tämän jälkeen voidaan alkaa etsiä mikropalveluiden rajoja monolii- tin karkeajakoisemmista rajatuista konteksteista. Jos monoliitti on jo jaettu toimialan mukaan moduuleihin eikä sovellukseen ole kertynyt paljoa teknistä velkaa, voidaan aloit- taa suoraan tästä vaiheesta. Joskus pelkkä monoliitin refaktorointi kiinteämmäksi ratkai- see sovelluksen ongelmat niin, että siirto mikropalvelupohjaiseksi jää tarpeettomaksi. Vaiheittainen monoliitin pilkkominen on järkevää aloittaa järjestelmän osista, jotka hyö- tyvät eniten koodikannan erottamisesta tai jotka on helppo erottaa (Newman, 2015a). Usein muuttuvat osat tai osat, joita tullaan muuttamaan lähitulevaisuudessa ovat hyviä 30 aloituskohtia. On myös hyvä aloittaa kokonaan yhden kehitystiimin vastuulla olleista osista. Erityistä tietoturvallisuutta edellyttävien osien irrottaminen mahdollistaa tiukem- man turvallisuuden toteutuksen vaikuttamatta muuhun järjestelmän toimintaan. Mikropalvelua irrotettaessa on tärkeää huomioida, kuinka tiukasti se on kytketty muu- hun koodikantaan (Newman, 2015a). Tiukasti kytkettyjen järjestelmän osien irrottami- nen on vaikeaa. Riippuvuussuhteiden kartoittamiseen voidaan käyttää erilaisia työkaluja. Useimmat IDE-työkalut mahdollistavat koodikannan riippuvuussuhteiden analysoinnin. Sosiaalinen koodianalyysi on vielä pidemmälle menevä analysointitekniikka, joka yhdis- tää koodin rakenteellisen analysoinnin kehittäjien toiminnan analysointiin versiohallin- tasovellusten kautta (Tornhill, 2019). Sosiaalisella koodianalyysilla pystytään etsimään koodikannan aktiivisimmin muuttuvat osat (Dehghani, 2018). Koodista irrotettava osa voidaan joko kirjoittaa uudestaan tai siirtää suoraan mikropal- veluun (Newman, 2018). Newmanin mukaan suora siirto on mahdollista, mikäli monolii- tissa on kiinteä toimialan mukaan jaettu modulaarinen rakenne. Dehghani (2018) suo- sittelee kirjoittamaan palvelujen koodit uudestaan. Vanhassa koodissa on hänen mu- kaansa usein paljon monoliitin toteutukseen liittyvää boilerplate-koodia. Uudelleen kir- joittaminen myös mahdollistaa paremman, enemmän mikropalvelun toimialaa vastaa- van toteutuksen. Siirrettäessä kannattaa aluksi vain kopioida toiminallisuus mikropalve- lussa ja säilyttää monoliitti ennallaan (Newman, 2018). Toiminnallisuus voidaan myö- hemmin poistaa monoliitista kokonaan, kun mikropalvelun toiminnasta on varmistuttu. Pelkästään koodikannan siirtämisen jälkeen mikropalvelu on vielä kytketty monoliitin tie- tokantaan. Mikropalveluiden integraatio tietokantatasolla ei ole toivottavaa, mistä lisää Integraatio-luvussa. Tietokannan pilkkominen on haastavaa (Todkar, 2018; Newman, 2015a). Todkar (2018) esittää vaiheittaisen strategian tietokannan pilkkomiselle. Palvelua mallin- nettaessa selvitetään tarvittavat monoliitista irrotettavat osat. Mikropalvelu erotetaan 31 ensin loogisesti monoliitin sisällä, jolloin virheet palvelurajan määrittämisessä on hel- pompi korjata. Loogiseen erotteluun kuuluu abstrahointikerroksen luonti tietokantaan, jonka kautta järjestelmän erotettavat osat kutsuvat tietokantoja. Loogisen erotuksen jälkeen tarvittavat uudet tietokantataulut toteutetaan monoliitin tie- tokantaan ja toteutetut abstrahointikerrokset kytketään uusiin tietokantatauluihin (Tod- kar, 2018). Kaikki viiteavainliitokset (foreign key) taulujen välillä täytyy poistaa, joten lii- toslauseet (join) täytyy siirtää tietokantatasolta logiikkatasolle abstrahointikerrokseen. Tähän vaiheeseen kuuluu myös datan siirto erotettavista tauluista uusiin tauluihin. Tie- tokannan erottaminen ensin monoliitin sisällä mahdollistaa analyysin muutoksen vaiku- tuksesta suorituskykyyn. Liitoslauseiden toteuttaminen logiikkatasolla on raskaampaa kuin tietokantatasolla. Seuraavaksi mikropalvelu erotetaan monoliitista niin, että se on vielä integroitu mono- liitin tietokantaan (Todkar, 2018). Lopuksi uuden palvelun taulut ja data siirretään mikro- palveluun ja ne poistetaan monoliitin tietokannasta. 4.5 Integraatio Mikropalvelupohjaisen järjestelmän toiminta perustuu palveluiden väliseen kommuni- kaatioon (Richardson, 2018; Newman, 2015a). Metodikutsujen sijaan mikropalvelut jou- tuvat kommunikoimaan mahdollisesti hitaan verkon välityksellä ulkoista kommunikaa- tiomekanismia käyttäen. Oikeanlaisen kommunikaatiomekanismin valinta on siis ensiar- voisen tärkeää. Nopeimmin käyttöön otettava integraatiomekanismi on jaettu tietokanta (Newman, 2015a). Integraatiota tietokantatasolla ei suositella, sillä se johtaa tiukasti toisiinsa kyt- kettyihin palveluihin, joita on vaikea muuttaa. Lisäksi se sitoo mikropalvelut yhteen tie- tokantateknologiaan. 32 Mikropalvelujen väliset viestit voidaan jakaa kahteen yläkategoriaan: synkronisisiin ja asynkronisiin (Rodger, 2018; Newman, 2015a.) Synkronisessa kommunikaatiossa mikro- palvelut lähettävät kutsuja verkon yli ja pysäyttävät suorituksen vastauksen odottamisen ajaksi. Asynkronisessa kommunikaatiossa kutsun lähettäjä ei jää odottamaan vastausta. Synkroninen kommunikaatio on yksinkertaisempaa, ja kutsujen tulokset nähdään heti (Rodger, 2018; Newman, 2015a). Synkroninen kommunikaatio sopii komentotyyppisiin (CRUD) toimintoihin ja monista järjestyksessä suoritettavista vaiheista koostuviin ope- raatioihin (Rodger, 2018). Asynkroninen kommunikaatio mahdollistaa käyttöliittymän responsiivisena pysymisen tilanteissa, joissa verkkoviiveestä tai muusta syystä vastaus kutsuun viivästyy. Asynkroninen ja synkroninen kommunikaatio liittyvät kahteen tapaan toteuttaa mikro- palvelujen välinen vuorovaikutus: pyyntö/vastaus ja tapahtumapohjainen kommuni- kaatio (Newman, 2015a). Pyyntö-/vastaus-pohjaisessa kommunikaatiossa asiakasoh- jelma lähettää pyynnön kohdeohjelmalle ja odottaa vastausta. Pyyntö-/vastaus-metodi on mahdollista toteuttaa sekä synkronisesti että asynkronisesti, esimerkiksi callback- funktion avulla. Tapahtumapohjaisessa kommunikaatiossa asiakasohjelmat julkaisevat tapahtumia ja kuuntelevat itselleen relevantteja muiden palveluiden julkaisemia tapah- tumia. Tapahtumapohjainen kommunikaatio on luonteeltaan asynkronista. Pyyntö/vastaus on yksinkertainen tapa toteuttaa prosessien välinen kommunikaatio (Williams, 2015). Yleisin tapa toteuttaa pyyntö-/vastaus-pohjainen kommunikaatio on käyttää ohjelmointirajapintojen toteutukseen REST-arkkitehtuuria ja viestinvälitykseen HTTP-protokollaa. Eksplisiittinen pyyntöihin perustuva kommunikaatio johtaa Newmanin (2015a) mukaan herkästi järjestelmän toimintalogiikan pakkautumiseen muutamaan tärkeimpään mikro- palveluun. 33 Tapahtumapohjainen kommunikaatio johtaa vähemmän tiukasti toisiinsa kytkettyihin mikropalveluihin (Newman, 2015a). Tapahtumia julkaisevat palvelut eivät ole kytkettyjä niitä kuunteleviin palveluihin, joten lähestymistapa johtaa löyhemmin kytkettyyn hajau- tetusti hallittuun järjestelmään. Uusien palvelujen liittäminen järjestelmään helpottuu, kun palvelut täytyy vain kytkeä tapahtumanjulkaisualustaan ja asettaa kuuntelemaan tarvitsemiaan tapahtumia. Tapahtumapohjainen kommunikaatio edellyttää jonkinlaisen viestinvälitysalustan käyttöönottoa tapahtumien julkaisua ja kuuntelemista varten. Ylei- sesti käytettyjä viestinvälitysalustoja ovat RabbitMQ ja ZeroMQ (Lewis ja Fowler, 2014). 4.6 Testaus Mikropalveluarkkitehtuurissa testaus on haastavaa arkkitehtuurin hajautetun luonteen vuoksi (Richardson, 2018). Koska mikropalvelupohjainen järjestelmä perustuu palvelui- den väliseen vuorovaikutukseen, kehittäjien tulee varmistaa palvelun toimivuus osana mikropalveluiden verkkoa. Useita mikropalveluita kattavat testit ovat kuitenkin aikaa vieviä ja monimutkaisia toteut- taa (Richardson, 2018). Yksinkertainen mikropalvelun rajapintaa testaava testi (myöh. API-testi) saattaa vaatia useamman mikropalvelun ajamista ja monimutkaisen toiminta- logiikan suorittamista. Asiakaspohjaiset sopimukset (consumer driven contract) ratkaise- vat hitaan testauksen ongelman API-testeissä (Newman, 2015a; Richardson, 2018). So- pimukset määrittävät palveluiden asiakkaiden eli palveluita käyttävien mikropalveluiden odotukset palveluilta. Asiakaspalvelut kirjoittavat nämä odotukset koodimuodossa tes- teinä, jotka ajetaan odotusten toteuttajapalvelussa automaattisesti julkaisun yhteydessä. Kuluttajien toteuttamilla sopimustesteillä päästään eroon suuresta määrästä palvelujen välisiä integraatiotestejä. Useita mikropalveluja kattavien testien hitaus johtuu suurimmaksi osaksi mikropalvelu- jen asentamisen viemästä ajasta. Richardson (2018) suosittelee tästä syystä tekemään 34 laajempia päästä päähän -testejä (end-to-end test), jotka testaavat samassa testissä useita toiminnallisuuksia. 4.7 Julkaiseminen Sovelluksen julkaisun automatisointi mahdollisimman pitkälle on mikropalveluarkkiteh- tuurissa tärkeää, sillä palvelumäärän kasvaessa toisistaan riippuvaisten mikropalveluiden julkaisusta tulee erittäin monimutkaista (Newman, 2015a). Jatkuvan integraation ja toi- mituksen periaatteet automatisoivat julkaisuprosessia ja takaavat julkaisun toimivuuden automatisoiduilla testeillä. Virtualisointi mahdollistaa fyysisen palvelimen laitteiston abstrahoinnin kautta mikropalvelukohtaisen ajonaikaisen ympäristön koteloinnin ja au- tomatisoidun asennuksen. Jatkuva integraatio (CI, continuous integration) on sovelluskehityskäytäntö, jossa kehit- täjät integroivat tekemänsä muutokset tasaisin, lyhyin aikavälein yhteiseen versionhal- lintajärjestelmän tietovarastoon (repository) (Meyer, 2014). Tavallisesti pyrkimys on yh- distää jokaisen kehittäjän muutokset vähintään kerran päivässä. Integraation yhteydessä järjestelmän toimivuus muutosten jälkeen varmistetaan automatisoiduin testein. Jat- kuva integraatio toteutetaan työkaluilla, jotka toimivat yhdessä versionhallintajärjestel- män ja integraatiopalvelimen kanssa. Integraatiotyökalu ajetaan aina, kun yhteiseen tie- tovarastoon siirretään muutoksia. Työkalu rakentaa integraatiopalvelimelle ajettavat ar- tefaktit sovelluksesta muutosten kanssa ja ajaa testit näitä artefakteja vastaan. Jatkuva toimitus (CD, continuous delivery) on jatkuvan integraation jatke. Jatkuvassa toi- mituksessa jatkuvan integraation prosessiin lisätään vaiheet, jotka tarvitaan sovelluksen julkaisuun tuotannossa. Jatkuvassa toimituksessa pyritään tilanteeseen, jossa sovelluk- sen uusimman version vienti tuotantoon on napinpainalluksen takana. Jatkuva toimitus eroaa jatkuvasta julkaisusta (continuous deployment) siinä, että jatkuvassa toimituksessa sovellusta ei julkaista automaattisesti tuotannossa. 35 Mikropalvelupohjaisessa järjestelmässä CI/CD-prosessin toteuttamiseen on useita ta- poja (Newman, 2015a). Yksinkertaisin tapa on käyttää yhtä tietovarastoa, johon koko jär- jestelmän koodikanta yhdistetään. Tietovarastosta rakennetaan yksi koontiversio (build), jolla testit ajetaan integraatiopalvelimella. Yhden koontiversion lähestymistapa johtaa hitaaseen integraatioprosessiin, sillä muutoksiin liittyvän mikropalvelun testien lisäksi ajetaan myös muiden palveluiden testit. Integroitujen muutosten jälkeen on epäselvää, mitkä mikropalvelut pitää julkaista uudestaan muutosten takia. Newmanin (2015a) mukaan parempi lähestymistapa on säilyttää joka mikropalvelun koodia omassa tietovarastossaan, josta on oma koontiversio. Tällä tavalla mikropalve- lusta vastuussa olevat kehittäjät kontrolloivat myös mikropalvelun CI/CD-prosessia. Tes- taus on nopeaa, sillä vain kyseisen palvelun omat testit ajetaan ja mikropalvelu on jul- kaistavissa itsenäisesti. Virtualisointi on teknologia, joka mahdollistaa useamman itsenäisen ympäristön ajami- sen samalla palvelimella. Laitteiston tarjoamat resurssit jaetaan useamman virtualisoi- dun ympäristön kesken (Merkel, 2014). Mikropalvelujen tuotantoympäristöjen virtualisointi mahdollistaa ympäristön vaatimus- ten koteloinnin (Richardson, 2018). Teknologisesti heterogeenisillä mikropalveluilla voi olla paljon vaihtelevia ajon aikaisen ympäristön vaatimuksia. Virtualisoinnin avulla kehit- täjät voivat määrittää automaattisesti julkaisuprosessissa asennettavan virtuaalisen ym- päristön. Virtuaalisilla ympäristöillä voidaan myös kontrolloida kunkin mikropalvelun käytettävissä olevaa resurssimäärää. Virtualisointi voidaan toteuttaa virtuaalikoneiden (esim. VMWare, VirtualBox) tai uu- dempien konttipohjaisten teknologioiden (esim. Docker) avulla (Merkel, 2014). Virtuaa- likoneet virtualisoivat laitteistotasolla ottamalla käyttöön osan isäntäkoneen laitteiston suorituskyvystä ja luomalla kokonaisen itsenäisen tietokoneen käyttöönotetun laitteis- 36 ton päälle. Virtuaalikoneet toteutetaan hypervisor-osalla, joka luo ja ajaa koneita. Virtu- aalikoneet voidaan jakaa kahteen tyyppiin. Ensimmäisen tyypin virtuaalikoneissa hyper- visor on yhdistetty suoraan laitteistoon. Toisessa tyypissä hypervisor ajetaan isäntäko- neen käyttöjärjestelmän kautta. Kuvassa 6 kuvataan tyypin 1 ja 2 virtualisoinnit sekä konttipohjainen virtualisointi. Kuva 6. Virtuaalikoneiden ja konttien erot (Merkel, 2014). Ohjelmistokontit tarjoavat virtuaalikoneita kevyemmän, standardoidun virtualisoin- tialustan (Merkel, 2014). Siinä missä virtuaalikoneet virtualisoivat laitteistotasolla, kontit virtualisoivat käyttöjärjestelmätasolla. Kontit ovat ikään kuin tiloja, jotka on eristetty toi- sista konteista ja tietyistä osista isäntäkäyttöjärjestelmää. Koska kontit käyttävät hyväksi isäntäkoneen käyttöjärjestelmää, niihin ei tarvitse sisällyttää omaa käyttöjärjestelmää. 37 Konttien resurssienkäyttö on myös paljon virtuaalikoneita tehokkaampaa: jos kontti ei suorita mitään, se ei käytä isäntäkäyttöjärjestelmän resursseja. Jaramillo, Nguyen ja Smart (2016) suosittelevat mikropalvelujen julkaisemista Docker- konteissa. Edellä mainittujen virtualisaation ja ohjelmistokonttien etujen lisäksi Docker- kontit toimivat useilla eri alustoilla kuten Linux-distribuutioilla ja pilvipalveluissa. Docker- kontit luodaan automatisoidusti kuhunkin konttiin määritellyn komentosarjan (Docker- file) mukaan. Sovelluksista rakennetaan docker-muistinkuvia (docker-image) (Docker Foundation, 2019). Docker-muistinkuva on itsenäisesti suoritettava pakkaus, joka sisältää koko sovel- luksen koodikannan, ajonajan ja kaiken muun suorittamiseen tarvittavan. Docker-muis- tinkuvat rakennetaan dockerfilen perusteella. Docker-kontit ovat käynnistettyjä docker- muistinkuvia. Muistinkuvien rakentamisesta ja konttien käynnistämisestä ja ajamisesta vastaa Docker engine -sovellus. 4.8 Valvonta Mikropalveluarkkitehtuuri kasvattaa järjestelmän tilanteen valvonnan monimutkai- suutta (Newman, 2015a). Toisin kuin monoliittisessa järjestelmässä mikropalveluarkki- tehtuurissa ongelman aiheuttajan etsimiseen ei ole välttämättä selvää alkukohtaa. Toi- sista mikropalveluista riippuvaisten mikropalvelujen ketjussa vikatilanteen ilmenemis- paikka saattaa olla kaukana aiheuttajasta. Sama mikropalvelu voi olla myös skaalattu kloonaamalla. 38 Kuva 7. Mikropalveluiden valvontajärjestelmä (Richardson, 2018). Mikropalvelupohjaisen järjestelmän valvonta voidaan toteuttaa kokoamalla yhteen mik- ropalvelutasolla tuotettua valvontadataa, kuten lokitiedostoja (Richardson, 2018). Ku- vassa 7 kuvataan lokitietoja aggregoiva järjestelmä: mikropalvelutasolla lokitietoja voi- daan tuottaa ohjelmakirjastojen avulla. Mikropalvelut siirtävät lokitietonsa keskitetylle lokitietojen keräyspalvelimelle, jonka kautta kehittäjät voivat valvoa järjestelmän toimin- taa. Valvontajärjestelmään tarvitaan lokitietojen keräyspalvelin, mikropalvelutasolla lo- kien tuottaja sekä prosessi, joka siirtää lokitiedot mikropalveluista lokitietojen keräyspal- velimelle. 39 5 Sovelluksen tämänhetkinen tilanne Yrityksessä kiinnostuttiin mikropalveluarkkitehtuurista lähinnä arkkitehtuurin herättä- män innostuksen ja sen lupaamien mahdollisuuksien takia. Arkkitehtuurin kokeilualus- taksi otettiin tuolloin suunnitteluvaiheessa ollut toiminnanohjausjärjestelmä, jota käyte- tään tämän diplomityön case-projektina. Sovellusta lähdettiin kehittämään ensin monoliittisena huomioiden tuleva siirtymä mik- ropalveluarkkitehtuuriin. Koodikannasta pyrittiin saaman mahdollisimman helposti mik- ropalveluiksi pilkottava pyrkimällä kiinteään modulaariseen rakenteeseen. Sovellusta varten otettiin käyttöön myös automatisoitu jatkuva julkaisu ja valmius jatkuvaan toimi- tukseen. Ajatuksesta siirtää sovellus mikropalvelupohjaiseksi luovuttiin muutama kuukausi kehi- tystyön aloittamisen jälkeen. Sovellusta kehitettäessä käytetyt siirtoa helpottavat peri- aatteet hidastivat kehitystyötä. Myös mikropalveluarkkitehtuurin hyödyt paljastuivat ky- seenalaiseksi. Toiminnanohjausjärjestelmä on ollut kehityksessä vuoden 2018 lopusta lähtien. Järjes- telmää kehitettiin yhteistyössä ensimmäisen asiakkaan kanssa, jonka käyttöön järjes- telmä siirtyi kesällä 2019. 5.1 Sovelluksen vaatimukset ja ominaisuudet Sovellus on pilvipalveluna toimiva toiminnanohjausjärjestelmä ensisijaisesti palvelualan yrityksille. Sovellus tarjotaan palveluna, eli kaikki sovellusta käyttävät yrityksen käyttävät samaa sovellusta, mutta jokainen omaa dataansa. Alustavat ominaisuudet määriteltiin yhdessä ensimmäisten käyttäjien kanssa. Keskeisimmät vaatimukset liittyvät työajan seurantaan ja työn organisointiin. 40 Työntekijä voi merkitä sovellukseen työtunnit, poissaolot ja työmatkat. Työtunteja voi- daan merkitä kellokorttimaisesti leimaamalla sisään/ulos tai vaihtoehtoisesti merkitse- mällä suoraan tehtyjä työjaksoja. Tehtyjä työtunteja voidaan kohdistaa eri tavoilla eri yri- tysten tarpeiden mukaan. Mahdollisia kohdistuskohteita ovat alustavasti asiakkaat, toi- mipisteet, joissa työ on tehty, projektit, tehtävälajit ja tehtävälajien alitehtävälajit. Työn- tekijät voivat kirjata aikaa joko mobiilisovelluksen tai verkkosivun kautta. Tehdyistä työtunneista, poissaoloista ja työmatkoista voidaan luoda erilaisia raportteja muun muassa laskutusta ja palkanmaksua varten. Tiedot tehdyistä työtunneista, poissa- oloista ynnä muista vastaavista voidaan myös siirtää suoraan järjestelmää käyttävälle ti- litoimistolle. Järjestelmää voidaan käyttää työvuorojen suunnitteluun. Alustavasti työvuoroille voi- daan määritellä toimipiste, jossa vuoro suoritetaan, ja suoritettava tehtävä (tehtävälaji) sekä työntekijät, joille vuoro osoitetaan. Työntekijöille voidaan myös määrittää pätevyyk- siä eri toimipisteisiin ja tehtävälajeihin, jolloin työvuorojen suunnittelija pystyy näke- mään, kuka on pätevä mihinkin työtehtävään. Työvuoroille rajataan aikaikkuna, jonka aikana vuoro on suoritettava, ja maksimiaika työ- vuorolle. Työntekijälle on myös mahdollista lähettää automaattisesti sähköpostiin tai mobiilisovellukseen erilaisia työvuoroihin liittyviä muistutuksia. Muistutuksia voidaan lä- hettää, jos työntekijä ei ole aloittanut määrättyä työvuoroa ajoissa tai mikäli maksimiaika on lähestymässä. Muistutukset voidaan myös kytkeä pois päältä. Järjestelmä on konfiguroitavissa eri organisaatioiden tarpeiden mukaan. Edellä mainitut aikaleimaus- ja työajan kohdistamistavat ovat organisaatiotason asetuksia. Järjestel- mästä on myös tarkoitus tehdä tilauspohjainen, jolloin yritykset valitsevat itselleen tar- peellisia järjestelmän eri osia ja maksavat vain käyttämistään osista. 41 Järjestelmän tukee eritasoisia käyttäjiä. Yritykset voivat määritellä omat käyttäjätasonsa työntekijöilleen ja käyttäjätasoille pääsyt sovelluksen eri ominaisuuksiin. Sovelluksen back end toteutettiin PHP-kielellä käyttäen Laravel -sovelluskehystä. Tieto- kantana toimii MySQL. 5.2 Sovelluksen rakenne Sovellus jaettiin karkeajakoisiin moduuleihin toimialan mukaan. Moduuleita oli tarkoitus käyttää lähtökohtana mikropalveluiden rajojen määrittämiseen. Moduulit toteutettiin PHP:n nimiavaruuksina. 5.2.1 Organization Kuva 8. Organization-moduulin rakenne. 42 Organisaatiomoduuli sisältää yritysten organisaation rakenteen ja muita työn organisoin- tiin liittyviä asioita. Organisaation rakenteeseen kuuluvat itse yritys ja mahdolliset alior- ganisaatiot. Työn organisointiin liittyvät asiat ovat organisaatiotason kohteita, johon teh- tyjä työtunteja ja suunniteltuja työvuoroja voidaan liittää. Näitä kohteita ovat toimipis- teet (PointOfBusiness), projektit (Project), sekä tehtävälajit ja alitehtävälajit (TaskType, SubtaskType). Moduuli sisältää myös organisaatio- ja aliorganisaatiokohtaisia asetuksia portaalin toiminnasta (OrganizationSetting). 5.2.2 OrganizationProxy Kuva 9. OrganizationProxy-moduulin rakenne. OrganisationProxy-moduuli on tarkoitettu organisaatioiden asiakkaiden ja toimittajien hallinnointiin. Asiakkaat ja toimittajat (OrganizationProxy) voivat olla järjestelmän muita käyttäjäorganisaatioita (-proxiedOrganization) tai ulkoisia organisaatioita. Työntekijät voivat osoittaa tekemänsä työtunnit asiakkaalle 43 Järjestelmässä olevien organisaatioiden välisillä toimittaja-/asiakassuhteilla on tarkoitus tulevaisuudessa integroida esimerkiksi palkanmaksu tilitoimiston ja asiakasyrityksen vä- lillä tai laskutus toimittajien/asiakkaiden välillä. 5.2.3 Employment Kuva 10. Employment-moduulin rakenne. Employment-moduuli on tarkoitettu työntekijöiden hallinnointiin. EmployeeProfile ku- vaa yhtä työntekijää ja sisältää tietoja tästä. EmployeeProfile liitetään Identity-moduulin User-käyttäjämalliin. Employment-malli kuvaa työntekijän työsuhdetta yritykseen (Or- ganization). Työntekijöille on mahdollista määrittää pätevyyksiä (Competence). Päte- vyys voidaan linkittää erilaisiin pätevyyksien lähteisiin (source). Tällä hetkellä lähteenä käytetään ainoastaan toimipistettä (PointOfBusiness). Toimipisteen pätevyyttä käyte- tään tällä hetkellä työvuorosuunnittelussa määrittämään, keillä työntekijöillä on päte- vyys tehdä työvuoro missäkin toimipisteessä. 44 5.2.4 TimeLog Kuva 11. TimeLog-moduulin yksinkertaistettu malli TimeLog-moduuli sisältää työajanseurannan ja työvuorojen suunnittelun sekä näihin liit- tyviä oheistoimintoja. Tehtyä työaikaa voidaan mitata joko aikaleimauksilla (TimeLo- gEntry) tai suoraan aikajaksoilla (TimeSpan) organisaation asetuksista riippuen. Aikalei- maukset ovat yksinkertaistettuna kellokorttimaisia sisään/ulos leimauksia. Työntekijät syöttävät aikaleimaukset tai -jaksot verkkosivun tai mobiilisovelluksen kautta. Sovellus 45 muodostaa lähetetyistä aikaleimauksista reaaliajassa aikajaksoja vastaavia alisekvens- sejä (TLESubSequence). Työaikasekvenssi (TimeLogSequence) on tehdystä työajasta luotu aggregaatti. Alisek- vensseistä ja aikajaksoista ryhmitetään työaikasekvenssejä määriteltyjen sääntöjen mu- kaan. Määritellyt säännöt riippuvat organisaation asetuksista. Jos yritys käyttää esimer- kiksi työvuorojen suunnittelua, ryhmitys voidaan toteuttaa yhdistämällä aikaleimaukset suunniteltuihin työvuoroihin. Työaikasekvenssejä voidaan eksportoida Excel-muodossa (TimeLogExport). Raportteja voidaan tehdä muun muassa laskutukseen ja palkanmaksuun. Eri raportit sisältävät eri- laista business-logiikkaa. 5.2.5 TimeRule Kuva 12. TimeRule-moduulin yksinkertaistettu malli. Työvuoronsuunnitteluun käytetty moduuli. Moduulissa voidaan määritellä aikasääntöjä (TimeRule), joille voidaan määritellä maksimiaika ja intervalli. Aikasäännöt voidaan 46 osoittaa eri kohteisiin (TimeRuleAssignment). Osoitus ja aikasääntö muodostavat yh- dessä työvuoron. 5.2.6 DrivingLog Kuva 13. DrivingLog-moduulin domain-malli. DrivinLog-moduuli sisältää työmatkojen lokituksen. Moduulia käytetään työmatkakor- vausten laskemiseen ja eksportointiin (DrivingLogExport). Työmatkakorvaukset laske- taan työmatkojen (DrivingLogEntry) ajoneuvotyypin (VehicleType) kilometrikorvauksen (TravelCompensation) ja matkustajakohtaisen kilometrikorvauksen (PassengerCompen- sation) perusteella. 47 5.2.7 Identity Identity on käyttäjien tilien (User-luokka) hallitsemiseen käyetty moduuli. Se sisältää käyttäjätilien rekisteröinnin, deaktivoinnin ja autentikaation. Verkkosivun autentikaatio suoritetaan tavanomaisesti istunnoilla ja evästeillä. API-autentikaatioon käytetään oauth2-palvelinta. Tilien hallinnan toiminnot ovat enemmänkin infrastruktuurin kuin bu- siness-logiikan toimintoja. Koska tilien hallintaan liittyy kuitenkin enemmän vaatimuksia tietoturvallisuuden kannalta, Identity-moduuli erotettiin omaksi moduulikseen. 5.2.8 Permission Kuva 14. Permission-moduulin rakenne. Permission -moduulissa hallitaan käyttäjien auktorisointia. Auktorisointi toteutetaan roolien (Role) ja kykyjen (Ability) kautta. Kyvyt ovat auktorisoinnin perusta. Niillä ohja- taan käyttäjien pääsyä järjestelmän eri osiin ja eri osien näkyvyyttä käyttöliittymissä. Roolit eivät sisällä omaa logiikkaansa, vaan ne ovat organisaatioiden määrittämiä karkea- jakoisempia kykyjen yhdistelmiä, joita osoitetaan eri käyttäjille. 48 Tulevaisuudessa kyvyillä on tarkoitus ohjata myös organisaatioiden pääsyä järjestelmän eri osiin. Kyvyt voidaan osoittaa scope -attribuutin kautta osia käyttäville organisaatioille. Tämä mahdollistaa erilaisten tilaustyyppien käyttämisen. 5.3 CQRS CQSR, Command Query Responsilibity Segregation, tarkoittaa sovelluksen tietokantara- japinnan luku- ja kirjoitusmallien erottamista toisistaan (Richardon, 2018). Tietokannan lukuoperaatiot toteutetaan lukumallin kyselyjen (query) kautta, kun taas muutosoperaa- tiot tehdään kirjoitusmallin komentojen (command) kautta. Kyselyt palauttavat dataa, komennot eivät. Luku- ja kirjoitusmallien erottaminen johtaa selkeämpään rakenteeseen monimutkai- sissa sovelluksissa, missä luku- ja kirjoitusoperaatiot eroavat toisistaan (Richardson 2018). Koska data ei kulje samaa reittiä, CQRS mahdollistaa asynkroniset kirjoitusope- raatiot ja synkroniset lukuoperaatiot. CQRS-periaatteen toteutus monoliittisessa sovel- luksessa kasvattaa olennaisesti sovelluksen kompleksisuutta. Mikropalveluarkkitehtuurissa luku- ja kirjoitusmallit ovat mikropalveluiden rajapinnoissa implisiittisesti eroteltuja (Richardson, 2018). Kukin rajapinnan päätepiste vastaa yhtä ky- selyä tai komentoa, joten data kulkee luonnostaan eri reittiä. Asynkroniset komennot ja synkroniset kyselyt liittyvät palvelujen väliseen viestinreitityksen konfiguraatioon. Sovelluksessa käytettiin CQRS-periaatetta. Käyttämällä CQRS-periaatetta sovelluksen monoliittisessa versiossa pyrittiin helpottamaan siirtymää mikropalveluihin. Monoliitissa mikropalveluja vastaavissa moduuleissa toteutetuista komennoista ja kyselyistä käyvät ilmi erotettavan mikropalvelun rajapinnan tarvittavat päätepisteet. 49 Keskittämällä myös sovelluksen sisäiset kirjoitus- ja lukuoperaatiot moduulien komen- toihin ja kyselyihin saadaan moduulien välinen kommunikaatio vastaaman mikropalve- lupohjaisen järjestelmän kommunikaatiota. Kuvassa 15 mallinnettu kirjoitusmalli toteutettiin CommandFactory-, Command- ja CommandHandler-luokilla. CommandFactory-luokka ottaa vastaan parametreja mallin käyttäjältä, validoi ne, ja luo niistä Command-olioita. Command-oliot sisältävät kirjoitet- tavat datat. CommandHandler-luokka vastaanottaa Command-olioita ja tallentaa olioi- den datat tietokantaan. Kuvassa 16 mallinnettu lukumalli koostuu QueryFactory-, Query- ja QueryHandler-luo- kista. QueryFactory-luokka vastaa kirjoitusmallin Factory-luokkaa. Se validoi käyttäjän parametrit ja rakentaa niistä Query-olioita. QueryHandler-luokka taas vastaanottaa Query-olion, muodostaa siitä tietokantakyselyn ja palauttaa tuloksen. Koska lukuoperaa- tiot eivät muuta järjestelmän tilaa, on validointi vähemmän tiukkaa. Lukuoperaatioiden validoinnilla voidaan antaa palautetta käyttäjälle, mikäli tämä esimerkiksi käyttää kyse- lyssä väärän tyyppistä parametria. Kuva 15. Sovelluksen kirjoitusmalli. 50 Kuva 16. Sovelluksen lukumalli. Jokaiselle eri luku- ja kirjoitusoperaatiolle määritettiin oma nimiavaruus, joka sisältää omat Factory-, Command/Query- ja Handler-luokkansa. Esimerkiksi Employment-mo- duuli sisältää StoreEmployeeProfile-kirjoitusoperaation ja GetEmployeeProfiles-lu- kuoperaation. 5.4 Docker-kontit Mikropalveluja varten päätettiin siirtyä käyttämään konttiteknologiaa. Teknologiaksi va- littiin Docker sen vakiintuneen aseman vuoksi. Kontteja siirryttiin käyttämään sekä kehi- tys- että tuotantoympäristöissä. Docker valittiin konttiteknologiaksi, sillä se on vakiintunut alan standardiksi. Julkisissa Docker-hakemistoissa on saatavilla runsaasti valmiita docker-muistinkuvia, mikä mahdol- listaa konttiteknologian nopean käyttöönoton. 51 Yhdenmukaistamalla kehitys- ja tuotantoympäristöt suoraviivaistetaan testaus- ja julkai- suprosessia ja vältytään ympäristöjen eroista johtuvista ohjelmointivirheistä. Docker ha- vaittiin myös kevyemmäksi kuin aiemmin kehitysympäristössä käytetty VirtualBox-virtu- aalikone. 5.5 Jatkuva integraation ja toimitus Mikropalvelupohjaisen järjestelmän kehittämiseen valmistauduttiin ottamalla käyttöön jatkuvan integraation ja toimituksen ketju. Ketju on toteutettu Docker-konttien, Google Cloud Container Registry -pilvipalvelun, BitBucket -versionhallinnan tietovarastojen säi- lytyspalvelun ja CircleCI-työkalun avulla. Google Cloud Container Registry on Google Cloud Platformiin kuuluva palvelu, johon voi- daan varastoida docker-muistinkuvia (Google, 2020). Palveluun varastoidaan testeissä käytettävän sovelluksen docker-muistinkuva ja CÌ/CD-ketjun tuottamien julkaisuversioi- den docker-muistinkuvat. Tämä antaa valmiuden jatkuvaan toimitukseen, toisin sanoen uusi tuotantoversio voidaan julkaista napin painalluksella. CircleCI on työkalu CI/CD-ketjun toteuttamiseen (CircleCI Documentation, 2020). Cir- cleCI:hin määritellään työnkulkuja (workflow), jotka voidaan asettaa versionhallinnan haaroihin (branch) Työnkulut koostuvat ajettavista tehtävistä (job) jotka koostuvat eri as- keleista (step). Tehtäville määritellään tehtävän toteuttamisympäristö ja suoritettavat komennot. Ympä- ristö koostuu suorittajasta (executor), hakemistosta ja konfiguraatiosta. Suorittaja voi olla virtuaalikone tai docker-muistinkuva. CircleCI tarjoaa Linux-, macOS- ja Windows-virtu- aalikoneet sekä valmiita docker-muistinkuvia. Hakemistoksi määritellään jokin hake- misto työnkulkuun liitetyn versionhallinnan haaran sisältä. Hakemisto sisällytetään suo- rittajan virtuaaliympäristöön. Tehtävässä voidaan siis ajaa komentoja määritellyssä vir- tuaaliympäristössä, joka sisältää sovelluksen koodikannan. 52 Kuva 17. CI/CD-ketjun tuotantotyökulku. CircleCI:hin määritettiin kehitys- ja tuotantotyönkulut. Kehitystyönkulku on käytössä kai- killa muilla paitsi julkaisu-versiohaaroilla (release branch). Tuotantotyönkulku ajaa ensin testit nopeimmista hitaimpiin. rakentaa sitten tuotannossa käytettävän docker-muistin- kuvan sovelluksesta ja lataa sen Googlen pilvipalvelun konttirekisteriin. Tuotantotyönku- lun vaiheet on kuvattu kuvassa 17. Kehitystyönkulussa docker-muistinkuva rakennetaan kehitysympäristön riippuvuuksilla eli tuotannossa käytettyjen pakkausten lisäksi sellaisilla pakkauksilla, jotka on määritetty käytettäväksi pelkästään sovelluksen kehitysympäristössä. Kehitystyönkulkua ei myös- kään ajeta automaattisesti joka kerta, kun sitä käyttävään versiohaaraan lisätään muu- toksia. Sen sijaan työnkulun vaiheet ajetaan manuaalisesti CircleCI:n verkkosovelluksen kautta. Tämä mahdollistaa pelkkien testien ajamisen. Tästä on kehitystyössä hyötyä, sillä Googlen pilvipalvelu ajaa testit lokaalista kehitysympäristöä nopeammin. 5.6 Nykytilanne Sovellusta kehitettäessä ilmeni, että järjestelmän siirtäminen mikropalvelukohtaiseksi ai- heuttaisi ongelmia pysyä aikataulussa. Kehittäjien aiemman kokemuksen puute mikro- palveluarkkitehtuurista kasvatti osaltaan epävarmuutta ja riskiä. Myös arkkitehtuurin hyödyt kyseenalaistettiin. Kokonsa puolesta sovelluksen skaalautumisessa ei tulisi ongel- mia pitkään aikaan, ja pienen kokonsa takia kehitystiimi ei myöskään hyötyisi arkkiteh- tuurin organisaatiotason hyödyistä. Näitten syitten takia mikropalveluarkkitehtuurista luovuttiin ja kehitystä jatkettiin monoliittisella arkkitehtuurilla. 53 Joistakin mikropalveluarkkitehtuuria varten käytetyistä suunnitteluperiaatteista luovut- tiin. Toimialan mukaan moduuleihin jaetusta rakenteesta on kuitenkin pidetty kiinni. Mo- duulit on pyritty pitämään kiinteinä, mutta riippuvuuksia ei enää rajoiteta Query- ja Com- mand -luokkien tarjoamiin rajapintoihin. Query-lukumallilla piilotettiin alla oleva tietokantatason integraatio moduulien välillä. Koska monoliitin tietokantaa ei tulla pilkkomaan, ylimääräisestä tietokannan abstrahoin- nista moduulitasolla ei ole monoliittisessa sovelluksessa hyötyä. Ennalta määriteltyjen Query-luokkien käyttö oli monissa tilanteissa kankeaa, ja se johti ylimääräiseen koodiin ja tietokannan lukuoperaatioihin. Näitten syitten takia moduulien välisten lukuoperaati- oiden rajaamisesta moduulien Query-luokkiin luovuttiin. Kirjoitusoperaatiot toteutetaan edelleen Command-luokkien kautta. 54 6 Monoliitin pilkkominen ja demoversio Mikropalveluarkkitehtuurista luopumisen jälkeen päätettiin kuitenkin tutkia mahdolli- suutta toteuttaa siirto arkkitehtuuriin tulevaisuudessa. Siirtymisen edellytyksistä, kan- nattavuudesta ja toteutuksesta toteutettiin selvitys. Sovelluksesta myös toteutettiin pie- nimuotoinen mikropalvelupohjainen demoversio, jossa demonstroitiin käytännön rat- kaisuja. Selvityksessä kartoitettiin sovelluksen koodikannan riippuvuussuhteet. Selvittämällä kuinka tiukasti moduulit ovat kytkettyjä toisiinsa saadaan arvioitua työmäärää, jonka kunkin moduulin erottaminen mikropalvelluksi edellyttäisi. Kannattavuutta tutkittiin ar- vioimalla sovelluksen tämänhetkistä tilannetta sekä koodikannan ja skaalaamisen vaati- muksia tulevaisuudessa. Siirtymän toteutuksesta laadittiin suunnitelma, jossa selvitettiin miltä osin ja mitä priorisoiden siirtymä kannattaisi toteuttaa. Demoversiossa pieni osa monoliittisesta sovelluksesta erotetaan omaksi mikropalveluk- seen. Toteuttamalla käytännössä toimiva demoversio pyritään selvittämään mikropalve- lupohjaisen järjestelmän minimivaatimuksia. 6.1 Rakenteen analyysi Moduulien väliset riippuvuudet kartoitettiin etsimällä moduulien koodikannasta moduu- lin ulkopuolisia luokkia käyttävää koodia. Etsittävät luokat rajoitettiin toisten moduulien luokkiin, eli siinä ei huomioitu yleiskäyttöön tarkoitettuja Common -nimiavaruuteen teh- tyjä luokkia eikä sovelluskehyksen apuluokkia. Myös alun perin mikropalvelujen rajapin- toja vastaamaan luodut Command- ja Query- luokat jätettiin pois tuloksista. Taulukossa 1 esitetään kartoitetut moduulien väliset riippuvuudet. 55 Taulukko 1. Moduulien välisten riippuvuuksien määrä. Riippuvuuksien suunta on pystyrivin mo- duuleista vaakarivin moduuleihin. Huomattiin, että TimeLog- ja Organization-moduulit ovat selvästi tiukimmin muihin mo- duuleihin kytkettyjä. TimeLog-moduulissa on 98 ja Organization-moduulissa 27 riippu- vuutta. Tulos ei ole yllättävä, sillä suurin osa kehitystyöstä mikropalveluarkkitehtuurista luopumisen jälkeen on tapahtunut näissä moduuleissa. Suurin osa riippuvuuksista koostui Laravelin Eloquent-nimisen ORM-kirjaston (Object Re- lational Mapping) kautta suoritetuista toisen moduulin tietokantatauluihin koostuvista tietokantakyselyistä. Eloquent kuvaa tietokannan taulut erityisiin Model-luokkiin eli mal- leihin. Mallien kautta pystytään vaivattomasti hakemaan alla olevan tietokantataulun re- laatioiden mallit. Tätä ominaisuutta on käytetty laajasti sen helppokäyttöisyyden vuoksi. Esimerkiksi seuraava metodi TimeLogEntry-mallissa määrittelee relaation Organization- mallin kautta time_log_entries -taulun ja organizations-taulun välille, jossa vierasavain sijaitsee time_log_entries -taulussa: public function organization() { return $this->belongsTo(\Organization\Organization::class); } Riippuvuussuhde -> Organization TimeLog OrganizationRelationship Employment Organization 9 5 7 TimeLog 50 8 16 OrganizationRelationship 4 0 0 Employment 2 2 0 Permission 0 0 0 0 DrivingLog 5 0 0 2 Identity 1 0 0 1 TimeRule 2 5 0 0 Riippuvuussuhde -> Permission DrivingLog Identity TimeRule Organization 2 0 1 3 TimeLog 1 0 2 21 OrganizationRelationship 0 0 0 0 Employment 0 1 1 0 Permission 0 0 0 DrivingLog 0 0 0 Identity 0 0 0 TimeRule 0 0 0 56 Suoranaisten logiikkaa sisältävien riippuvuuksien määrä rajoittui muutamaan TimeLog- moduulin käyttämään luokkaan. Moduulit ovat siis tiukasti kytkettyjä lähinnä tietokan- tatasolla. Koska kaikki järjestelmän tietokantakyselyt on tehty Eloquent-mallien kautta, luokkakohtaisten riippuvuuksien kartoitus paljastaa myös tietokantakyselyjen riippuvuu- det. Luokkariippuvuuksien määrä on vain suuntaa antava tieto moduulien mikropalveluiksi erottamiseen vaaditusta työmäärästä. Järjestelmän toiminta perustuu vahvasti Elo- quent-mallien kautta tehtyihin tietokantakyselyihin. Pelkkä luokkariippuvuuksien kartoi- tus paljastaa vain Eloquent-malleihin määritellyt relaatiot, ei siis paikkoja, joissa kyseistä relaatiota on käytetty. Työmäärä on näin ollen tosiasiassa huomattavasti suurempi, kuin luokkariippuvuuksien määrä antaisi olettaa. Eloquent-malleihin määriteltyjä relaatioita toisten moduulien tau- luihin ei ole myöskään järkevää korvata kutsuilla toisiin mikropalveluihin. Käytännössä tämä vaatisi lähes koko Eloquent-järjestelmän uudelleen kirjoittamista. Toisiin mikropal- veluihin tehtyjen verkkokutsujen piilottaminen samaan tietokantakyselyjä abstrahoivaan järjestelmään johtaisi myös helposti suureen määrään hitaita palvelujen välisiä kutsuja. 6.2 Lähestymiskohdat Toiminnanohjausjärjestelmien potentiaalinen toimiala on hyvin laaja. Koska case-sovel- luksesta on tarkoitus tehdä useamman yrityksen tarpeisiin vastaava tuote, on vääjäämä- töntä, että sovelluksesta tulee laaja ja monimutkainen kokonaisuus. On siis ajan kysymys, milloin sovelluksen koko alkaa haitata kehitystyötä. Buchgeherin (2017) esimerkkiprojektissa 2 500 tunnin alkuvaiheen kustannukset tarkoit- taisivat, että siirtymä mikropalveluarkkitehtuuriin vaatisi yli puoli vuotta yrityksen back end -tiimiltä. Kustannusvaatimus ei ole suoraan verrannollinen, sillä sovellukset eroavat 57 toisistaan. Esimerkkiprojektiin verrattuna siirtymää ei tarvitsisi aloittaa aivan alusta, sillä joitakin mikropalveluarkkitehtuurin edellytyksiä on yrityksessä jo ehditty ottaa käyttöön. On kuitenkin ilmeistä, että siirtymä veisi paljon aikaa, eikä pienen yrityksen ole järkevää pysäyttää muuta kehitystyötä vaikeasti ennustettavissa olevaksi ajaksi. Inkrementaalinen lähestymistapa monoliitin pilkkomiseen on siis järkevin tapa toteuttaa siirtymä. Seuraava kysymys on, kannattaisiko koko järjestelmä siirtää mikropalvelupohjaiseksi vai toteuttaa siirtymä vain osittain. Monoliittinen sovellus ei ole vielä kasvanut niin suureksi, että kehitystyö olisi hidastunut. Osittainen siirtymä keskittyen ongelmakohtiin voisi olla siis järkevä lähestymistapa. Newman (2015a) suosittelee monoliitin pilkkomisen aloittamista kohdista, jotka hyöty- vät eniten koodikannan erottamisesta tai jotka ovat helppoja erottaa. Monoliitissa ei ole havaittu kehitystä hidastavia kipukohtia. Suorituskyvyn kannalta on kuitenkin havaittu, että TimeLog-moduulin erilaisten raporttien luonti on ylivoimaisesti raskaimpia operaa- tioita järjestelmässä. Hyvin suurien raporttien luomista varten on jouduttu kasvattamaan palvelimelle asetettua skriptien maksimisuoritusaikaa. Raportointijärjestelmä voisi olla yksi järkevä kohta, joka voisi hyötyä mikropalveluarkkitehtuurin tehokkaammasta skaa- lautumisesta. Raportointijärjestelmä on riippuvainen suuresta määrästä sekä TimeLog- moduulin että muiden moduulien dataa, joten suorituskyvyn kannalta jatkuva suurien datamäärien kysely verkkokutsujen kautta voisi aiheuttaa ongelmia. Helpoimmin erotettavia osia ovat moduulit, jotka ovat vähiten kytkettyjä muhin järjes- telmän moduuleihin. Vähiten riippuvuussuhteita sisältävät moduulit olivat Identity (6 luokkariippuvuutta moodulista tai moduuliin), Permission (3) ja DrivingLog (9). Nämä moduulit on luotu kehitystyön alkuvaiheessa, kun monoliittia kehitettäessä pyrittiin vielä huomioimaan siirtymä mikropalveluarkkitehtuuriin, joten ne on pyritty pitämään mono- liitista helposti eroteltavina. 58 6.3 Suunnitelma Koska monoliitti ei ole vielä kasvanut niin suureksi, että sovellus olisi kärsinyt monoliitin ongelmista, sovelluksen siirtämisestä ei olisi suurta hyötyä. Koodikannan jakaminen mik- ropalveluiksi tekisi todennäköisesti yksittäisten mikropalveluiden koodin jonkin verran helpommin ymmärrettäväksi, sillä pienemmissä koodikannoissa on vähemmän sisäistet- tävää. Mikropalveluiden pakottama kova kotelointi myös estäisi teknisen velan kerty- mistä järjestelmään. Sen sijaan, että järjestelmä siirrettäisiin mikropalvelupohjaisiksi, siihen kehitettävät uu- det ominaisuudet voitaisiin kehittää mikropalveluina. Toteuttamalla uudet ominaisuudet mikropalveluina vältyttäisiin kasvavan monoliitin ongelmista ja toisaalta vältyttäisiin ole- massa olevan monoliittisen sovelluksen siirtämiseltä. Monoliitissa mahdollisesti ilmene- viä kipukohtia, kuten raportointijärjestelmää, voitaisiin myös erottaa mikropalveluiksi. Toinen strategia voisi olla toiminnanohjausjärjestelmän jakaminen useisiin laajempiin sovelluksiin suoranaisen mikropalveluarkkitehtuurin sijasta. Tällä tavalla vältyttäisiin suuren mikropalvelumäärän hallinnoinnin haasteilta sekä suurikokoisen monoliitin on- gelmilta. Strategiaa puoltaa se, että yritys ei pienen kokonsa vuoksi suoranaisesti hyödy mikropalveluarkkitehtuurin organisaatiotason hyödyistä. On myös oletettavaa, että so- velluksen käyttäjämäärät eivät kasva niin paljon, että mikropalveluarkkitehtuurin parem- masta skaalautumisesta olisi merkittävästi hyötyä. 6.4 Demoversio Demoversiossa yksi osa monoliittia erotettiin omaksi mikropalvelukseen. Tavoitteena oli demonstroida mikropalveluarkkitehtuuria käytännössä eikä niinkään toteuttaa tuotan- nossa käytettävää sovellusta. Demoversiolla pyrittiin myös tutkimaan käytännön toteu- tuksessa mahdollisesti ilmeneviä haasteita. 59 Irrotettavaksi osaksi valittiin DrivingLog-moduuli. Moduuli valittiin irrotettavaksi, koska se koettiin helpoimmaksi irrottaa. Muut löyhästi kytketyt moduulit, Permission ja Iden- tity, vastaavat käyttäjien autentikoinnista ja auktorisoinnista. Mikropalvelupohjaisissa järjestelmissä nämä toiminnot toteutetaan tavallisesti ainakin osittain erityisellä API-yh- dyskäytävällä, joka autentikoi ja auktorisoi käyttäjät ennen pyyntöjen ohjaamista palve- luille. Näiden moduulien siirto mikropalveluiksi olisi siis monimutkaisempaa kuin puh- taasti business-logiikkaa sisältävän DrivingLog- moduulin. Moduuli irrotettiin vaiheittain. Ensin moduuli irrotettiin monoliitin sisällä loogisesti koo- dikannasta niin, että ainoat riippuvuudet olivat tulevan mikropalvelun rajapintaa vastaa- vat Command- ja Query-luokat. Riippuvuuksien vähäisen määrän vuoksi tämä oli vaiva- tonta. Testejä päivitettiin sisältämään uudet Command- ja Query-luokat, ja erottelun on- nistuneisuudesta varmistuttiin ajamalla testit. Seuraavaksi moduuli irrotettiin monoliitista omaksi mikropalvelukseen. Moduulin koodi- kanta siirrettiin omaan docker-konttiinsa. Jokaisesta Command- ja Query-operaatiosta tehtiin päätepiste mikropalvelun rajapintaan. Tässä vaiheessa uusi mikropalvelu oli vielä kytketty monoliitin tietokantaan. Tietokan- nassa olevat DrivingLog-moduulin taulut olivat pysyneet selvästi erossa muista tauluista, joten taulujen siirtäminen omaan tietokantaan oli yksinkertaista. Monoliitin tietokan- nasta poistettiin kaikki vierasavaimet mikropalvelun tauluihin ja taulut siirrettiin mikro- palvelun omaan tietokantaan. Koska monoliitti vastaa autentikoinnista ja auktorisoinnista, kutsut mikropalveluun ohja- taan monoliitin kautta. Kutsuja monoliitista mikropalveluun ei siis tarvitse erikseen au- tentikoida. Kutsut mikropalvelusta monoliittiin autentikoidaan monoliitin sisältämän oauth-serverin kautta. 60 Mikropalvelussa käytettiin Lumen-sovelluskehystä. Lumen on Laravel-sovelluskehykseen perustuva keveyteen ja nopeuteen pyrkivä minimalistinen sovelluskehys, joka on suun- niteltu mikropalveluja varten. Lumen valittiin, koska se on laajalti yhteensopiva monolii- tissa käytetyn Laravel-pohjaisen koodin kanssa. Rajapinta toteutettiin REST-arkkitehtuu- rimallia käyttäen, ja kommunikaatio tapahtuu http-protokollaa käyttäen. REST ja http va- littiin, sillä monoliitin nykyinen rajapinta oli toteutettu näitä teknologioita käyttäen. Siir- tyminen tapahtumapohjaiseen kommunikaatioon olisi vaatinut järjestelmään laajempia muutoksia. 61 7 Tulokset Ensimmäisessä vaiheessa mikropalveluarkkitehtuurin testialustana käytetty sovellus päädyttiin toteuttamaan monoliittisena. Ensimmäisessä vaiheessa otettiin kuitenkin käyttöön mikropalveluarkkitehtuuria tukeva jatkuvan integraation ja toimituksen proses- siketju sekä sovelluksen ajaminen docker-konteissa. Toisessa vaiheessa monoliittisena toteutetun toiminnanohjausjärjestelmän siirtämisestä mikropalvelupohjaiseksi tehtiin selvitys. Selvityksessä tutkittiin, kannattaisiko sovellus siirtää mikropalvelupohjaiseksi. Sovelluksesta luotiin pienimuotoinen demoversio, jossa yksi osa järjestelmää irrotettiin monoliitista omaksi mikropalvelukseen. Irrotettava osa oli työmatkojen kirjaamiseen käytetty DrivingLog-moduuli. Moduuli oli kehitetty ennen mikropalveluarkkitehtuurista luopumista, joten se oli löyhästi kytketty muuhun monoliittiin. Kiinteän, löyhästi kytke- tyn monoliitin osan irrottaminen mikropalveluksi oli yksinkertainen toimenpide. Työn ensimmäinen tutkimuskysymys oli, kannattaisiko yrityksen ottaa käyttöön mikro- palveluarkkitehtuuri case-projektissa tai samankaltaisissa projekteissa? Vastaus tähän kysymykseen on: ei yrityksen nykyisessä tilanteessa. Sovellusta kehitettiin aluksi ajatuksena toteuttaa se mikropalvelupohjaisena. Mikropal- veluarkkitehtuurista luovuttiin kahdesta syystä. Ensinnäkin pelkästään mikropalvelu- suuntaisen monoliitinkin kehittäminen oli hitaampaa kuin puhtaasti monoliittisen ja toiseksi mikropalveluarkkitehtuurin hyödyt yrityksen näkökulmasta kyseenalaistettiin. Monoliittisen sovelluksen kehittäminen on alkuvaiheessa nopeampaa kuin mikropalve- lupohjaisen (Fowler, 2015b). Arkkitehtuurin alkuvaiheen hitaus havaittiin jo mikropalve- lusuuntaista monoliittia kehittäessä. Tietokantatason riippuvuuksien välttäminen johti joustamattomaan tapaan tehdä tietokantakyselyitä. Myös jatkuva moduulien välisten riippuvuuksien välttäminen hankaloitti kehitystyötä. Jälkikäteen voidaan myös havaita, 62 että palvelurajat olisivat vaatineet uudelleen määrittelyä, mikäli sovellus olisi toteutettu mikropalvelupohjaisena: esimerkiksi TimeLog- ja TimeRule-moduulit ovat tiukasti kytket- tyjä toisiinsa. Newman (2019) ei suosittele mikropalveluarkkitehtuuria tästä syystä startup-yrityksille. Hänen mukaansa mikropalveluarkkitehtuuri ratkaisee enemmänkin asemansa vakiinnut- taneen yrityksen ongelmia, kun sovelluksia tulee skaalata. Mikropalveluarkkitehtuuria käyttävät yritykset ovat pääasiassa suuria (Netflix, Guardian, Spotify). Case-sovellus ei tule oletettavasti pitkään aikaan kärsimään monoliittisen arkkitehtuurin ongelmista. Pit- källä tähtäimellä on kuitenkin oletettavaa, että case-sovellus kasvaa suuren toimialansa vuoksi riittävän suureksi hyötyäkseen mikropalveluarkkitehtuurista. Vaikka arkkitehtuu- rin käyttö olisikin pitkällä aikajänteellä kannattavaa, yritykselle on nuoren ikänsä ja pie- nen kokonsa vuoksi tärkeää saada nopeasti toteutettua toimivia järjestelmiä. Tulevia projekteja ajatellen huomioitavaa on kehitettävä sovellus ja yrityksen tilanne. Parhaiten mikropalveluarkkitehtuurista hyötyvät sellaiset sovellukset, joiden toimiala on riittävän laaja, niin että monoliitin ongelmat tulevat esille. Myös tuotteena tehdyt sovel- lukset sopivat paremmin mikropalveluarkkitehtuuriin, koska yrityksellä on täysi kontrolli sovelluksesta. Tämä mahdollistaa pitkäjänteisen kehityksen. Yrityksen tilanteen kannalta tulee huomioida monoliittista sovellusta hitaampi ja sitä kautta kalliimpi alkuvaiheen ke- hitys. Toinen tutkimuskysymys oli, mitä käytännön asioita on huomioitava mikropalvelupoh- jaista järjestelmää kehittäessä? Huomioitavat asiat voidaan jakaa kahteen osaan: itse sovellukseen liittyviin ja sovelluksen ympärillä toimivaan hallinnointiin liittyviin. Sovel- lukseen liittyvistä asioista saatiin hyvä kuva case-sovelluksen ja demoversion kautta. Hal- linnoinnin haasteiden selvittämiseen olisi tarvittu kokonainen tuotannossa toimiva mik- ropalvelupohjainen järjestelmä, joten niiden selvittämisessä jouduttiin nojautumaan kir- jallisuuskatsaukseen. 63 Demoversion kehitys onnistui nopeasti ja ongelmattomasti. Helppous johtui siitä, että erotetun monoliitin osan toteutuksessa oli pyritty siihen, että se olisi helposti erotetta- vissa. Mikäli mikropalveluarkkitehtuuria kehitetään monoliitti ensin -strategialla, on olennaista toteuttaa sovellus ’mikropalvelusuuntaisena’. Mikropalvelusuuntaisessa to- teutuksessa koodikanta ryhmitellään toimialan mukaan moduuleihin. Toimialojen mu- kaan jaetut moduulit pyritään pitämään mahdollisimman kiinteinä ja löyhästi kytkettyinä muihin moduuleihin, ja moduulien välinen kommunikointi rajoitetaan tiettyihin rajapin- toihin. Puhtaasti monoliittisena toteutetun järjestelmän mikropalvelupohjaisena toteuttamisen vaatima työmäärä riippuu siis järjestelmän rakenteesta. Mikropalveluarkkitehtuurista luopumisen jälkeen eniten kehityksen alla olleet osat olivat olennaisesti vahvemmin kyt- kettyjä muuhun sovellukseen. Esimerkiksi TimeLog-moduulin irrottaminen monoliitista olisi ollut huomattavasti suuritöisempi suuren riippuvuusmäärän takia. Vahvasti kytket- tyä moduulia ei ole myöskään järkevää irrottaa suoraan ilman muutoksia, sillä se johtaisi hajautettuun monoliittiin, josta Brown (2015) varoittaa. Hajautetussa monoliitissa mik- ropalvelut ovat tiukasti kytkettyjä toisiinsa, mikä johtaa suureen määrään verkkokutsuja sekä vaikeasti muutettaviin mikropalveluihin. Monoliitin rakenteesta riippuen on siis va- rauduttava kirjoittamaan suuriakin määriä koodikannasta uudelleen. Pienimuotoisella demoversiolla ei saada kuvaa laajan, tuotannossa toimivan mikropal- velupohjaisen järjestelmän hallinnoinnin haasteista. Lewis ja Fowler (2014) sekä New- man (2015a) painottavat kuitenkin mikropalvelupohjaisen järjestelmän ylläpidon ja hal- linnoinnin haasteita ja monimutkaisuutta. Newman (2015a) pitää mikropalvelupohjaisen järjestelmän ylläpidon edellytyksenä automatisoitua jatkuvaa integrointia ja toimitusta sekä lokitietojen keräysjärjestelmän käyttöönottoa. Käyttöön otetut docker-kontit ja jatkuvan integraation ja toimituksen ketju tukevat hyvin myös monoliittisen sovelluksen kehitystä. Kehitysympäristössä docker-kontit ovat aiem- 64 min käytettyjä virtuaalikoneita kevyempiä. Käyttämällä samoja docker-kontteja sekä tuo- tanto- että kehitysympäristöissä vältytään eroavista ympäristöistä johtuvista virheistä. Toiminnanohjausjärjestelmän kaikkien testien ajaminen CircleCI:n kautta pilvipalvelussa on useita minuutteja nopeampaa kuin testien ajaminen paikallisesti. Ketju myös paran- taa sovelluksen toimintavarmuutta edellyttäen, että testit eivät löydä ohjelmistovirheitä ennen kuin sovellus voidaan julkaista. 65 8 Johtopäätökset Työn päätarkoituksena oli selvittää, kannattaisiko yrityksen käyttää mikropalveluarkki- tehtuuria case-sovelluksessa tai tulevissa samankaltaisissa ohjelmistoprojekteissa. Toi- sena tavoitteena oli selvittää mikropalvelupohjaisen sovelluksen käytännön edellytyksiä. Työssä päädyttiin siihen, että mikropalveluarkkitehtuuri ei sovellu yritykselle sen tämän- hetkisessä tilanteessa. Mikropalveluarkkitehtuuri on pitkän aikavälin sijoitus, joka alussa hidastaa kehitystyötä merkittävästi mutta nopeuttaa kehitystyötä sovelluksen kasvaessa riittävän monimutkaiseksi. Alkuvaiheen kehityksen hitaus havaittiin case-projektin kehi- tystyössä. Buchgeherin (2017) esittelemän AMS Engineering -yrityksen mikropalvelu- pohjaisen projektin 2 500 tunnin alkuvaiheen kustannukset toimivat suuntaa antavana arviona siitä, kuinka paljon case-sovelluksen toteutus mikropalvelupohjaisena olisi lop- puun vietynä maksanut. Toteutettu mikropalvelupohjaisen järjestelmän demoversio on liian yksinkertainen, että kehitystyön nopeutta monimutkaisessa sovelluksessa voitaisiin analysoida sen kautta, mutta kirjallisuuskatsauksessa havaittiin yleinen konsensus, että mikropalveluarkkitehtuuri nopeuttaa kehitystyötä sovelluksen kasvaessa riittävän moni- mutkaiseksi (esim. Fowler 2015; Newman 2015a). Työn toimeksiantajayritykselle on nuoren ikänsä ja pienen kokonsa vuoksi tärkeämpää saada kehitettyä sovelluksia nope- asti. Case-sovelluksen havaittiin kuitenkin soveltuvan hyvin mikropalvelupohjaiseksi. Case- sovelluksena käytetyn toiminnanohjausjärjestelmän kaltaiset pilvipalveluna toteutetta- vat sovellukset ovat hyvin mikropalvelupohjaiseksi soveltuvia ohjelmistoja. Kuten case- sovellusta kehitettäessä huomattiin, toiminnanohjausjärjestelmien toimiala on tavan- omaisesti riittävän laaja, että järjestelmät hyötyvät kompleksisuutensa takia mikropalve- luarkkitehtuurista. Kirjallisuuskatsauksessa havaittiin, että mikropalvelupohjainen järjes- telmä hyötyy pilvipalvelupohjaisuudesta, sillä se mahdollistaa helpomman skaalauksen ja hallinnoinnin (Newman, 2015a). Case-sovelluksen järjestelmä myös toteutettiin tuot- teena, mikä mahdollistaa kehityksen pitkällä aikavälillä. Mikäli ohjelmistoprojekti tuote- taan tilaustyönä asiakkaalle, voidaan mikropalveluiden käyttämisen edellytyksenä pitää 66 asiakkaan sitoutumista projektin pitkäaikaiseen kehitykseen, sillä lyhyissä projekteissa mikropalveluarkkitehtuurin hyödyt jäävät saavuttamatta. Käytännön edellytyksiä pyrittiin tutkimaan toteuttamalla case-sovellus mikropalvelu- pohjaisena. Tavoitteesta jouduttiin luopumaan aikataulusyitten takia, mikä sinällään vah- vistaa käsityksen arkkitehtuurin korkeasta lähtökustannuksesta. Sen sijaan toteutettiin pienimuotoinen demoversio, jossa yksi osa sovellusta irrotettiin mikropalveluksi. Yhden ohjelmiston moduulin erottaminen mikropalveluksi onnistui nopeasti. Yksinkertaisuu- tensa takia voidaan kuitenkin katsoa, että demoversio antaa vajavaisen kuvan arkkiteh- tuurin todellisista haasteista. Edellytyksiä selvitettäessä nojauduttiinkin siksi vahvasti kir- jallisuuskatsaukseen. Demoversio kuitenkin havainnollistaa sen, että monoliitin mikro- palveluiksi pilkkomisen työmäärä riippuu suuresti monoliitin rakenteesta. Valittu irrotet- tava osa oli kiinteä ja löyhästi kytketty muuhun sovellukseen ja siten helposti irrotetta- vissa. Tutkimuksen keskeisin tulos on yleistettävissä muihinkin yrityksiin, joilla ei ole varaa kor- keisiin alkuvaiheen kustannuksiin ja uuden teknologian käyttöönottoon liittyvään riskiin. Useat pk-yritykset ovat tällaisia yrityksiä. Etenkin startup-yrityksillä on tavallisesti rajalli- set resurssit ja tämän vuoksi kiire saada kassavirtaa tuottavia sovelluksia tuotantoon. Yleisesti ottaen mikropalveluarkkitehtuuria harkitsevien yritysten tulee ottaa huomioon korkeat alkuvaiheen kustannukset ja hajautettujen mikropalvelujen hallinnoinnin moni- mutkaisuus. Koska mikropalveluarkkitehtuurin hyödyt tulevat esille vasta laajoissa ja mo- nimutkaisissa sovelluksissa, mikropalveluarkkitehtuuri ei ole ratkaisu, jota kannattaisi käyttää joka sovelluksess