úterý 29. srpna 2017

Java vs Javascript na serveru

Dlouho jsem přemýšlel, zda se vůbec pustit do porovnání těchto technologií. Vždy jsem kolem tohoto tématu dost kličkoval, protože je natolik kontroverzní, že si asi mnoho lidí bude myslet, že jsem se zbláznil, nebo že porovnávám jablka a hrušky.

Nicméně, vždy je dobré se na technologie podívat právě tímto způsobem. Zjistit, jaké výhody či nevýhody může mít javascript oproti Jave, je přesně to, co možná mnoho lidí hledá.

Abych hned na začátku uklidnil všechny, kteří očekávají, že budu velebit javascript a tvrdit, že je to ta nejlepší technologie na server aplikace, tak je můžu uklidnit. Není. Když člověk potřebuje připevnit poličku, pomocí vrutů, také na to nepoužije kladivo, ale šroubovák. Na druhou stranu, kladivo je zase skvělý nástroj na hřebík. A přesně tímto způsobem bychom měli přemýšlet i při výběru technologií. Nekoukat jen na to, co se nám líbí, ale i na další aspekty, které nakonec můžou rozhnout o úplně jiné variantě.

Pojďme ji nejdříve v rychlosti říct, co stálo za úspěchem Javy, jakožto primárního jazyka na server aplikace....

Java na serveru

Pokud byste udělali mezikorporátní anketu na to, jaké technologie používají na backend systémy, s jistotou vám můžu říct, že by vyhrála Java. Těch důvodů, proč tomu tak je, je hned několik.

Java je multiplatfomní
Asi bych to shrnul do jedné věty: "Můžeme mít server na linuxu". A to je to, co přesně spoustu firem chce. Naproti tomu, je to přesně ten samý důvod, proč se C# nestal králem. Ač se o to snaží, myslím, že už se to nikdy nepovede. Existuje spoustu firem, které mají stack na .NETu, ale věřte mi, že v celkovém měřítku hrají druhé housle a vždycky hrát budou.

Java má/měla J2EE specifikaci
V době, kdy se java stala populárním jazykem, tak Sun Microsystems udělal jeden dobrý tah. Vytvořil dvě základní specifikace J2SE a J2EE. Ta první se týkala Javy na klientovi a ta druhá na serveru. Ona specifikace nebyla nijak převratná. Naopak, v dnešní době byste na staré EJB koukali jako na jeden velký antipattern. Však i Rod Johnson o tom napsal knihu a tím vlastně vznikl Spring Framework, který kraluje dodnes.
Ale přesto, byla to specifikace. Na tomto základě začaly vznikat aplikační servery a vůbec celý ekosystém kolem javy se stal robustním. Robustním natolik, že pokud někdo dnes řekne, že potřebuje vytvořit větší systém, ihned většině architektům a vývojářům vyskočí java.

Java je staticky typovaný jazyk
Dalším důvodem, proč se Java stala tak populární je to, že je to staticky typovaný jazyk. Zjednodušeně to znamená, že pokud píšete kód, musíte deklarovat typy. Pokud byste toto pravidlo porušili, projekt se vám jednoduše nezkompiluje. Dnešní editory kódu umí dělat kompilaci za běhu a tak programátorovi rovnou říkat, kde má chybu.
Toto se ukázalo jako důležitá vlastnost jazyka. V případě, že upravujete existující projekt, budete za staticky typovaný jazyk rádi.

Java je objektově orientovaný jazyk
Psát o tom, co je OOP je asi zbytečné. Na toto téma existuje milióny článků. Nicméně uvést to jako důvod musím. Bez OOP by nebyla Java a bez Javy by nebyl svět tak zfanatizován pojmem OOP :)
Díky tomuto principu totiž vznikly věci jako je například DI/IoC kontejnery. A tím nejznámějším právě onen Spring.

Java má garbage collector
Garbage collector, nebo chcete-li sběrač odpadků je vlastnost, díky které se v Jave příliš nestaráte o to, jak váš projekt alokuje zdroje. Zjednodušeně se dá říct, že vedle vaší aplikace běží agent, který se snaží o to, aby po vás čistil to, co máte v naalokované paměti. Tím se snižuje riziko, že napíšete aplikaci, která shodí celý server.

O Jave by se dalo napsat spoustu zajímavých věcí. Sám jsem s tím jazykem žil 10 let. V době, kdy jsem Javu objevil a kdy jsem se jí naučil, jsem si myslel, že už nic lepšího nemůže být. Nicméně žijeme v roce 2017 a je dobré asi zmínit ještě jednu věc. Vím, že tím asi naštvu část javistů, ale pravdou je, že java svůj vrchol slávy má nejspíše za sebou. Její stagnace v podobě nových vlastností je jasná ukázka toho, že její budoucnost není tak růžová, jak si možná někdo myslí.

Nyní se konečně pojďme podívat na Javascript....

Javascript na serveru

Javascript na serveru nemá příliš dlouhou historii. Pokud bych měl být objektivní, tak bych nejspíše řekl, že se javascript spíše hledá. Pokud někdo zvolí javascript jako server technologii, měl by si nejdříve zjistit několik věcí, které se nakonec můžou stát zásadním blokujícím problémem.

Node.js

Pokud budeme mluvit o javascriptu na serveru, tak je nutné zmínit samotné běhové prostředí. Zjistil jsem, že existuje spoustu lidí, kteří vlastně vůbec netuší, co je ono Node.js.

Takže ještě jednou: "Node.js je běhové prostředí". Stejně tak, jako je pro Javu běhové prostředí JVM či pro C# .NET.

Abychom z Node.js vytvořili server, necháme náš skript bežet na námi definovaném portu. Nic víc, nic míň.

Single thread

Javascript je single thread. To je asi nejzásadnější věc, se kterou musíme hned od začátku počítat.

V jave je to nejčastěji tak, že se každý request spustí ve vlastním threadu. Výhodou tohoto řešení je, že se neblokují další requesty, které přicházejí v okamžiku zpracování.

Naproti tomu v Node.js všichni klienti sdílejí pouze jeden thread. Abychom to lépe pochopili, pojďme si udělat jednoduchou ukázku:
import * as Express from 'express';

const app = Express();

app.get('/', (request, response) => {
    response.json({success: true});
    const start = Date.now();
    while (true) {
        if ((Date.now() - start) > 5000) {
            break;
        }
    }
});

const port = process.env.PORT || 8080;
app.listen(port, () => {
    console.log(`Application ready on port: ${port}`);
});

V případě, že nyní zavoláte http://localhost:8080/, tak ihned dostanete odpověd v podobě {"success": true}. V případě, že se znovu pokusíte poslat request, bude celý thread zablokovaný 5 vteřin.

Toto je jednoduchý důkaz toho, jak funguje samotné Node.js.

Asi každého hned napadne, že pokud je Node.js single thread, tak v něm napsat použitelnou server aplikaci je nemožné. Opak je pravdou...

Event loop / Async await

Abychom byli schopni použít Node.js v produkci, je třeba dodržet několik pravidel. Tím nejzákladnějším je, že vše musí asynchronní.

Jelikož víme, že javascript je single thread, tak jak vlastně může fungovat taková metoda setTimeout, jak vlastně může fungovat Promise, či obecně všechny callbacky?

Odpovědí je event loop. Pokud se chcete detailněji seznámit s tímto mechanismem, můžete začít klidně zde: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/

Pojďme si to zjednodušit. Samotné callbacky fungují tak, že se pustí v podprocesu vedle a dále se vykonává náš kód.

Pojďme si udělat krátkou ukázku:
const runDirect = () => {
    console.log('This is direct');
};
const runLater = (timeout: number) => {
    setTimeout(() => {
        console.log(`This is later with timeout: ${timeout}`);
    }, timeout);
};

runDirect();
runLater(100);
runLater(0);
runDirect();

Výsledkem bude:
This is direct
This is direct
This is later with timeout: 0
This is later with timeout: 100

Toto je ukázka toho, že v javascriptu existuje asynchronní zpracování.

Nyní si pojdmě ukázat, jak nepsat a jak psát kód na serveru:
import * as Express from 'express';
import sync_query from 'sync-query';
import async_query from 'async-query';

const app = Express();

app.get('/bad', (request, response) => {
    const users = sync_query('SELECT * FROM users');
    response.json({users});
});

app.get('/good', (request, response) => {
    async_query('SELECT * FROM users').then((users) => {
        response.json({users});
    });
});

app.get('/better', async (request, response) => {
    const users = await async_query('SELECT * FROM users');
    response.json({users});
});

const port = process.env.PORT || 8080;
app.listen(port, () => {
    console.log(`Application ready on port: ${port}`);
});

V případě první routy bad bychom se dostali do stavu, kdy bychom mohli celé vlákno zastavit a souběžné zpracování by nebylo možné. Tedy ostatní volající by čekali, než by celá metoda byla vykonána. Je to tedy přesně ta ukázka, která říká: "Takto ne.".

Routa good a better je v podstatě ten stejný zápis. Využívá vzoru Promise, který je možné přepsat pomocí async/await. Zde je vidět, že se kód vykonává deklarativně. Tedy jedná se o callback, který se "někdy vykoná".

Pokud bychom toto porovnali s tím, jak bychom zapisovali kód v Jave, tak největším rozdílem je to, že v Node.js píši deklarativně. Zjednodušeně řečeno, celá naše aplikace v Node.js bude plná callbacků. V Jave bych psal vše imperativně. Tedy to, co se má vykonat, se vykoná hned a pak následuje další série příkazů.

Alternativy v Javascriptu

Nyní se pojďme podávat na alternativy, které by javista hledal v Node.js

REST - Spring vs Express

První věcí, která se hned nabízí je psaní REST API.

Zde si dovolím tvrdit, že v případě Node.js jste na tom o trochu lépe, než v případě Javy. Nejde tak ani o to, že byste měli k dispozici více možností, právě naopak. Ale hlavně pro to, že tvorba jednotlivých endpointů je v Expressu o dost snažší. Pokud k tomu připočítám podporu middleware v Expressu, nemůže mu svět Javy konkurovat.

Druhou nespornou výhodou je to, že v javascriptovém světě je pro vás JSON přirozený formát. Ač se to na první pohled možná nezdá, o dost se zvyšuje transparentnost zpracování vstupu a výstupu.

Zde bych ještě rád zmínil jednu věc a tou je samotný fakt, že REST API v Node.js je spíše přežitek. Od doby, kdy světlo světa spatřilo GraphQL, neexistuje jediný rozumný důvod používat REST.

DI kontejner - Spring vs ???

Jedna z nejčastějších otázek v javascriptovém světě na serveru je DI kontejner. Od doby, kdy se Node.js objevilo, vzniklo asi tak tisíc knihoven, které měly převést Spring do světa Node.js.

Můžete hádat, kolik z nich se reálně uchytilo a jsou dnes populární jako Spring. Je to přesně 0.

Myslím, že existují dva hlavní důvody proč tomu tak není.

1. Absence anotací
Až do nedávné doby nebylo možné využít AOP princip. Alespoň ne tak efektivně jako v případě Javy. Proto je většina knihoven, které se o DI kontejner snaží, jaksi krkolomná. V budoucnu je možné, že se tato situace změní. Přeci ECMA má nyní dekorátory (čti anotace) a to může celou tuto situaci vyřešit.

2. Javascript je jiný
Druhým důvodem je to, že javascript samotný DI vlastně ani moc nepotřebuje. Respektivě ho nechce. Node.js vychází z patternu modulů, které do svého kódu importujete. Proto existuje velká část javascript vývojářů, kteří místo DI principu volí funkcionální přístup.
O tom, která z těchto variant je lepší se vedou dlouhé diskuze.
Osobně se spíše přikláním k druhé variantě a to tedy, že DI skutečně nepotřebuji. Když si vezmu v potaz to, co skutečně od serveru očekávám, tak jeho výhody mi moc nepomůžou. Abych použil DI jenom kvůli testům a mockování závislostí, tak od toho zde máme knihovny jako proxyquire.

Pokud bych se měl pokusit o objektivní názor, tak zde je na tom Java lépe. Lépe hlavně pro to, že tyto věci má víceméně vyřešené. V javascriptovém světě je toto přesně ta část, která na svůj Spring ještě čeká. Spring který využije funcionálního paradigma.

Persistence - JPA vs Mongoose

Zde se názory asi nejvíce liší. Na jedné straně je skvělé, že mám v Jave věci jako je JPA, na straně druhé je fakt, že JPA je dobré tak pro středně velký projekt.
Pokud bych měl porovnat JPA a Mongoose z Node.js, tak bych jasně řekl, že Mongoose je na tom lépe. Nejde o funkcionalitu, ale samotný fakt, že s MongoDB komunikuji pomocí JSONu. V té chvíli se mi dost stírá ona hranice mezi kódem na serveru a jednotlivými dotazy do databáze.
Java by byla v persistentní vrstvě skvělá. Ale jen v případě, že by databáze byla objektová a ne relační.

Messaging - JMS vs Cloud lambda functions

Pro lidi ze světa Javy je JMS známá technologie. V podstatě jde o to, že slouží asynchronní k výměně informací mezi systémy. Pokud bychom toto hledali v javascriptu, tak nic použitelného nenaleznete. Nicméně v roce 2017 je třeba se zamyslet, zda neexistují i jiné varianty.

Tím nejlepším, co můžeme dnes využít jsou Cloud lambda funkce. Více viz: Google Cloud dokumentace.

Node.js na produkci

Použít javascript na serveru není žádná partizánská činnost. Existuje velké množství firem, které Node.js na produkci používají. Pokud si někdo myslí, že jde o malé firmy, tak je musím zklamat. Javascript na serveru používají takové firmy jako je Microsoft, IBM, PayPal, Netflix či třeba LinkedIn.

Napsat backend v javascriptu může být dobrá, ale také špatná volba. V současném stavu je to tak, že Node.js je vhodné jako backend pro frontend. Pokud onen backend slouží jen jako rozhraní mezi databází a webovou aplikací, tak neexistuje moc důvodů, proč Node.js nepoužít.

Ve chvíli, kdy backend znamená složitou orchestraci jiných systémů, složité výpočty, apod, tak zde je využití javascriptu dost na hraně. Osobně si myslím, že zde javascript ještě není. Nejde ani tak o to, že by to nebylo možné, ale spíše fakt, že samotný jazyk je na tyto účely nedospelý. I když ECMA specifikace postupně napravují škody, které byly na javascriptu napáchány, tak bude ještě nějaký čas trvat, než bude skutečně použitelný i v robustnějších backend systémech.

Závěr

A co vy? Používáte Node.js na produkci?

React a hrátky s TypeScriptem

V minulosti jsem se již několikrát zmiňoval, že používat JavaScript bez statických typů, je stejné jako jezdit na kole poslepu. Nemusí se...