Archiv autora: Mirek

Jak validovat český bankovní účet v C#?

Nedávno jsem zjistil, že číslo českého bankovního účtu musí splňovat kontrolní součet definovaný ČNB.

Nenašel jsem na internetu žádný kód v C# tak jsem se rozhodl napsat svůj vlastní ve formě extension metody.

public static class StringExtensions
{
    private static Regex CzBankAccountRegex = new Regex(@"((\d{0,6})-)?(\d{1,10})/(\d{4})");
    public static bool IsValidCzBankAccount(this string bankAccountInput)
    {
        if (bankAccountInput == null)
            return false;

        Match match = CzBankAccountRegex.Match(bankAccountInput);
        
        if (!match.Success)
            return false;

        string acc1 = match.Groups[2].Value;
        string acc2 = match.Groups[3].Value;
        string bankCode = match.Groups[4].Value;

        if (string.IsNullOrEmpty(acc1))
            return CzBankAccountControlSum(acc2) && CzBankCodes.Contains(bankCode);

        return CzBankAccountControlSum(acc1) 
            && CzBankAccountControlSum(acc2)
            && CzBankCodes.Contains(bankCode);
    }

    private static readonly int[] CzBankAccountWeights = new int[] { 6, 3, 7, 9, 10, 5, 8, 4, 2, 1 };
    private static bool CzBankAccountControlSum(string accountNumberStr)
    {
        string padded = accountNumberStr.PadLeft(10, '0');
        int sum = 0;
        for (int i = 0; i < padded.Length; i++)
            sum += int.Parse(padded[i].ToString()) * CzBankAccountWeights[i];

        return sum % 11 == 0;
    }

    private static readonly HashSet<string> CzBankCodes = new HashSet<string>()
    {
        "0100", "0300","0600","0710","0800","2010","2060","2070","2100","2200",
        "2220","2250","2260","2275","2600","2700","3030","3050","3060","3500",
        "4000","4300","5500","5800","6000","6100","6200","6210","6300","6700",
        "6800","7910","7950","7960","7970","7990","8030","8040","8060","8090",
        "8150","8190","8198","8199","8200","8220","8230","8240","8250","8255",
        "8265","8270","8280","8293","8299","8500"
    };
}

Jak si mohu nejlépe vydělat v IT?

Chtít si vydělat lepší peníze je záměr, který v IT hledá mnoho lidí a není na tom nic špatného. IT průmysl je obrovský a nabízí spoustu možností.

Kde se v IT nachází nejvíce peněz ovlivňuje několik faktorů.

Které technologie?

Moderní: Nedává smysl investovat svůj čas do vzdělávání se v zastaralých, nepoužívaných technologiích, které nikdo na trhu práce nikdo moc nepoptává.

Již nějakou dobu ustálené: Vyhýbejte se příliš novým, příliš čerstvým technologiím a soustřeďte se na technologie, které již nějakou dobu na trhu existují a jsou dlouhodobě modernizované. Neinvestujte svůj čas do technologií, které jsou „nové, úžasné a revoluční“, toto platí zejména u Javascriptových frameworků, které vznikají a zas upadají do zapomění velmi rychle.

Stále populární a používané: I kdysi dlouho ustálené technologie mohou časem zaniknout a zestárnout. Vybírejte pouze ty technologie, které mají na trhu práce uplatnění, protože v nich všichni pracují. Nevybírejte ty, které již zanikly, zastaraly a už o ně není zájem.

Mají velkou komunitu: Toto je velmi důležitá vlastnost. Všichni specialisté v konkrétní technologii používají internet ke sdílení a komunikaci svých znalostí mezi ostatními. Čím větší komunita, tím snažší je se danou technologii naučit ve všech jejích složitostech, protože internet je plný otázek, odpovědí a témat k dané technologii. Technologie s příliš malou komunitou nejsou nutně hůř placené ale pro úplné nováčky v IT nejsou vhodné.

Nespecializované: Když začínáte v IT od nuly tak nedává smysl zaměřovat veškerou pozornost na technologie, které mají příliš specializované použití. Naopak se snažíme vybrat ty technologie, které mají velmi široké pole působnosti a lze v nich vybudovat cokoliv pro kohokoliv. Tyto technologie jsou na trhu nejvíce poptávané.

Které odvětví?

Přestože pracujete se správnými technologiemi, musíte přemýšlet i nad tím, v jakém odvětví se necháte jako ajťák zaměstnat.

Nejlepší peníze pro ajťáky mají instituce ve finančnictví: banky, pojišťovny, finanční skupiny atd. Pro úplného nováčka v IT bez předchozích zkušeností nebo formálního vzdělání nemusí být snadné se dostat už jen na juniorní pozici, i když dlouhodobý nedostatek lidí na trhu práce tlačí i korporáty k tomu snižovat nároky.

Konkrétní odvětví není zas tak důležité. Co je pro růst mzdy důležité je nezůstávat na žádné pozici déle než 2 – 3 roky — a to až do doby, než se svoji mzdou budete opravdu spokojení. Samozřejmě na jakékoliv pozici v IT je nutné zůstat minimálně rok protože vás nikdo nebude chtít zaměstnat, pokud budete měnit zaměstnavatele po pár měsících.

Toto je má zkušenost z praxe. Je úplně k ničemu snažit se v jakékoliv firmě vyjednávat vyšší mzdu, firmy zaměstnávající ajťáky jsou z nějakého důvodu stále velmi neflexibilní a nikdy vám nepřidají tolik, kolik byste měli při změně zaměstnavatele.

Zároveň platí, že množství zářezů v životopise aspoň na 1 – 3 roky působí na trhu práce IT specialistů mnohem lépe, než jeden zářez na 10 let. Trh práce se totiž na vás začne koukat jako na člověka, který načerpal znalosti z mnoha různých prostředí a do jejich firmy můžete přinést vědomosti, které oni nemají. Jste tak mnohem zajímavější v porovnání s člověkem, který pracoval 10 a více let pro jednu firmu (a to i na různých pozicích).

Zaměstnání nebo práce na IČO?

V IT oboru velmi často platí, že jako zaměstnanec máte sice typické zaměstnanecké benefity ale většinou až o 50% menší peníze v důsledku toho, že v ČR má zaměstnavatel povinnost odvádět za vás mnohem vyšší daně.

Zkušený programátor s hrubou mzdou 160.000 Kč měsíčně dostane na ruku 120.000 Kč ale zaměstnavatel státu musí zaplatit 94.000. Celkově za programátora tedy firma musí zaplatit 214.000 Kč (120000+94000).

Firmy nabízí na IČO mnohem víc, protože platí pouze to, co si ajťák vyfakturuje. To ovšem znamená, že se stanete podnikatelem a své příjmy si musíte zdanit sami. Jako podnikatel na IČO máte menší nárok na důchod, nemáte placenou dovolenou, žádné stravenky, žádné zaměstnanecké výhody a podobně.

Nelze se tedy koukat pouze na finanční stránku ale i na to, že každý má v různém životním období různé preference. IČO dává větší smysl pro mladého, svobodného člověka zatímco zaměstnání dává smysl pro někoho s rodinou a dětmi.

Potřebuji mít vysokou školu, aby mě někde vzali?

Dodnes si dělám legraci z toho, že po úspěšném absolvování vysoké školy s magisterským titulem jsem měl nadřízené, kteří neměli ani maturitu.

V IT oboru záleží na praxi, na zkušenostech a na zářezech v životopisu víc, než na formálním vzdělání. Jednu známá IT firma v ČR údajně zaměstnává jen vysokoškoláky z ČVUTu. Pro mě je takový požadavek spíš vykřičník, že v této firmě pracují lidé s přebujelým egem, kteří si asi něco potřebují dokazovat.

Potřebuji umět angličtinu?

Zpravidla ano, aspoň na úrovni porozumění psaného textu. Dost ale záleží, kterým směrem se v IT chcete vydat a kolik chcete do svého vzdělání investovat.

  • V korporátech jsou časté požadavky pro angličtinu na úrovni mluveného slova.
  • Na českém internetu existuje řada českých návodů, jak se naučit programovat v různých technologiích.
  • Česká knihkupectví jsou plná knih pro úplné začátečníky.
  • Existují česky přednášené kurzy a bootcampy. Jděte na google a vyhledejte „kurz programování“. Nabídka je v dnešní době obrovská.
  • Angličtinu se můžete učit za pochodu. Myslete na to, že se potřebujete dostat aspoň na úroveň čtení textu.

Potřebuji umět vyhledávat na Googlu?

Pokud jako pacient svému lékaři řeknete, že jste si něco o své nemoci vygooglil, protočí oči, protože jste pitomec, co důvěřuje každé blbosti, kterou na Googlu najde. A má pravdu, jste totální pitomec, protože porovnáváte obrovsky komplexní, náročné, lékařské vzdělání trvající mnoho let se svými deseti vteřinami na internetu.

V IT oboru je to úplně naopak.

Pokud si neumíte rychle a efektivně dohledat informace, které pro svoji práci potřebujete, pokud nejste připraveni se neustále vzdělávat, zjišťovat a dohledávat si informace, které neznáte, máte dost drasticky sníženou šanci, že vás někdo bude chtít v IT zaměstnávat. A to i v případě, že máte vysokoškolský titul.

IT profesionálové se něco nového z googlení učí téměř každý den. Celý IT průmysl existuje jen díky znalostem, které ajťáci mezi sebou na internetu veřejně sdílí. Kdyby neexistoval Google, celý IT průmysl se sesype jak domeček z karet — což zní, jako kdybych přeháněl jenže nepřeháním, toto je jedna z největších absurdit IT oboru.

Příklad z praxe: Během práce na projektu jsem zjistil, že nerozumím, jakým způsobem se vlastně používají asymetrické a symetrické šifry v TLS protokolu a co přesně je „certificate pinning“. Toto jsem v rámci projektu potřeboval vědět. Proto jsem začal googlit „how TLS works“ a „what is certificate pinning“.

Chci jít do IT a chci pro to něco začít dělat. Kde mám začít?

Pokud začínáte úplně od nuly, nabízí se několik cest.

  • Programování. Alfa a omega IT oboru. Jakmile se naučíte programovat, dobře placenou práci si najdete vždy.

    Nikdo vás nikdy nezaměstná na pozici programátora, pokud začínáte úplně od nuly.

    ALE: situace se drasticky změní, pokud nezačínáte od nuly (0) ale od 0.03. Musíte investovat do svého vzdělání, něco naprogramovat, něco vytvořit, vyrobit nějaký projekt, ať už podle knihy, kurzu online zdarma nebo rekvalifikačnímu kurzu, na který budete pár měsíců chodit. Někteří zaměstnavatelé o vás začnou mít zájem protože pro ně je vaše osobní investice do vzdělání dostatečný důkaz nejen toho, že umíte nějaké minimum ale že to zároveň se svým vstupem do IT myslíte vážně.
  • Junior tester – hromada lidí se do IT dostane přes testerské pozice. Stačí umět postupovat podle instrukcí avšak bez praxe a zkušeností neočekávejte moc peněz. Za svoji kariéru jsem poznal lidi, kteří se během testování začali aktivně zajímat o to, jak software funguje. Ve vlastním volném čase se začali učit programovat a ve stejné firmě povýšili na junior programátora s mnohem vyšší mzdou, než jakou měli na pozici testera. Absolutně nutný je ale zájem něco se naučit.
  • Projektový, produktový nebo aplikační manažer — tyto pozice zpravidla nevyžadují specializované IT znalosti ale očekává se dobré vystupování a komunikační schopnosti.
  • Na volné noze – IT obor je úžasný tím, že díky němu můžete zůstat navždy nezávislí. Pokud začínáte od nuly, zároveň máte podnikavou povahu a nebojíte se rizika pak váš úspěch záleží zcela na vás. V takovém případě je ale nutné neustále investovat do svého vzdělání, neustále se učit. To je ale pravda téměř ve všem, pokud jde o podnikání na volné noze 🙂

Které profese a technologie v IT jsou nejlépe placené?

StackOverflow Survey 2022

Nejlepším a nejvěrohodnějším zdrojem informací o tom, které profese jsou v IT nejlépe placené je StackOverflow survey neboli anketa, kterou StackExchange pořádá každý rok a kterou vyplňují IT profesionálové z celého světa.

Co je StackOverflow nebo StackExchange? StackOverflow (SO) vznikl v roce 2008 jako alternativa zdarma pro tehdejší placené weby, kde se člověk mohl zeptat na nějakou technologickou otázku a dostal na ni odpověď od jiného, technologicky zaměřeného uživatele. SO je dodnes zdarma a jeho úspěch je obrovský až do té míry, že se v IT odvětví považuje SO za naprostý standard a za velmi kvalitní a komunitou spravovanou studnici informací snad ve všech IT technologiích a podoborech, které v IT odvětví lze nalézt.

Celosvětový průměr, USA, Německo

Následující tabulka obsahuje přepočtené celosvětové výsledky, výsledky pouze za USA a výsledky pouze za Německo, jehož hodnoty jsou podle mně nejblíž tomu, co lze očekávat i v ČR. Ve výsledcích ankety jsou uvedené hodnoty medián (střední hodnota). V tabulce níže jsem tyto hodnoty přepočetl na měsíční průměrnou mzdu v Kč. Skutečná průměrná hodnota se samozřejmě bude od měsíční průměrné mzdy málo lišit vzhledem k velkému vzorku dat.

Pokud se chceš podívat, co jednotlivé pozice znamenají, přejdi na konec tohoto článku, kde jsou k pozicím v angličtině uvedené i české termíny a vysvětlení, co tyto pozice vlastně obnáší.

PoziceCelosvětová průměrná měsíční výplata v KčUSA průměrná měsíční výplata v KčNěmecko, průměrná měsíční výplata v Kč
Senior executive250000422000205000
Engineering manager236000380000202000
Engineer, site reliability202000370000175000
Security professional192000329000148000
Cloud infrastructure engineer189000358000162000
Blockchain168000374000169000
Engineer, data168000316000162000
DevOps specialist166000316000154000
Marketing or sales professional165000274000125000
Product manager162000327000157000
Data scientist or machine learning specialist157000316000157000
Scientist152000274000135000
Data or business analyst146000264000151000
Developer, back-end144000316000148000
Developer, embedded applications or devices144000295000143000
Developer, desktop or enterprise applications141000280000146000
Database administrator140000274000146000
Project manager140000295000148000
Developer, full-stack140000295000141000
Developer, QA or test140000253000141000
System administrator137000267000135000
Developer, game or graphics135000295000124000
Designer132000267000124000
Educator131000274000146000
Developer, front-end126000280000135000
Developer, mobile119000304000142000
Academic researcher117000232000128000
Student4600021100057000

Přijde mi fascinující, že v USA si člověk v IT který ještě studuje, vydělá stejně, jako manažer v Německu na vrcholové pozici.

Výsledky ankety v ČR

Tuto anketu vyplnili i lidé z ČR ale těch odpovědí je tam celkem málo. 692 odpovědí z ČR a 229 ze slovenska.

Odpovědi z ČR jsem vyfiltroval do tabulky níže.

Abyste podle těchto údajů zjistili, kolik vydělávají například devopsáci v ČR, zadejte do pole v pravém horním rohu tabulky „devops“. Vypíše to 54 řádků což je podle mně opravdu málo, v originálních statistikách k tomu totiž musíte zohlednit následující:

  • s jakou praxí člověk povolání vykoná
  • v jaké je věkové kategorii
  • údaje nemusí být zadány pravdivě
  • které jiné pracovní role byly stejným uživatelem zadány. V IT je totiž úplně normální, že vaše pozice není přesně definovaná a ke svému popisu práce vykonáváte další věci navíc.
DevTypeCountryCompTotalCompFreqMěsíčně
Developer, back-end;Cloud infrastructure engineerCzech Republic1700000Monthly1700000
Developer, full-stack;Developer, back-endCzech Republic1440000Monthly1440000
Developer, front-end;Developer, full-stack;Developer, back-end;Developer, desktop or enterprise applicationsCzech Republic800000Monthly800000
Developer, full-stack;Developer, back-end;DevOps specialist;Cloud infrastructure engineer;Product manager;Senior Executive (C-Suite, VP, etc.)Czech Republic500000Monthly500000
Developer, full-stack;Educator;Database administrator;DevOps specialist;Cloud infrastructure engineerCzech Republic350000Monthly350000
Developer, back-end;Developer, desktop or enterprise applications;Developer, mobileCzech Republic300000Monthly300000
Developer, full-stack;DevOps specialistCzech Republic3000000Yearly250000
Developer, back-endCzech Republic250000Monthly250000
Data scientist or machine learning specialist;Project manager;Data or business analyst;Scientist;Product manager;Senior Executive (C-Suite, VP, etc.);BlockchainCzech Republic250000Monthly250000
Developer, back-endCzech Republic240000Monthly240000
Developer, front-endCzech Republic2700000Yearly225000
Developer, back-endCzech Republic220000Monthly220000
Developer, back-endCzech Republic220000Monthly220000
Developer, mobileCzech Republic210000Monthly210000
Developer, front-end;Developer, full-stack;Developer, back-end;Educator;Project manager;Designer;Product managerCzech Republic200000Monthly200000
Developer, full-stack;Engineering manager;Project managerCzech Republic200000Monthly200000
Developer, back-endCzech Republic200000Monthly200000
Developer, front-endCzech Republic185000Monthly185000
Developer, back-end;DevOps specialist;System administratorCzech Republic180000Monthly180000
Developer, back-end;Developer, QA or test;Database administrator;DevOps specialistCzech Republic180000Monthly180000
Developer, full-stack;Developer, desktop or enterprise applications;Engineering manager;Other (please specify):;Project managerCzech Republic180000Monthly180000
Developer, back-endCzech Republic175000Monthly175000
Developer, back-endCzech Republic175000Monthly175000
Developer, back-end;BlockchainCzech Republic169400Monthly169400
Developer, full-stack;Developer, back-end;Developer, QA or test;DevOps specialistCzech Republic168000Monthly168000
Academic researcher;ScientistCzech Republic2000000Yearly166700
Developer, back-endCzech Republic2000000Yearly166700
Developer, front-end;Developer, full-stack;Developer, back-end;Engineering managerCzech Republic160000Monthly160000
Developer, full-stack;DevOps specialist;Cloud infrastructure engineer;System administratorCzech Republic1920000Yearly160000
Developer, front-endCzech Republic1864000Yearly155300
Developer, front-end;Developer, desktop or enterprise applicationsCzech Republic155000Monthly155000
Developer, full-stack;Engineering managerCzech Republic154000Monthly154000
Developer, front-end;Developer, full-stackCzech Republic150000Monthly150000
Project manager;Designer;Product managerCzech Republic150000Monthly150000
Developer, back-end;Developer, desktop or enterprise applications;Educator;Cloud infrastructure engineerCzech Republic150000Monthly150000
Developer, back-endCzech Republic150000Monthly150000
Developer, back-end;Cloud infrastructure engineerCzech Republic1800000Yearly150000
Engineer, site reliability;Developer, full-stack;DevOps specialist;Cloud infrastructure engineer;System administratorCzech Republic150000Monthly150000
Developer, full-stack;DevOps specialistCzech Republic150000Monthly150000
Developer, full-stack;DevOps specialist;Data or business analystCzech Republic1800000Yearly150000
Developer, front-end;Developer, full-stack;Developer, back-end;Educator;Database administrator;Developer, game or graphics;Cloud infrastructure engineer;Designer;System administratorCzech Republic1764000Yearly147000
Developer, mobileCzech Republic145000Monthly145000
Developer, back-endCzech Republic1700000Yearly141700
Developer, back-endCzech Republic1700000Yearly141700
Developer, back-endCzech Republic1700000Yearly141700
Developer, full-stack;Developer, back-endCzech Republic140000Monthly140000
Developer, front-endCzech Republic140000Monthly140000
Data scientist or machine learning specialist;Developer, back-endCzech Republic140000Monthly140000
Developer, front-endCzech Republic140000Monthly140000
Developer, front-end;Developer, back-end;Developer, desktop or enterprise applications;Data or business analystCzech Republic140000Monthly140000
Developer, full-stackCzech Republic140000Monthly140000
Developer, back-endCzech Republic135000Monthly135000
Data scientist or machine learning specialist;Developer, back-endCzech Republic135000Monthly135000
Developer, back-end;Developer, desktop or enterprise applicationsCzech Republic135000Monthly135000
Developer, mobile;Engineering managerCzech Republic1600000Yearly133300
Developer, back-end;EducatorCzech Republic130000Monthly130000
Developer, back-end;DevOps specialistCzech Republic130000Monthly130000
Developer, back-endCzech Republic130000Monthly130000
Developer, front-end;Developer, full-stack;Developer, back-end;DevOps specialistCzech Republic130000Monthly130000
Data scientist or machine learning specialist;Developer, full-stack;Database administrator;DevOps specialist;Project managerCzech Republic130000Monthly130000
Developer, front-end;Developer, full-stackCzech Republic130000Monthly130000
Developer, full-stack;Developer, back-endCzech Republic130000Monthly130000
Developer, full-stackCzech Republic130000Monthly130000
Developer, back-end;Developer, desktop or enterprise applications;Educator;DevOps specialist;Cloud infrastructure engineer;System administratorCzech Republic130000Monthly130000
Developer, full-stack;Developer, mobileCzech Republic130000Monthly130000
Developer, full-stack;Developer, back-end;Engineering manager;DevOps specialistCzech Republic130000Monthly130000
Developer, back-endCzech Republic128000Monthly128000
Developer, front-end;Developer, full-stack;Developer, back-endCzech Republic127000Monthly127000
Developer, front-end;Developer, desktop or enterprise applicationsCzech Republic1520000Yearly126700
Developer, full-stack;Project managerCzech Republic1500000Yearly125000
Developer, back-endCzech Republic125000Monthly125000
Developer, back-endCzech Republic1500000Yearly125000
Database administrator;Data or business analystCzech Republic1500000Yearly125000
Developer, front-end;Developer, QA or testCzech Republic125000Monthly125000
Developer, front-endCzech Republic124800Monthly124800
Developer, front-endCzech Republic122300Monthly122300
Developer, back-end;Developer, desktop or enterprise applicationsCzech Republic120000Monthly120000
Engineer, site reliability;Developer, back-endCzech Republic120000Monthly120000
Developer, full-stack;Developer, desktop or enterprise applications;Academic researcherCzech Republic120000Monthly120000
Developer, back-endCzech Republic120000Monthly120000
Developer, front-endCzech Republic120000Monthly120000
Developer, back-end;Developer, desktop or enterprise applications;Engineering managerCzech Republic120000Monthly120000
Developer, back-endCzech Republic120000Monthly120000
Developer, back-end;Engineering manager;Cloud infrastructure engineer;Senior Executive (C-Suite, VP, etc.)Czech Republic120000Monthly120000
Developer, front-endCzech Republic120000Monthly120000
Developer, front-end;Developer, full-stack;Developer, back-end;Developer, desktop or enterprise applicationsCzech Republic1440000Yearly120000
Developer, full-stack;Developer, back-end;Project managerCzech Republic120000Monthly120000
Other (please specify):Czech Republic1400000Yearly116700
Developer, mobileCzech Republic116000Monthly116000
Developer, mobile;Engineering manager;Project manager;Product managerCzech Republic115000Monthly115000
Developer, embedded applications or devicesCzech Republic115000Monthly115000
DevOps specialist;Cloud infrastructure engineerCzech Republic110000Monthly110000
Developer, front-endCzech Republic110000Monthly110000
Developer, back-endCzech Republic110000Monthly110000
Developer, full-stack;Database administrator;DevOps specialistCzech Republic110000Monthly110000
Developer, mobileCzech Republic1320000Yearly110000
Developer, back-end;Developer, desktop or enterprise applications;DevOps specialistCzech Republic1300000Yearly108300
Developer, back-end;DevOps specialistCzech Republic1300000Yearly108300
Developer, back-end;Developer, desktop or enterprise applicationsCzech Republic105000Monthly105000
Developer, mobileCzech Republic105000Monthly105000
Developer, front-end;Engineer, data;Engineer, site reliability;Developer, full-stack;Developer, back-end;Developer, QA or test;Database administrator;DevOps specialist;Cloud infrastructure engineer;Designer;System administrator;Security professionalCzech Republic1250000Yearly104200
Developer, back-end;Developer, desktop or enterprise applicationsCzech Republic104000Monthly104000
Developer, front-end;Developer, full-stack;Developer, back-end;DevOps specialist;Cloud infrastructure engineerCzech Republic103000Monthly103000
Developer, full-stackCzech Republic100000Monthly100000
Developer, front-end;Developer, full-stack;Data or business analystCzech Republic100000Monthly100000
Developer, front-end;Developer, full-stack;Developer, back-end;Developer, desktop or enterprise applicationsCzech Republic100000Monthly100000
Developer, full-stack;Developer, back-endCzech Republic100000Monthly100000
Engineer, data;Developer, back-endCzech Republic100000Monthly100000
Developer, back-endCzech Republic100000Monthly100000
Developer, back-end;Database administrator;Cloud infrastructure engineerCzech Republic1200000Yearly100000
Developer, front-end;Developer, mobile;DesignerCzech Republic100000Monthly100000
Engineer, data;Developer, full-stack;Developer, back-end;Developer, desktop or enterprise applications;Developer, mobile;Developer, embedded applications or devices;Project managerCzech Republic100000Monthly100000
Developer, full-stack;Developer, mobileCzech Republic100000Monthly100000
Developer, front-end;Developer, full-stackCzech Republic100000Monthly100000
Developer, back-endCzech Republic100000Monthly100000
Developer, full-stack;Developer, mobileCzech Republic100000Monthly100000
Developer, front-end;Developer, full-stack;Developer, back-end;Developer, mobile;Database administrator;Project manager;Designer;System administratorCzech Republic100000Monthly100000
Developer, back-endCzech Republic100000Monthly100000
Developer, full-stackCzech Republic100000Monthly100000
Developer, front-end;Developer, full-stack;Developer, back-end;Developer, QA or test;Database administrator;DevOps specialist;Cloud infrastructure engineer;Designer;System administrator;Security professionalCzech Republic1200000Yearly100000
Developer, full-stack;Developer, back-end;Database administratorCzech Republic100000Monthly100000
Developer, front-end;Developer, back-endCzech Republic100000Monthly100000
Developer, front-end;Developer, full-stack;Developer, back-end;Developer, desktop or enterprise applicationsCzech Republic100000Monthly100000
Data scientist or machine learning specialistCzech Republic100000Monthly100000
Developer, full-stack;Developer, back-endCzech Republic100000Monthly100000
Developer, full-stack;Developer, mobileCzech Republic100000Monthly100000
Developer, mobileCzech Republic98000Monthly98000
ScientistCzech Republic96000Monthly96000
Developer, front-endCzech Republic96000Monthly96000
Developer, back-endCzech Republic95800Monthly95800
Project managerCzech Republic95000Monthly95000
Developer, back-end;DevOps specialistCzech Republic95000Monthly95000
Developer, mobileCzech Republic94500Monthly94500
Developer, front-end;Developer, full-stack;Developer, back-endCzech Republic92000Monthly92000
Developer, back-endCzech Republic1100000Yearly91700
Developer, QA or testCzech Republic90000Monthly90000
Developer, back-endCzech Republic90000Monthly90000
Product managerCzech Republic90000Monthly90000
Developer, full-stackCzech Republic90000Monthly90000
Developer, front-end;Engineer, data;Developer, full-stack;Developer, back-end;Database administrator;Data or business analystCzech Republic90000Monthly90000
Developer, back-end;Student;Developer, mobileCzech Republic90000Monthly90000
Developer, desktop or enterprise applicationsCzech Republic90000Monthly90000
Developer, back-end;Developer, QA or test;Developer, mobile;Database administrator;DevOps specialist;Cloud infrastructure engineer;System administratorCzech Republic90000Monthly90000
Developer, back-endCzech Republic90000Monthly90000
Developer, full-stackCzech Republic90000Monthly90000
Developer, front-end;Developer, full-stack;Developer, back-end;Developer, mobile;Engineering managerCzech Republic90000Monthly90000
Engineering manager;Senior Executive (C-Suite, VP, etc.)Czech Republic90000Monthly90000
Developer, front-end;Developer, full-stack;Developer, back-end;Developer, mobileCzech Republic90000Monthly90000
Developer, desktop or enterprise applicationsCzech Republic89000Monthly89000
Developer, full-stack;Security professionalCzech Republic85000Monthly85000
Developer, back-endCzech Republic85000Monthly85000
Developer, QA or testCzech Republic85000Monthly85000
Developer, back-endCzech Republic85000Monthly85000
Developer, front-endCzech Republic84000Monthly84000
Developer, front-end;Developer, full-stack;Developer, back-endCzech Republic1000000Yearly83300
Developer, front-end;Developer, back-endCzech Republic1000000Yearly83300
Developer, back-end;Developer, desktop or enterprise applications;System administratorCzech Republic989000Yearly82400
DevOps specialist;System administratorCzech Republic82177Monthly82177
Developer, front-end;Developer, full-stack;Developer, desktop or enterprise applicationsCzech Republic82100Monthly82100
Developer, back-endCzech Republic82000Monthly82000
Developer, back-end;DevOps specialist;System administratorCzech Republic980000Yearly81700
Developer, mobileCzech Republic81000Monthly81000
Developer, front-end;Developer, full-stack;Developer, back-endCzech Republic80000Monthly80000
Developer, back-end;DevOps specialistCzech Republic80000Monthly80000
Developer, back-endCzech Republic80000Monthly80000
Other (please specify):;Security professionalCzech Republic80000Monthly80000
Developer, front-end;Developer, mobileCzech Republic80000Monthly80000
Developer, embedded applications or devicesCzech Republic80000Monthly80000
Developer, front-end;EducatorCzech Republic80000Monthly80000
Developer, back-end;Cloud infrastructure engineerCzech Republic80000Monthly80000
Developer, full-stack;DevOps specialistCzech Republic80000Monthly80000
Developer, front-endCzech Republic80000Monthly80000
Developer, full-stackCzech Republic80000Monthly80000
Developer, front-end;Developer, back-end;Database administrator;DevOps specialist;Cloud infrastructure engineer;Data or business analyst;System administrator;Security professionalCzech Republic80000Monthly80000
Developer, front-end;Developer, full-stack;Developer, back-end;DevOps specialistCzech Republic80000Monthly80000
Developer, front-end;Developer, full-stackCzech Republic80000Monthly80000
Developer, back-end;DevOps specialistCzech Republic80000Monthly80000
Developer, back-endCzech Republic80000Monthly80000
Other (please specify):Czech Republic80000Monthly80000
Developer, full-stackCzech Republic80000Monthly80000
Developer, back-endCzech Republic80000Monthly80000
Developer, back-endCzech Republic80000Monthly80000
Developer, full-stackCzech Republic80000Monthly80000
Engineer, data;Database administratorCzech Republic80000Monthly80000
Engineer, site reliability;Developer, QA or test;DevOps specialist;Cloud infrastructure engineerCzech Republic80000Monthly80000
Developer, full-stack;Developer, back-endCzech Republic80000Monthly80000
Developer, desktop or enterprise applications;Developer, QA or test;Educator;DevOps specialist;System administratorCzech Republic80000Monthly80000
Developer, full-stack;Developer, back-end;Developer, embedded applications or devicesCzech Republic950000Yearly79200
Developer, front-endCzech Republic950000Yearly79200
Developer, back-endCzech Republic77000Monthly77000
Developer, front-end;Engineer, site reliability;Developer, full-stack;Developer, back-end;Developer, desktop or enterprise applications;DevOps specialist;System administratorCzech Republic76000Monthly76000
Developer, full-stack;Developer, back-endCzech Republic75000Monthly75000
Developer, front-end;Developer, full-stack;DesignerCzech Republic75000Monthly75000
Developer, front-end;Developer, full-stack;Developer, back-end;Developer, desktop or enterprise applicationsCzech Republic75000Monthly75000
Data or business analystCzech Republic900000Yearly75000
Developer, embedded applications or devices;Security professionalCzech Republic75000Monthly75000
Developer, QA or testCzech Republic73000Monthly73000
Developer, full-stackCzech Republic72000Monthly72000
Developer, back-end;Developer, desktop or enterprise applicationsCzech Republic70000Monthly70000
DevOps specialistCzech Republic70000Monthly70000
Developer, desktop or enterprise applications;Developer, embedded applications or devicesCzech Republic70000Monthly70000
Developer, full-stack;Developer, desktop or enterprise applicationsCzech Republic70000Monthly70000
Developer, back-end;Developer, embedded applications or devicesCzech Republic70000Monthly70000
Developer, full-stackCzech Republic70000Monthly70000
Developer, front-end;Developer, full-stack;Developer, back-end;Cloud infrastructure engineerCzech Republic70000Monthly70000
Developer, back-end;Database administrator;Data or business analystCzech Republic70000Monthly70000
Developer, front-end;Developer, full-stack;Developer, back-end;Developer, desktop or enterprise applications;Database administrator;Data or business analyst;Designer;System administratorCzech Republic840000Yearly70000
Developer, full-stack;Developer, back-end;Developer, desktop or enterprise applications;Database administratorCzech Republic70000Monthly70000
Developer, front-end;Developer, full-stack;Developer, back-end;Developer, desktop or enterprise applicationsCzech Republic70000Monthly70000
Developer, back-end;Developer, mobileCzech Republic70000Monthly70000
Developer, embedded applications or devicesCzech Republic70000Monthly70000
Developer, back-end;Database administrator;Project manager;System administratorCzech Republic70000Monthly70000
Developer, back-end;DevOps specialist;Project manager;System administratorCzech Republic70000Monthly70000
Developer, full-stack;Developer, back-end;System administratorCzech Republic70000Monthly70000
Developer, back-end;DevOps specialist;Project manager;Cloud infrastructure engineer;Product managerCzech Republic70000Monthly70000
Developer, back-end;Developer, mobile;Academic researcher;BlockchainCzech Republic68610Monthly68610
Developer, desktop or enterprise applicationsCzech Republic68000Monthly68000
Developer, back-endCzech Republic68000Monthly68000
Developer, back-end;DevOps specialistCzech Republic67000Monthly67000
Developer, full-stack;Educator;Other (please specify):Czech Republic67000Monthly67000
Developer, embedded applications or devices;Security professionalCzech Republic800000Yearly66700
Developer, back-endCzech Republic800000Yearly66700
Data scientist or machine learning specialist;Developer, front-end;Developer, full-stack;Developer, back-end;Developer, QA or test;DevOps specialistCzech Republic66500Monthly66500
Developer, back-end;Developer, desktop or enterprise applicationsCzech Republic66500Monthly66500
Developer, front-end;Developer, back-end;Developer, desktop or enterprise applicationsCzech Republic65000Monthly65000
Academic researcher;DesignerCzech Republic65000Monthly65000
Developer, back-endCzech Republic65000Monthly65000
Developer, front-end;Developer, full-stack;Developer, back-end;DevOps specialist;Cloud infrastructure engineerCzech Republic65000Monthly65000
Data scientist or machine learning specialist;Developer, embedded applications or devicesCzech Republic65000Monthly65000
Developer, front-end;Project manager;DesignerCzech Republic64000Monthly64000
Engineering managerCzech Republic63000Monthly63000
Developer, full-stackCzech Republic63000Monthly63000
Developer, full-stackCzech Republic63000Monthly63000
Developer, full-stack;Developer, desktop or enterprise applications;Developer, mobile;Database administratorCzech Republic750000Yearly62500
Developer, front-end;Engineer, data;Developer, full-stack;Developer, back-end;Developer, desktop or enterprise applications;Developer, QA or test;Developer, mobile;Database administrator;Data or business analyst;DesignerCzech Republic750000Yearly62500
Developer, back-end;Developer, desktop or enterprise applications;Developer, embedded applications or devicesCzech Republic60000Monthly60000
Developer, full-stack;DevOps specialistCzech Republic60000Monthly60000
Developer, desktop or enterprise applications;Developer, embedded applications or devices;Security professionalCzech Republic60000Monthly60000
Developer, back-endCzech Republic60000Monthly60000
Developer, back-end;Developer, desktop or enterprise applicationsCzech Republic60000Monthly60000
Developer, back-endCzech Republic60000Monthly60000
Engineer, data;Developer, back-end;System administratorCzech Republic60000Monthly60000
Developer, full-stack;Developer, back-end;Developer, desktop or enterprise applicationsCzech Republic60000Monthly60000
Developer, back-end;Developer, desktop or enterprise applications;System administrator;Security professionalCzech Republic60000Monthly60000
Developer, full-stack;Developer, back-end;Student;Educator;Database administrator;Designer;System administratorCzech Republic60000Monthly60000
Developer, back-end;Developer, desktop or enterprise applications;DevOps specialistCzech Republic60000Monthly60000
Developer, front-end;Developer, mobile;Developer, game or graphicsCzech Republic60000Monthly60000
Developer, back-endCzech Republic60000Monthly60000
Data scientist or machine learning specialist;Database administrator;Academic researcher;Data or business analystCzech Republic60000Monthly60000
Developer, front-end;Developer, full-stack;Developer, back-endCzech Republic60000Monthly60000
Academic researcherCzech Republic60000Monthly60000
Developer, front-end;Engineer, site reliability;Developer, full-stackCzech Republic57000Monthly57000
Developer, full-stack;Developer, back-end;Database administratorCzech Republic56000Monthly56000
Developer, back-end;Developer, desktop or enterprise applications;Developer, QA or test;Database administratorCzech Republic55000Monthly55000
Developer, back-end;Developer, desktop or enterprise applications;Database administrator;Developer, embedded applications or devicesCzech Republic55000Monthly55000
Data scientist or machine learning specialistCzech Republic55000Monthly55000
Developer, full-stackCzech Republic55000Monthly55000
Developer, full-stack;Developer, back-end;Developer, desktop or enterprise applications;Database administratorCzech Republic55000Monthly55000
Developer, desktop or enterprise applicationsCzech Republic55000Monthly55000
Developer, back-endCzech Republic54000Monthly54000
Developer, full-stack;Developer, back-endCzech Republic53000Monthly53000
Developer, back-end;Educator;Academic researcher;Developer, game or graphics;Scientist;System administratorCzech Republic52000Monthly52000
Academic researcherCzech Republic50000Monthly50000
Developer, back-endCzech Republic50000Monthly50000
Developer, full-stack;Developer, back-end;Cloud infrastructure engineerCzech Republic50000Monthly50000
Developer, game or graphicsCzech Republic50000Monthly50000
Developer, full-stack;DevOps specialist;System administratorCzech Republic50000Monthly50000
Academic researcherCzech Republic50000Monthly50000
Developer, front-end;Developer, full-stack;Developer, back-end;Developer, mobile;DesignerCzech Republic50000Monthly50000
Developer, desktop or enterprise applications;Developer, embedded applications or devicesCzech Republic50000Monthly50000
Developer, front-end;Developer, full-stack;Developer, back-end;Developer, desktop or enterprise applications;Developer, embedded applications or devices;Cloud infrastructure engineerCzech Republic50000Monthly50000
Developer, front-endCzech Republic50000Monthly50000
Developer, front-end;Developer, full-stack;Developer, back-end;Developer, desktop or enterprise applicationsCzech Republic50000Monthly50000
Developer, front-end;Developer, full-stack;Developer, back-endCzech Republic50000Monthly50000
Developer, front-end;Developer, back-end;EducatorCzech Republic50000Monthly50000
Developer, full-stackCzech Republic50000Monthly50000
Developer, front-end;Developer, full-stack;Developer, back-end;Developer, embedded applications or devicesCzech Republic50000Monthly50000
Developer, front-endCzech Republic50000Monthly50000
Data scientist or machine learning specialist;Academic researcherCzech Republic50000Monthly50000
Academic researcherCzech Republic50000Monthly50000
Developer, back-end;StudentCzech Republic50000Monthly50000
Developer, front-end;Developer, full-stack;Developer, back-end;Developer, desktop or enterprise applicationsCzech Republic50000Monthly50000
Developer, back-end;Developer, embedded applications or devicesCzech Republic50000Monthly50000
Database administrator;System administratorCzech Republic50000Monthly50000
Developer, back-endCzech Republic48500Monthly48500
Developer, full-stackCzech Republic48000Monthly48000
Academic researcher;ScientistCzech Republic48000Monthly48000
Data scientist or machine learning specialist;Developer, full-stackCzech Republic48000Monthly48000
Developer, back-end;Developer, desktop or enterprise applicationsCzech Republic47000Monthly47000
Developer, full-stackCzech Republic47000Monthly47000
Developer, front-endCzech Republic45000Monthly45000
Developer, back-endCzech Republic45000Monthly45000
Developer, front-end;Developer, full-stack;Developer, QA or test;Database administratorCzech Republic45000Monthly45000
Developer, full-stackCzech Republic45000Monthly45000
Developer, back-end;Security professionalCzech Republic45000Monthly45000
Developer, back-end;DevOps specialistCzech Republic45000Monthly45000
Developer, back-endCzech Republic45000Monthly45000
Developer, full-stack;Developer, desktop or enterprise applications;Developer, mobile;Educator;Developer, embedded applications or devicesCzech Republic44000Monthly44000
Developer, full-stack;Developer, mobileCzech Republic43000Monthly43000
Developer, desktop or enterprise applications;DevOps specialistCzech Republic43000Monthly43000
Developer, front-end;Developer, full-stack;Developer, back-end;Cloud infrastructure engineer;DesignerCzech Republic42500Monthly42500
Developer, full-stack;Developer, back-end;System administratorCzech Republic42000Monthly42000
Developer, front-end;Developer, full-stack;Developer, back-end;Developer, mobileCzech Republic42000Monthly42000
Developer, front-end;Developer, desktop or enterprise applications;Developer, mobile;EducatorCzech Republic40000Monthly40000
Data scientist or machine learning specialist;Developer, front-end;Developer, full-stack;Developer, back-end;Developer, desktop or enterprise applications;Developer, mobile;Engineering manager;Database administrator;DevOps specialist;Project manager;Data or business analyst;Product manager;Senior Executive (C-Suite, VP, etc.);System administrator;Blockchain;Marketing or sales professionalCzech Republic40000Monthly40000
Developer, back-end;StudentCzech Republic40000Monthly40000
Developer, mobile;Developer, game or graphicsCzech Republic40000Monthly40000
Developer, back-end;Developer, desktop or enterprise applications;DevOps specialist;Developer, embedded applications or devicesCzech Republic40000Monthly40000
Developer, front-end;Developer, full-stack;Developer, back-end;Developer, desktop or enterprise applications;System administratorCzech Republic40000Monthly40000
Developer, front-endCzech Republic40000Monthly40000
Developer, front-end;Developer, mobileCzech Republic40000Monthly40000
Developer, front-end;Developer, full-stack;Developer, back-endCzech Republic39000Monthly39000
Developer, full-stackCzech Republic37000Monthly37000
Developer, full-stackCzech Republic37000Monthly37000
Developer, full-stack;Database administrator;Project manager;Designer;System administratorCzech Republic444000Yearly37000
Developer, desktop or enterprise applicationsCzech Republic36000Monthly36000
Developer, embedded applications or devices;System administrator;Security professionalCzech Republic35000Monthly35000
Developer, front-end;Engineer, site reliability;Developer, full-stack;Developer, back-end;Developer, QA or test;Database administrator;DevOps specialist;Cloud infrastructure engineer;System administratorCzech Republic35000Monthly35000
Developer, front-end;Engineer, site reliability;Developer, full-stack;Developer, back-end;Developer, desktop or enterprise applications;Database administrator;DevOps specialist;Cloud infrastructure engineer;System administrator;Security professionalCzech Republic35000Monthly35000
Developer, full-stackCzech Republic35000Monthly35000
Developer, full-stackCzech Republic33000Monthly33000
Engineer, data;Developer, back-end;StudentCzech Republic33000Monthly33000
Developer, back-endCzech Republic30000Monthly30000
Developer, back-endCzech Republic30000Monthly30000
Developer, full-stack;Developer, back-endCzech Republic30000Monthly30000
Developer, front-end;Developer, full-stackCzech Republic30000Monthly30000
EducatorCzech Republic30000Monthly30000
Developer, full-stack;Cloud infrastructure engineer;System administratorCzech Republic30000Monthly30000
Developer, full-stack;Developer, back-endCzech Republic27500Weekly27500
Developer, mobileCzech Republic25000Weekly25000
Developer, front-endCzech Republic21000Monthly21000
Developer, back-end;Engineering manager;Senior Executive (C-Suite, VP, etc.)Czech Republic21000Monthly21000
Developer, front-end;Engineer, site reliability;Developer, full-stack;Developer, back-end;DevOps specialistCzech Republic180000Yearly15000
Developer, front-end;Developer, full-stack;Developer, back-end;System administratorCzech Republic15000Monthly15000
Developer, back-end;DevOps specialist;Developer, embedded applications or devices;Cloud infrastructure engineerCzech Republic180000Yearly15000
Data scientist or machine learning specialist;Engineer, data;DevOps specialist;Data or business analyst;System administratorCzech Republic130000Yearly10800
Developer, full-stack;DevOps specialistCzech Republic120000Yearly10000
Developer, front-end;Developer, full-stack;Developer, back-end;BlockchainCzech Republic10000Monthly10000
Developer, back-end;Cloud infrastructure engineerCzech Republic110000Yearly9200
Developer, front-endCzech Republic110000Yearly9200
Developer, desktop or enterprise applicationsCzech Republic105000Yearly8800
Engineer, site reliability;Developer, back-end;Database administrator;DevOps specialistCzech Republic105000Yearly8800
Developer, front-endCzech Republic90000Yearly7500
Developer, front-end;Developer, full-stack;Developer, back-end;Database administrator;System administratorCzech Republic72000Yearly6000
Developer, full-stackCzech Republic50000Yearly4200
Developer, mobileCzech Republic4000Monthly4000
Developer, full-stack;Developer, back-endCzech Republic4000Monthly4000
Developer, front-end;Developer, full-stackCzech Republic3960Monthly3960
Developer, front-endCzech Republic30000Yearly2500
Developer, front-end;Developer, full-stack;Developer, back-endCzech Republic2400Monthly2400
Developer, front-end;StudentCzech Republic300Monthly300
Engineer, data;Developer, back-end;Senior Executive (C-Suite, VP, etc.)Czech Republic2292Yearly200
Developer, front-end;Engineer, site reliability;Developer, full-stack;Developer, desktop or enterprise applications;Developer, QA or test;Developer, mobile;Project manager;Product manager;System administrator;Marketing or sales professionalCzech Republic150Monthly150
Developer, front-end;Developer, full-stack;Developer, back-end;Developer, QA or test;Engineering manager;Project manager;Designer;Product manager;Senior Executive (C-Suite, VP, etc.);System administrator;Marketing or sales professionalCzech Republic0Monthly0

Jaké technologie v IT vydělávají nejvíc? Celosvětové statistiky.

Statistika je rozdělena na 7 seznamů:

  • Programovací, skriptovací a markup jazyky
  • Databáze a databázové systémy
  • Platformy
  • Webové frameworky
  • Jiné frameworky, knihovny
  • Jiné nezařazené technologie
  • Vývojářské nástroje

U každé technologie je uvedený medián roční mzdy. V tabulkách níže jsem tento medián převedl na české koruny a vydělil 12 pro výpočet průměrné měsíční mzdy. Od skutečného průměru se budou uvedené hodnoty níže nejspíš trochu lišit ale ne o moc.

Nezapomeň na to, že toto jsou celosvětové statistiky, nikoliv statistiky v ČR. Různé technologie jsou různě populární/nepopulární v různých demografiích a některé technologie jsou populární/nepopulární stejně úplně všude. Uvedené částky tedy spíše prezentují popularitu na trhu práce než skutečné hodnoty na výplatních páskách.

Programovací jazyky

Programovací jazykPrůměrná měsíční mzda v Kč
Clojure225000
Erlang217000
F#201000
LISP200000
Ruby196000
Elixir196000
Scala196000
Perl190000
Go188000
Rust184000
OCaml183000
Groovy180000
Crystal179000
Objective-C175000
Bash/Shell172000
Haskell169000
Fortran169000
Lua168000
Swift165000
PowerShell165000
Julia164000
APL160000
COBOL159000
Assembly158000
Python150000
Solidity148000
TypeScript148000
C#147000
Kotlin146000
SQL146000
C++143000
R143000
C142000
JavaScript138000
Java136000
SAS135000
Delphi135000
HTML/CSS135000
VBA131000
MATLAB121000
PHP106000
Dart92000


Ostatní frameworky

Ostatní frameworkyPrůměrná měsíční mzda v Kč
Apache Spark172000
Apache Kafka163000
Hadoop149000
Tidyverse146000
.NET139000
Hugging Face Transformers139000
Uno Platform138000
Pandas132000
NumPy130000
Torch/PyTorch130000
Spring130000
Electron125000
Qt125000
Scikit-learn124000
Xamarin124000
GTK122000
TensorFlow118000
Keras107000
React Native106000
Capacitor104000
Cordova96000
Ionic96000
Flutter84000

Databázové systémy

DatabázePrůměrná měsíční mzda v Kč
DynamoDB185000
Couchbase176000
Cassandra159000
Neo4j157000
Elasticsearch155000
Redis150000
IBM DB2147000
CouchDB141000
PostgreSQL138000
Microsoft SQL Server136000
SQLite125000
Oracle117000
MongoDB113000
Cloud Firestore110000
MariaDB109000
MySQL109000
Firebase Realtime Database84000


Vývojářská prostředí

Vývojářské prostředíPrůměrná měsíční mzda v Kč
TextMate183000
Emacs182000
RubyMine171000
GoLand163000
Neovim157000
Vim157000
Rider152000
CLion148000
Xcode138000
IntelliJ134000
IPython/Jupyter132000
RStudio131000
Visual Studio Code128000
Visual Studio127000
PyCharm125000
RAD Studio (Delphi C++ Builder)125000
Webstorm123000
Notepad++121000
Sublime Text119000
Nano118000
Qt Creator113000
Atom109000
Eclipse101000
Android Studio98000
PhpStorm98000
Spyder88000
NetBeans63000

Platformy

PlatformaPrůměrná měsíční mzda v Kč
Vlastní hardware ve sdílených prostorech a datacentrech218000
AWS159000
IBM Cloud / Watson148000
Linode146000
Microsoft Azure146000
OpenStack146000
Google Cloud143000
DigitalOcean131000
VMware127000
Spravovaný externí hosting120000
Oracle Cloud Infrastructure111000
Heroku104000
OVH104000
Firebase91000


Webové frameworky

Webový frameworkPrůměrná měsíční mzda v Kč
Phoenix185000
Ruby on Rails176000
Play Framework174000
Deno153000
Gatsby153000
Svelte139000
ASP.NET Core139000
Blazor138000
React.js136000
FastAPI136000
Drupal134000
Flask131000
ASP.NET129000
Node.js127000
Angular.js125000
Next.js123000
Fastify121000
Angular119000
Vue.js118000
Django118000
Express117000
jQuery112000
Symfony103000
Nuxt.js100000
Laravel75000


Ostatní nástroje

Ostatní nástrojePrůměrná měsíční mzda v Kč
Chef235000
Pulumi218000
Terraform197000
Puppet188000
Homebrew172000
Kubernetes167000
Ansible163000
Flow153000
Docker146000
Yarn134000
npm126000
Unreal Engine124000
Unity 3D116000

Příloha: Popisy pozic ze StackOverflow Survey 2022

Název pozice z anketyČeský pojemPopis
Senior executiveHlavní manažer, hlavní vedoucí, generální ředitelVrcholová manažerská/vedoucí pozice v korporátech
Engineering managerTechnický manažerNižší manažer nebo vedoucí pracovník s technologickými znalostmi
Engineer, site reliabilityManažer provozu"Site Reliability Engineer (SRE)" je korporátní pozice, jde o kombinaci DevOpsáka, IT admina, který má na starosti infrastrukturu, hardware i software.
Security professionalSecuriťákVětšinou korporátní pozice. Člověk, který je vyčleněný pouze na IT bezpečnost.
Cloud infrastructure engineerCloudový inženýrDevOpsák, admin nebo architekt specializovaný na cloudovou infrastrukturu
BlockchainBlockchainProgramátor/ajťák specializovaný na blockchainové technologie na kterých je založený Bitcoin, Ethereum atd.
Engineer, dataDatový specialistaToto je obecný popis pro někoho, kdo se specializuje na přímou práci s daty, databázemi, datovou analýzou, data warehousing a podobně.
DevOps specialistDevopsákAjťák, který se zabývá programováním a skriptováním automatizovaných nástrojů, procesů a operací, které jsou součástí životního cyklu jedné nebo více aplikací
Marketing or sales professionalMarkeťákMarketingový pracovník v IT odvětví, nejde podle mě o člověka s IT znalostmi.
Product managerProduktový manažerProdukťáci jsou většinou most mezi ajťáky a marketingem. Mají na starost konkrétní firemní produkty a jejich strategický rozvoj, často ale nemají a nepotřebují technické IT vzdělání.
Data scientist or machine learning specialistDatový analytik nebo specialista přes strojové učení/AIAjťák specializovaný na strojové učení a umělou inteligenci. Jde o obrovský podobor IT odvětví a je zde velký překryv s datovým specialistou.
ScientistVědecVědecký výzkum spojený s IT odvětvím
Data or business analystDatový/business analytikZde se podle mě myslí člověk na korporátní pozici, který se zabývá stanovováním vnitřních procesů s použitím IT nástrojů
Developer, back-endBackenďákProgramátor orientovaný na API nebo aplikace běžící na serverech, nikoliv na uživatelských zařízeních (PC, telefony atd.)
Developer, embedded applications or devicesProgramátor zařízeníZde se myslí programátor, který píše kód, který interaguje s hardwarem napřímo: RaspberriPi, bankomaty, roboti atd.
Developer, desktop or enterprise applicationsProgramátor "tlustých" nebo korporátních aplikací na PCProgramátor, který se věnuje psaní aplikací, které běží přímo na počítačích uživatelů (notebooky, desktopy)
Database administratorDatabázový adminAdmin co se stará o databáze, databázové systémy nebo databázové servery atd.
Project managerProjekťákČlověk, který v IT firmách dělá most mezi zákazníky/zadavately a programátory. Většina z nich nemá specializované IT vzdělání.
Developer, full-stackFullstackProgramátor, který dělá backend i frontend. Tato generalizace pomalu vymírá, ve větších firmách ji už většinou nenajdete.
Developer, QA or testQA/test developer, programátor testůProgramátor, který se věnuje psaní aplikací nebo skriptů, které ověřují, že jiná aplikace funguje jak má.
System administratorAdminAjťák, který se stará o fyzickou infrastrukturu, síťovou konfiguraci, instalace, evidenci zařízení atd.
Developer, game or graphicsGrafický vývojář, herní vývojářProgramátor specializovaný na tvoření her nebo na práci s grafickými procesy
DesignerDesignerČlověk který tvoří grafiku pro IT produkty. Podle mně se zde myslí spíš člověk s uměleckým vzděláním než s IT vzděláním.
EducatorŠkolitelŠkolitel, který školí ostatní ohledně IT produktů nebo IT technologií
Developer, front-endFrontenďákProgramátor zaměřený na dělání aplikací, se kterými přímo interagují koncoví uživatelé
Developer, mobileMobilář, programátor mobilních aplikacíProgramátor mobilních aplikací
Academic researcherAkademický pracovník ve výzkumuČlověk ve výzkumu na nějaké vysoké škole.
StudentStudentTuto možnost zaškrtli studenti, kteří se během studijí živí i v IT.

Architektura (nejen) C# projektu 2

Architektura souběžností

Jak jsem psal v předchozím díle, programátor by neměl používat transakční mechanismus databáze (např. SQL transakce) a za souběžnosti a zamykání dat před validací a zápisem by měl vzít zodpovědnost ve svém kódu napřímo.

Jak to ale udělat? Je vůbec možné vymyslet univerzální architekturu pro zpracování souběžností? Nebo je nutné analyzovat každou doménu zvlášť?

Čtení

V prostředí businessových API aplikací je nutné se na každý kus kódu dívat z pohledu škálovatelnosti a předpokládat, že každý řádek může běžet souběžně na 10 serverech a na každém serveru v 1000 vláknech (10×1000).

Konkrétní hodnoty nejsou důležité. 10×1000 může být v kontextu jedné aplikace zátěžový test, v kontextu jiné aplikace může jít o produkční provoz. O to nejde.

Pokud na API přistál request na čtení dat, je nám úplně jedno, kolik serverů a kolik vláken čtení obsluhuje. Díky tomu je čtení pro nás programátory ta jednodušší část kódu. Nemusíme vytvářet žádné zámky a prakticky jen delegujeme čtecí requesty na náš databázový systém, ať už jde o SQL data nebo NoSQL dokumenty (jsme v eventuální konzistenci).

Zápis

Pokud na API přistál request na zápis dat, je zamykání složitější. V jakém rozsahu se má zamykat? Na to existuje několik možností a všechny jsou validní, záleží na kontextu.

Příklad: mám 2 typy agregátů, Order (objednávka) a Product (produkt).

  1. Plošný zámek pro jakýkoliv zápis: všechny operace pro zápis používají jeden zámek.
    • Jakmile jeden uživatel provede nějakou změnu dat, všechny ostatní requesty musí čekat bez ohledu na to, zdali jde o změnu v objednávce nebo v produktu.
    • Může se zdát, že takovýto režim zamykání je absolutně nesmyslný. Berte ale na vědomí, že implementovat plošný zámek pro zápis je velmi triviální a například u POCů a malých aplikací může dávat smysl.
  2. Zámek nad typem agregátu: všechny operace pro zápis nad konkrétním typem agregátu používají jeden zámek.
    • Toto znamená, že lze souběžně změnit jakýkoliv Order a jakýkoliv Product ale už nelze souběžně změnit 2 různé Ordery.
    • Typ agregátu je z definice nezávislý na ostatních typech tzn. Order je zcela nezávislý na Product.
    • Tento typ zámku nedává dle mého názoru moc velký smysl. Implementačně je složitější a DDD již zaručuje izolaci na úrovni instance agregátu. Proto je lepší typ 3 níže.
  3. Zámek nad instancí agregátu: všechny operace pro zápis nad konkrétní instancí agregátu používají jeden zámek.
    • Toto znamená, že zle souběžně změnit 2 různé Ordery, nezávisle na sobě a na jakémkoliv jiném agregátu, např. na Productu.
    • Není však možné provádět souběžně 2 operace na jednom Orderu, na jedné objednávce.
    • Zamykání nad instancí agregátu lidem často připomíná aktory, v předchozím článku ale pojmenovávám důvody, proč je lepší se aktorům vyhnout.
  4. Detailní manipulace se zámky v instanci agregátu v závislosti na business logice
    • V rámci jedné instance agregátu je možné provádět souběžně doménovou logiku, která provádí zápis dat. Např. první metoda/handler aktualizuje informaci Order.State (stav objednávky) zatímco jiná metoda/handler aktualizuje informaci Order.CustomerNote (poznámka zákazníka). Z businessové logiky to dává smysl, není třeba zamykat stav objednávky, pokud dochází k aktualizaci poznámky zákazníka.
    • Implementace na takto podrobné úrovni by dle mého názoru měla probíhat pouze na základě reálného optimalizačního požadavku. Jinými slovy, pokud v provozu existují reálné problémy s tím, že se uživatelům nedaří něco změnit.
      • Většinu času by toto neměl být problém, protože víme, že u většiny businessů je ~80% operací čtení a pouze ~20% operací je zápis, rozložený napříč všemy instancemi agregátů.
      • POZOR: Pokud nad jednou instancí agregátu potřebuje pracovat v jednom čase velké množství lidí, pak je možné, že jsme blbě navrhli doménu. Pokud vznikne objednávka a s touto objednávkou – konkrétní instancí agregátu Order – potřebují pracovat zpracovatel objednávek, skladník, účetní, dispečer atd. pak je zjevné, že naše doména není úplně dobře vymyšlená. (Zpracovatel by měl pracovat se stavem objednávky, skladník se skladovými zásobami/výdejkami, účetní s doklady, dispečer s dopravci, helpdesk se zákazníkem atd.)

„Distribuované zámky“

V doménové architektuře je doménová logika rozsekána mezi jednotlivé agregáty. User, Product, atd. Každý agregát obsahuje metody/handlery, které obsluhují jednotlivé commandy/requesty.

Dle mého názoru je se vyplatí už od začátku implementovat zámky nad konkrétní instancí agregátu (v předchozí kapitole bod 3.). Tyto zámky musí být implementovány jako distribuované zámky. Jen pozor, že velmi často pojem „distribuovaný zámek“ (v angličtině „distributed lock“) může znamenat jednu z následujících věcí:

  • Zámek, který je používán distribuovanou aplikací (= o čem píšu)
  • Zámek, který je distribuován na více serverech/instancích (např. redis skrz redlock algoritmus)

Pochopitelně zámek používáný distribuovanou API může a nemusí být sám distribuovaný (běžet na více serverech a instancích).

Zámek v agregátu a v ságách

V DDD je každá změna vyvolaná nějakým konkrétním commandem/requestem a tato změna je zpracována konkrétní metodou agregátu nebo handlerem. V mé architektuře to je MediatRovský handler.

Request handler náležící konkrétnímu agregátu je izolovaný od všeho ostatního a pracuje pouze s daty konkrétní instance agregátu. Před provedením metody musí dojít k uzamčení celého agregátu tzn. vytvoříme zámek s identifikátorem např. Product_123 kde 123 je identifikátor produktu.

V ságách probíhá redistribuce příkazů napříč více agregáty. Už na úrovni agregátu je však velmi často nutné řešit zámky!

Příklad: mějme operaci CreateOrder která vytváří objednávku. Musí se zamknout několik agregátů najednou ještě před tím, než sága začne vytvářet příkazy na jednotlivé handlery.

  • DiscountVoucher – slevový kupón, který je v objednávce použitý
  • ProductItem – skladová zásoba zakoupeného produkt
  • Order – může být potřeba zamknout objednávku samotnou v závislosti na granulitě naší domény. Pokud objednávce stačí jeden command pak ji není třeba zamykat, košaté objekty jako Order mají ale tendenci mít spoustu dalších připojených dat, které se vkládají ne najednou ale až po vytvoření.

Nyní nám ale vzniknul problém. Pokud sága vytváří sama svoje zámky, dostává se do konfliktu s vytvářením zámků přímo v agregátech. Pokud si sága vytvoří zámek nad Order ale pak pro Order zavolá příkaz, v rámci RequestHandleru pro Order dojde k deadlocku.

V naší architektuře potřebujeme vědět, že pokud tvoříme zámek v rámci agregátu, musíme zkontorlovat, jestli ten samý zámek na ten samý klíč není vytvořen už ságou. Pokud ano, nic nezamykáme a volání na redis přeskočíme. Tuto kontrolu stačí provést v rámci requestu.

Architektura (nejen) C# projektu

Řekněme, že stojíte u nového projektu, buď jako fullstack developer nebo jako backenďák programujete nějaké API. Máte naprosto čisté pole působnosti a můžete si to udělat jak chcete.

Že si to můžete udělat jak chcete je ale i nevýhoda. Pokud nemáte dost zkušeností se zvolením špatných architektur, nemáte vůbec tušení, jestli ta, se kterou to zkusíte teď, je ta špatná nebo dobrá.

Každý zkušenější programátor vám řekne, že ať už si zvolíte jakoukoliv architekturu, vždycky se objeví use-case, který vám v této architektuře bude dělat neplechu. A to je také pravda. Ale ještě zkušenější ajťák vám řekne, že zásadní je v kódu nějakým způsobem vyčlěnit doménu a zbytek je jen technologický detail.

Nejdřív bych ale rád pojmenoval ty nejpopulárnější přístupy, podle kterých se IT projekty píšou.

3-vrstvá architektura

V paradigmatu 3-vrstvé architektury se kód odděluje na 3 vrstvy:

  • databázová vrstva
  • doménová/business vrstva
  • frontend/UI vrstva

Databázová vrstva může komunikovat pouze s doménovou a doménová vrstva může komunikovat pouze s frontendovou vrstvou. Snahou tohoto rozdělení je tendence rozdělit kód do oblastí, které spolu co nejvíc souvisí tzn. kód, který souvisí s databází, by měl být v databázové vrstvě, kód který souvisí s doménou by měl být v doménové vrstvě a kód, který souvisí s UI by měl být v UI vrstvě.

Kritika 3-vrstvé architektury: všechno všude, všichni všechno

Kdekoliv, kde jsem byl, se 3 vrstvá architektura zvrhla v píseček, kde si každý programátor staví svůj vlastní hrádeček.

Jeden programátor do týmu vstoupí, jiný vystoupí, jeden chvíli dělá PR reviewera, ten je posléze taky nahrazen jiným a celá 3-vrstvá architektura se tak časem proměňuje v balast různých optimalizací, wrapperů které wrappují wrappované, business kód v databázové vrstvě, databázový kód v UI vrstvě, UI kód v business vrstvě, SQL procedury na 2000 řádků, nějaký „cool“ mechanismus o kterém už nikdo neví jak funguje, hromady ručně psaných „podpůrných nástrojů“, které je nutné pro normální programování používat a vždy nějaký přesložitělý, komplexní generátor datové vrstvy.

Z jednoduché myšlenky 3-vrstvé architektury, která sděluje, že někdy je nutné oddělovat kód podle typu použití, se stalo dogma, kterému všichni z nějakého důvodu věří ale nikdo jej ani nedodržuje. Je to totiž mylná orientace — není důležitá architektura programátorského projektu ale doména projektu (k tomu co je doména se dostanu). Každá 3-vrstvá nebo n-vrstvá architektura se tak postupně rozpadá jako domeček z karet.

Samoúčelná datová abstrakce, kterou nikdo nepotřebuje

Kdekoliv se používá 3-vrstvá architektura tak tam narazíte na nějaký přesložitělý generátor nebo kód obsahující sofistikovaný labyrint tříd a interfejsů určený pro abstrakci datové vrstvy.

  • IDataRepository<TDataModel> where TDataModel : IDataEntity
  • CrudDataSource<TDataSourceSelector, TDataModel> : IDataRepository<IDataSourceSelector, TDataModel> where TDataSourceSelector : IDataRepository<TDataModel> ….UGH!

Tak už aby někdo řekl pravdu.

Abstrakce datové vrstvy je užitečná hlavně kvůli psaní testů. Jakmile jednou zvolíte v projektu datový zdroj, nikdy se nezmění.

Prostě se to neděje. Fakt. Pokud k tomu vzácně někdy dojde u provozované, produkční aplikace, je to stejně vždy spojené se samostatným, relativně velikým projektem proporčně k velikosti projektu. Přechod na jiný datový zdroj u běžící aplikace je spojen s miliardou jiných dalších problémů a musí k tomu být opravdu dobrý důvod. Paradoxně v ten moment zjistíte, že ten váš šílený labyrint abstrakcí na tuto situaci není připravený a při přechodu na jiný datový zdroj se vám to rozpadne.

Přestaňte tedy jako programátoři přemýšlet nad tím, co se stane v momentě, kdy přejdete na jiný mechanismus persistence, protože to se v praxi téměř nikdy neděje.

Abstrakce je potřeba kvůli testům. Ale na ty vám stačí jednoduchý interface, který napíšete ručně a nepotřebujete k němu žádný šílený generátor.

Cibulová architektura, n-vrstvá architektura

Toto je všechno variace na 3-vrstvou architekturu. V cibulové architektuře jde o myšlenku, že různé oblasti kódu na sebe navazují postupně a lze si to tudíž představit jako cibuli.

Což…je naprosto neužitečné a neříká to nic o tom, jakým způsobem bychom měli psát kód. Která vrstva je uprostřed cibule? Která na vrchu? Je uprostřed databázová vrstva? Nebo business vrstva? Co když potřebuji sdílet něco napříč všemi vrstvami? („Ne to ne, na to se prej mračil jeden senior, který tu byl přede mnou“) Kam patří unit test business vrstvy? A co unit test databázové vrstvy? A kam patří definice IoC kontejneru?

Stejně jako u 3-vrstvé architektury, problém spočívá v orientaci na architekturu a nikoliv na doménu.

Relační databáze (RDBMS) musí zemřít (většinou)

3-vrstvá/n-vrstvá/cibulová architektura se používá v aplikacích, kde se jako persistence zvolilo SQL. Dlouho totiž SQLko bylo to jediné známé, jak se s daty nejlépe pracuje. V dnešní době ale už existují mnohem smysluplnější alternativy a nové projekty by tak v roce 2022 vůbec nad SQLkem neměly vznikat.

Kde dávají RDBMS smysl

Pracoval jsem pro jednu firmu, kde používali SQL Server a Oracle Server a zpracovávali geografická data. V této firmě zaměstnávali na full-time databázové specialisty, kteří veškerou aplikační logiku programovali v procedurách, triggerech, packages a dalších možnostech databázových systémů …a já jsem se svým trapným C# byl jen podřadný technik, který měl dělat různé aplikační a architektonické vyfikundace a webové servery s UI, které ti databázisti neuměli udělat. Můj trapný ASP.NET Core 2.1 MVC backend nebylo nic jiného, než wrapper okolo databázových procedur.

U takové firmy dává SQL smysl. Databáze je střed všeho, geografické údaje jsou jedním z primárních orientací relačních databází a hlavním zaměstnancem není programátor ale databázový specialista.

Dále dává SQL smysl u malých, osobních projektů, kde je výborný tooling. Např. malý blog, malý eshop a s Entity Frameworkem dokážu být produktivní velmi rychle. WordPress, na kterém běží tento blog, běží nad MySQL. Nahodím to někam na hosting a mám hotovo. MySQL není pro WordPress ideální řešení, je to ale zaběhnuté, známé řešení, existuje k němu perfektní podpora, tooling a související technologie.

Je to docela úchylné. Pokud používám MySQL/SQL Server pro osobní blog, je to jako kdybych si koupil plně vybavené BMW X7 jen abych s tím jezdil na nákup do Kauflandu a z plné výbavy používal jen plyn, brzdu, volant, blinkr a světla. Jasně, základní věci jako tabulky, primární klíč a indexy to umí. Kristova noho, tahle ďábelská mašina ale umí mnohem, mnohem víc, než si zjevně dovedu představit a nic z toho nevyužiju. V IT to samozřejmě nevadí, protože oproti BMW X7, MySQL/SQL Server (Express) mohu používat ihned a zdarma.

Kde nedávají RDBMS smysl

Všude jinde.

Vážně.

Nepoužívejte SQL pokud ho po vás nikdo vyloženě nechce a nemá k tomu dobrý důvod. Používejte pro data pouze dokumentové databáze jako mongodb. Toto píšu na základě svých zkušeností. Kdekoliv, kam jsem přišel k nějakému většímu projektu a používali nějaké RDBMS (většinou SQL Server v mém případě) tak RDBMS byl zdrojem nikdy nekončících problémů.

Důvod č.1 proč přestat používat SQL: prakticky nerealizovatelná horizontální škálovatelnost

RDBMS lze škálovat horizontálně. Prakticky to ale nikdo nedělá, protože to nikdo neumí, protože je to velmi složité. Musí se to analyzovat dopředu někým, kdo tomu rozumí a kdo už nějaké databázové servery škáloval. Programátoři to většinou nechtějí dělat, byť to jsou většinou oni, kdo díky skvělému existujícímu toolingu rozhodl, že se bude používat SQL.

Důvod č.2 proč přestat používat SQL: vysoká cena

SQL se v praxi škáluje jen vertikálně. Pokud stroj nestíhá, pořídí se silnější stroj, což znamená brutálně vysoké náklady za licence a za provoz na příšerně drahých serverech s 50 jádry a stovkami GB paměti.

Důvod č.3 proč přestat používat SQL: náročná optimalizace

Zkušení programátoři ví co jsou B+ stromy, primární/cizí/unikátní klíče, full text indexy, typy constraintů, umíme execution plány, umíme napsat proceduru i trigger byť to děláme strašně neradi…ale u velkých projektů prostě dřív nebo později narazíme na problém, který nedokážeme vyřešit. Máme všechny indexy správně ale prostě to běží z nějakého důvodu pomalu…a nejspíš to souvisí s nějakou procedurou? Ten samý problém se nám začal dít ještě někde jinde. A na podobném místě to začalo dělat deadlocky. A to jsem myslel, že když sem dám READ COMMITED tak to vyřeším…

Většinou pak my programátoři pácháme na SQL různé zločiny, dokud to nezačne fungovat, protože databáze nepovažujeme za svoji hlavní specializaci a nebaví nás se RDBMS takhle dlouho věnovat. Tyto zločiny vedou k menší stabilitě a udržitelnosti projektu a většinou se nám později vymstí někde jinde a kolotoč se opakuje. Ve velkých projektech, kde se používá RDBMS, programátoři spoustu času neřeší nic jiného, než problémy spojené s databázemi.

Důvod č.4 proč přestat používat SQL: nevyhnutelné deadlocky

Každý rostoucí projekt, který začne používat RDBMS, narazí na ochrnující problémy s deadlocky. Nevyhnutelně, zaručeně, bez absolutně žádné závislosti na profesionalitě vývojářů.

Typy transakcí v RDBMS nejsou sice tak složité na pochopení ale jak projekt roste, přestává být udržitelné sledovat, která transakce ovlivňuje kterou, jaké tabulky, řádky se uzamykají, jaké musí být čteny, kdy jsou v pořádku „dirty reads“ apod. V praxi to nikdo nesleduje, nikdo to neanalyzuje a nehlídá – programátoři volí SQL v dobré víře, že to znají, dokážou v tom být rychle produktivní a zbytek se nějak zvládne. Ale jakmile v určité velikosti projektu začne SQL vystrkovat růžky, už se tím nechtějí zabývat a projekt trpí kritickými bugy s deadlocky a špatnou optimalizací souběžností.

Důvod č.5 proč nepoužívat SQL: delegování souběžnosti

To, že programátoři zápasí s deadlocky a optimalizací není ten hlavní problém.

Většina programátorů se k SQL vztahuje tak, jako kdyby jim ACIDita a transakce všechny problémy se souběžnostmi vyřešili a oni se tak mohou soustředit na své úžasné nabobtnalé CRUD frameworky a generátory databázových vrstev. Tady mám tabulku s uživateli, tady tabulku s produkty, tady tabulku se skladovými zásobami — super, všechno narvu do transakce a SQL se za mě postará o zbytek!

Hlavní problém je v tom, že programátor vůbec delegoval souběžnosti na databázi, což je přístup, který mu projde u malého/interního projektu. Souběžnosti má mít programátor plně pod kontrolou ve svém kódu, protože stanovení souběžností je součástí domény —- jinými slovy, to, jaké procesy mohou běžet paralelně a jaké ne je zájmem zadavatele, i když to zadavatel třeba neumí přesně analyzovat a zadat.

Zadavatelé většinou nedokážou souběžnosti identifikovat, musí vám být ale schopni říct, co je racing-condition a co ne, když se jich zeptáte. Příklad: pokud ve stejném čase vznikají dvě obědnávky nad zbožím, u kterého zbývá poslední kus, zadavatel musí říct: ano, zde platí kdo dřív příjde, ten dřív mele. Super! V takovém případě dokážete přesně říct, nad kterými entitami/hodnotami umístit zámek či semafor.

Ze začátku můžete nahrubo uzamykat celé entity a v případě potřeby můžete vždy optimalizovat uzamykání nad konkrétními hodnotami. A budete vždy rychlejší, než jakékoliv SQL. Pokud má být projekt připravený na horizontální škálovatelnost tak musíte používat distribuované zámky, ale to není těžký problém (zámky umí dobře obstarávat Redis).

Důvod č.6 pro nepoužívat SQL: delegování domény (business vrstvy)

Pokud se rozhodnete, že přestanete používat transakce, své RDBMS omezíte jen na čtení a na čistě atomické DML (insert, update, delete) a souběžnosti si pohlídáte sami tak jediné, co ještě můžete užitečně používat, je referenční integrita dat skrz primární a cizí klíče, procedury a triggery a jiné vyfikundace databázového systému.

Přestaňte používat procedury, triggery a jiné vyfikundace databázového systému. Tento článek je psaný pro programátory a ne pro databázové specialisty, kteří se psaní procedur, triggerů, viewů apod. věnují full-time. Programátor píše business vrstvu správně v kódu a ne v databázi. Projekt je tak mnohem přehlednější a jasnější.

Primární klíče, cizí klíče, unikátní klíče, constraints atd. jsou delegování business vrstvy do datové vrstvy! Referenční integrita je sice strašně cool ale to, že uživatel a objednávka je ve vztahu 1:N by mělo být patrné z kódu, nikoliv z databáze. Jakékoliv „unikátní klíče“ jsou pak jen rozmělňováním business znalostí z domény do databáze.

Jakmile všechny tyto věci vyhodíte a začnete používat SQL čistě jako mechanismus persistence tak vám dojde, že používáte BMW M7 k ježdení do Kauflandu a začnete hledat jiná NoSQL řešení.

(RDBMS je adekvátním systémem pro čtení např. u systému pro datovou analýzu, datamining, data warehousing atd. či jako uložiště pro readmodely (o readmodelech níže))

Aspektově orientované programování (AOP)

Za celou svoji profesní kariéru jsem se nikde s tímto přístupem nepotkal byť si myslím, že je to celkem cool přístup a rád bych viděl nějaký větší, primárně v AOP psaný projekt. V C# tento způsob rozhodně není moc populární – jediný známý AOP projekt je komerční PostSharp.

AOP je paradigma, které rozlišuje různé „aspekty“ které se k IT projektu vztahují napříč mnoha úrovněmi: logování, databázi, autorizaci, autentizaci, business logiku atd. apod. a každý tento „aspekt“ se programuje odděleně „navěšením se“ na existující infrastrukturu.

Dle mého se ale tento přístup v C# nepoužívá hlavně proto, protože tu samou funkcionalitu „navěšení se na něco“ splňují dekorátory (decorator pattern). Když jsem si s PostSharpem naposledy hrál, projekt se celkem dramaticky zpomalil a běžel jak želva.

Pročítal jsem si ještě https://www.postsharp.net včetně dokumentace a včetně stránky s ksichtem Scott Hanselmana, což je v .NET komunitě velké jméno z Microsoftu a nepřesvědčilo mě o výhodách tohoto komerčního projektu, který ale v nějaké edici mohu použít zdarma, absolutně nic.

Aktoři (Actor)

Actor model je velice cool model, ve kterém je váš kód tlačen do aktorů. Zjednodušeně řečeno: aktor je instance třídy, která má vlastní identifikátor a s daným identifikátorem může v rámci této třídy běžet pouze jedna metoda a ostatní volání čekají ve frontě (tzn. obyčejný semafor, ten je však implicitní).

V .NET světě existují tři projekty, které využívají aktory:

  • MS Orleans
  • akka.net
  • dapr (potažmo Service Fabric Reliable Actors, programuje se to stejně)

Výhody: skvělé pro online hry

Dva hráči hrají nějakou online hru a každý z těchto hráčů je reprezentován aktorem. Tito hráči reprezentují s objekty, které se v této hře nachází — tyto objekty jsou také reprezentovány aktory.

Aktoři jsou ideální pro use-case, pro který byly vytvořeny: pro virtuální interakce v online hrách. Každý aktor reprezentuje objekt, se kterým lze dělat právě jen jedna akce a ostatní akce čekají ve frontě, ze které mohou být vyhozeny/přerušeny či posunuty výše. Aktoři mohou vytvářet nové aktory a volat existující aktory — což přesně pasuje do modelu virtuálního světa, kde se hýbe mnoho věcí najednou a operace aktorů a interakce mezi aktory jsou definované.

Nevýhody: A pak někoho napadlo s aktorama naprogramovat API pro eshop…

Muselo k tomu nevyhnutelně dojít. Já sám jsem byl součástí projektu, kde se aktoři přes DAPR používali k programování API do mobilní aplikace pro koncové uživatele. Sám jsem byl z aktorů celkem nadšený, když jsem se o nich naučil, než jsem dospěl k názoru, že aktoři jsou skvělí jen pro online hry či virtuální online světy a pro psaní backendů se naprosto nehodí.

Nefunguje to. Nepoužívejte prosím vás aktory pro obyčejné systémy jako jsou eshopy, API (neherních, formulářových) mobilních aplikací a podobně. Aktoři jsou fakt velmi specifická architektura, která dává smysl u online her nebo něčeho podobného.

Doménu, ve které se nachází objekty jako User nebo Product nedává smysl reprezentovat skrz aktory. Jasně: působí to dost dobře a „doménově“ když definuji, že u každého uživatele či produktu lze zavolat jen jednu metodu. Dává nám to pocit, že jsme zvítězili nad problémem souběžností (concurrency) a zbavili jsme se deadlocků a v kontextu konkrétního aktora jsme si vždy jisti, že běžíme v jednom vlákně.

Jenže…potom z aktorů využíváme prakticky jen mechanismus lockování a navíc máme toto lockování striktně limitované na jedinou metodu v aktorovi. Což by neměl být až zas takový problém, musíme si ale dávat pozor na to, abychom v aktorovi nekombinovali dlouho běžící proces s často volanou metodou. To je ale zbytečné omezení, na začátku projektu nikdy nemáme informace o konkrétním použití.

Jak se má programovat proces, ve kterém potřebujeme aktivovat dva aktory různých typů najednou? Jeden nápad je skrz jiný typ „kompozitního“ aktora, jehož identita se skládá z identit všech aktorů, se kterými pracuje. Na první pohled celkem hezký nápad. Jak ale „kompozitní aktor“ interaguje s ostatními „kompozitními aktory“? Mělo by to být vůbec možné? Jak ohlídáme, že nějaký programátor nenapíše nějakého long-running metodu do kompozitního aktora, který se ale musí spouštět často? Jak by se měly třídy kompozitních aktorů pojmenovávat: UserProductCompositeActor? Co když mám operaci, kde potřebuji interagovat s 5 dalšími aktory? Kompozitní aktoři přináší víc otázek než odpovědí a víc rizik než jistot.

Místo kompozitních aktorů tak můžeme používat nějaké obyčejné služby…ale jaký je pak rozdíl mezi aktory a službami s nějakým obyčejným uzamykáním skrz semafor? K čemu je dobré používat relativně komplexní architekturu, když z této architektury používáme jen tu nejtriviálnější část? A co se vlastně v aktorech samotných píše: Volají se služby? Nebo obsahují rovnou business logiku a píšou data do repozitářů?

Měly by se přes aktory číst data? To rozhodně ne: u většiny businessů je 90% operací čtení a 10% zápis (o tom píšu ještě níže) oproti online hrám, kde aktoři napřímo ovládají svá data a poměr operací pro čtení a zápis je mnohem vyváženější (např. každý pohyb hráče v prostoru je zápis dat ve změně souřadnic). V aktorovi konkrétní instance může běžet vždy jen jedna metoda najednou což by u eshopu velmi rychle vedlo k timeoutům a nejspíš i k deadlockům.

Měly by tedy existovat aktory u objektu jako je Produkt zvlášť pro zápis a zvlášť pro čtení? Ale …to pak není nic jiného, než CQRS (o kterém píšu níže). Navíc oproti aktorům CQRS readmodel nemá problém s tím, že je čten z více vláken na jednou což je u většiny businessů žádoucí.

Doménové programování

DDD neboli Domain Driven Development je programátorské paradigma, ve kterém se soustředítě na doménu. Doménou zde není myšlena doména jako např. „domena.cz“ ale nejlépe bych to nazval jako znalosti businessu.

Co myslím znalostmi businessu? To jsou instrukce, kterou my programátoři dostáváme od svých nadřízených, od projekťáků nebo od zadavatelů. Příklad:

  • „Po kliknutí na tlačítko se musí odeslat objednávka a poslat email na dispečink.“
  • Pokud uživatel aplikuje víc jak jeden slevový kupón, vezme se ten s nejvyšší slevou“

Většina programátorů ve své profesi neurčuje znalosti businessu. My programátoři jen převádíme tyto znalosti do kódu a snažíme se, aby ten software fungoval přesně tak, jak si to zadavatelé přejí.

Programování domény vychází z myšlenky, že je velmi užitečné psát kód, který vyjadřuje znalosti businessu napřímo.

Příklad: PHP blog vs. C# API

Použiju příklad se slevovými kupóny z předchozího paragrafu – pokud uživatel aplikuje víc jak jeden slevový kupón, vezme se ten s nejvyšší slevou.

Jak to na programuje začátečník v PHP? (Jakým jsem byl já v roce 2005) Třeba následovně.

V souboru create_order.php je kus kódu, který přečte $_POST['discount_coupons'] pole IDček slevových kupónů. Přes MySQL si všechny slevové kupóny načtu a přes max(array_column($data, 'discount_percent')) (nebo bůhví jak…) si zjistim výši slevy.

Celý kód by mohl vypadat tedy asi nějak takto:

//create_order.php
if(isset($_POST['discount_coupons'])) {
   $statement = $sqlConnection->prepare("SELECT * FROM coupons WHERE id IN (?)")
   $statement->bindParam($_POST['discount_coupons']);
   $couponsSql = $statement->execute();
   $maxDiscount = max(array_column($couponsSql, 'discount_percent'));
   //...hotovo
}

A na tomto způsobu programování není absolutně nic špatného. Funguje to, napíše se to rychle, práce je hotova. Když jste začátečník, kterého zaměstnávají na dělání relativně malých projektů, webů a eshopů se sexuálními pomůckami jako mě v roce 2005 tak tento způsob psaní kódu naprosto stačí.

Podle paradigmatu doménového programování bych dnes v C# totéž zadání napsal takto:

private readonly ICouponRepository repository;
public async Task PlaceNewOrderAsync(NewOrder newOrder) 
{
    var couponIds = newOrder.Coupons.Select(c => c.ID)
    var repoCoupons = await repository.FindCouponsAsync(couponIds);
    int maxDiscount = repoCoupons.Max(c => c.DiscountPercent);
    //atd...
}

Kromě zjevného rozdílu v odlišném zápisu dvou úplně odlišných programovacích jazyků, jaký je rozdíl z pohledu programování podle doménového paradigmatu?

  • PHP: Musíte PHPko aspoň trochu znát, abyste věděli, že isset($_POST) kontroluje, že přišel HTTP POST request, že $_POST obsahuje asociativní pole, že SELECT * FROM coupons je SQL dotaz nad nějakou relační databází, že bindParam je nastavení parametru do SQL dotazu protože spojování řetězců je SQL injection….doménová znalost je ztracena uprostřed obrovského množství technologických závislostí. Ten, kdo čte ten kód, musí znát PHPko, HTTP protokol a relační databáze protože jedině s těmito znalostmi pochopí, že kód jenom vybere tu nejvyšší slevu ze všech kupónů.
  • C#: V C# kódu je vyjádřeno:
    • že zde máme nějaký kupónový repozitář, kde jsou kupóny uloženy v ICouponRepository. Schválně to je psáno takto, protože v této části kódu nás nezajímá, jak je repozitář implementován.
    • že zde máme nějakou operaci PlaceNewOrderAsync která zjevně znamená, že vytváříme novou objednávku
    • že z nové objednávky vybereme přes Select(c => c.ID) IDčka kupónů a podle nich vyhledáme kupóny v databázi přes FindCouponsAsync
    • že ze všech kupónů v databázi zjišťujeme maximální slevu

(poznámka: Zápis v C# lze napsat stejně i v PHP — „doménově“ lze napsat kód i v PHP. V C# lze napsat kód tak, že přečteme data z HTTP POSTu a z relační databáze.)

Ten rozdíl, který chci zvýraznit je v tom, že v prvním zápisu je hromada technologických závislostí — musíte znát HTTP protokol a relační databáze, abyste věděli, co ten kód dělá.

V druhém zápisu je ale míra abstrakce taková, že nám stačí jen trochu znát C# a hned ze zápisu vidíme, co se děje. Není zde HTTP protokol, není zde relační databáze ale jen klasická manipulace s informacemi, ve které se snažíme co nejpřesněji vyjádřit to, co po nás chtěl zadavatel. To je podstata doménového programování.

Když programujeme doménu, soustředíme se na:

  • jasnou sémantiku kódu — jinými slovy text, který kód vyjadřuje, odpovídá významu toho, co ten kód dělá.
  • kód, který reprezentuje myšlenky a přání vlastníka businessu což pro nás programátory je většinou náš klient, zaměstnavatel, nadřízený, manažer, projekťák, analytik, architekt, atd. (nebo my samotní)

DDD – agregáty

V DDD se pod pojmem „agregát“ (aggregate) schovává něco, čemu by klasický programátor z 3-vrstvé architektury řekl „business objekt“.

Agregát je hlavní logická jednotka s identitou, na kterou je doména dělena. Každý agregát s identitou je zcela nezávislý na zbytku domény tzn. pokud děláme něco s agregátem „Produktu“ tak nesmíme dělat vůbec nic s agregátem „Skladu“. Typy agregátů jsou od sebe izolované tak moc, že je možné si je představit jako zcela oddělené služby, které fungují na samostatných serverech a nevědí o sobě navzájem.

Sága je název pro speciální služby, do kterých vstupují příkazy, které provádí operace nad více agregáty najednou, různých i stejných typů. V rámci ságy zpravidla kontrolujeme unikátnost emailů nebo distribuujeme operace na jednotlivé agregáty v rámci operace „Vytvoření objednávky“.

CQRS – oddělení čtení a zápisu

CQRS neboli Command Query Responsibility Segragation je paradigma, které říká, že 80% operací je čtení a 20% je zápis, u některých projektů kterými jsme prošel bych řekl klidně až 95% a 5%.

S CQRS souvisí koncept read modelů což je název pro ručně tvořené pohledy, které vás zajímají, přesně optimalizované pro čtení, které potřebujete. Diskový prostor je podstatně levnější, než procesorový čas takže ve tvorbě svých readmodelů se můžete nafukovat.

Myslím si, že je velmi důležité v kódu architekturně oddělovat čtení a zápis právě kvůli CQRS. Pokud máte malý projekt nad relační databází tak v C# používáte Entity Framework na čtení a na zápis a je vám CQRS naprosto lhostejný – oddělovat čtení a zápis nedává smysl.

Pro větší projekty či projektu, u kterých plánujete růst se ale vyplatí CQRS aplikovat:

  • Spousta zátěže na serverech je ze čtení. Pokud potřebujete serveru ulevit, vytvoříte read model s vlastní indexací atd. atp. a nemusíte se bát, že to zasáhne cokoliv jiného. Nafukujte se na desítky i stovky gigabajtů nenormalizovaných dat, protože diskový prostor je levný.
  • CQRS nutí programátory přemýšlet nad doménou jako nad pravidly, které se věnují zápisu. Ostatní data se dají rekonstruovat buď naživo (silná konzistence, např. SQL View) nebo dodatečně (eventuální konzistence, např. asynchronní read model).

CQRS a eventuální konzistence

CQRS je odbočením od doménového paradigmatu. Zadavatelé totiž téměř nikdy nerozlišují mezi čtením a zápisem. Zadání „Tady se musí vypisovat X“ je pro zadavatele v úplně stejné důležitosti, jako „Po kliknutí na tlačítko se musí stát Y“. Oddělování kódu dle CQRS je ale dle mého oběť, která se vyplatí.

CQRS je obrovská výhoda pro každý rostoucí projekt, že se vyplatí jej implementovat, nebo se na něj minimálně připravit už na začátku projektu. Na začátku může být všechno čtení a zápis ze stejných databází. Z pohledu CQRS máme data v silné konzistenci tzn. to, co vidíme v UI je to, co je aktuálně (nebo téměř aktuálně) platné, protože je to napřímo přečtené z databáze.

Jak ale projekt začne růst a databáze se začne zavařovat, přejdeme na asynchronní read modely (které vůbec nemusí být ve stejné databázi, ani na stejném serveru), které na základě událostí vyhozených z domény rekonstruují pohled, který potřebujeme.

Toto znamená, že máme data v eventuální konzistenci tzn. to, co vidíme v UI, nemusí být aktuálně platné, ale víme, že po dokončení asynchronní aktualizace nad readmodelem to platné bude. Pro 99% use-case toto není problém. Samozřejmě když říkám „není problém“ tak tím nemám na mysli to, že trvá hodiny a hodiny, než se váš read model zaktualizuje. Většinou existuje vysoká tolerance pro aktuálnost zobrazované (tzn. z readmodelu, nikoliv pravdivá z domény) informace např. o aktuálním stavu zboží, ta může být u většiny businessů až v minutách ale zpravidla by neměla trvat déle, než pár vteřin.

Není to problém také proto, že náš projekt v první řadě nikdy v silné konzistenci nebyl. Většina programátorů, zvyklá z SQL, přistupuje ke čtení dat jako k něčemu, k čemu lze přistupovat paralelně a při čtení nedochází k uzamčení čtených dat ani, pro čtení ani pro zápis. Paranoidní konzistence znamená, že dokud klient neobdrží data, která chce číst, nikdo jiný k těm datům nemá přístup ani pro čtení, ani pro zápis, což je ale use-case užitečný tak možná v nějakém těžce security prostředí, než v obyčejném eshopu/CMS.

Když přijdete za svým zadavatelem a řeknete něco na způsob „Můžu vyřešit to, že náš web bude rychlejší ale znamená to, že se může nějakým uživatelům někdy zobrazit to, že zboží je skladem, i když není. Objednat zboží, které není reálně skladem, stále nepůjde, jenom to potrvá to pár vteřin, než se aktualizuje informace, že zboží skladem už není.“ tak většina zadavatelů řekne, že to není absolutně žádný problém. Pokud nemáte nadřízené nějaké negramoty tak každý projekťák rozumí trojúhelníku čas-kvalita-peníze ve kterém si vždy můžete vybrat dvě věci na úkor jedné.

Asynchronní readmodel je drobné snížení kvality – přecházíme ze silné konzistence na eventuální – rychle a za málo peněz (pokud jsme s CQRS počítali už na začátku). Snížení kvality přechodem ze silné na eventuální konzistenci je ale naprosto přemláceno zvýšením responsivity SQL serveru, který není zahlcený SQL dotazy. Díky tomu dokáže zpracovat víc objednávek (například).

Architektura á la Miroslav Bartl

Tak je na čase ukázat, jak píšu doménu já. Stejně jako tento článek, je to prostě jen můj subjektivní názor založený na mých zkušenostech a na tom, jak to vyhovuje mně a co považuji za užitečné jak při tvoření solo projektů tak i při práci v týmu.

Agregát jako logická jednotka, nikoliv jako konkrétní třída

V první řadě je nutné identifikovat agregáty. Neprogramuji své agregáty jako třídy ale spíš jako oblasti, které obsahují kód, který se k danému agregátu vztahuje. V C# projektu si takto pojmenuji celou složku jako User nebo jako Product což jsou mé agregáty. Agregát je kořen stromu tzn. mé agregáty mohou obsahovat jiné objekty, kolekce objektů atd. apod.

V rámci těchto agregátů uplatňuji stejné pravidlo, jako v DDD: jednotlivé agregáty, ať už různých nebo stejných typů, na sebe nevidí. Tzn. jakýkoliv proces, který běží v kontextu agregátu, nevidí na žádný jiný agregát. (Jak řeším kolektivní business pravidla popíšu níže).

Ságy se většinou vztahují přímo k danému agregátu – potom je umisťuji do složky „Sagas“ k danému agregátu. Proč?

V rámci agregátu chci řešit pouze izolovanou logiku agregátu a přímý kontakt s repozitářem, který patří pouze danému agregátu. V operacích, které běží přímo nad agregátem, neřeším zámky/semafory, ty řeším v dané sáze.

Někdy nelze ságu ke konkrétnímu agregátu přiřadit. Jsou to zpravidla operace, které se zabývají mnoha různými agregáty najednou, jako například „Vytvoření objednávky“. Tyto operace dávám do složky „_Sagas“ s podtržítkem na začátku pouze pro účely řazení.

Model agregátu jako anemický model

Dle DDD definice je agregát jedna třída, která obsahuje všechny vlastnosti a metody, které se k agregátu vztahují. Jak jsem popsal výše, toto spíše vede k problémům.

Dle mého názoru třída agregátu má pouze svoji reprezentaci. Tomu se říká anemický model, který je některými lidmi tak strašně moc nesnášen.

public record User
{
    public string Id { get; init; }
    public string Name { get; init; }
    //... atd
}

A teď bacha.

  1. Reprezentace agregátu je modelem dokumentové databáze. Tzn. reprezentace agregátu je to, co je uloženo v NoSQL. Není žádné rozlišení mezi „business“ a „repo“ objektem, repozitář pracuje se stejným typem, jako handler.
  2. Reprezentace agregátu může vstoupit do UI a může přicházet z UI ale není to podmínka. UI téměř vždy potřebuje nějaké své vlastní modely, za překlad mezi UI modelem a doménovým modelem je odpovědné UI.

Pokud se kroutíte odporem, tak je to dobře. Znamená to, že konfrontuji vaše zaběhlé, dogmatické představy o tom, co je udržitelný kód.

Ad. 1 + 2) Agregát je sdílený model. DB/UI modely se tvoří podle potřeby.

Koukněte se na to takto: repozitář je součástí domény. Doména zahrnuje business kód + interakce čtení/zápisu s repozitářem. Není důvod vytvářet různé modely pro business a pro repozitář, protože obojí je součástí domény.

Repozitář je abstrahovaný jako interface, to sice ano, ale hlavně kvůli testům, abych nemusel v testech složitě mockovat všechny ty typy, které náleží do MongoDB.Driver NuGetu. Nemám důvod předpokládat, že budu přecházet na jiný způsob persistence a stejně tak byste neměli ani vy!

Mapování mezi C# třídou a MongoDB BSON dokumentem je triviální a vytváření dalších modelů nepřináší vůbec žádnou další výhodu.

Jako programátoři bychom se měli snažit psát vždy co nejméně kódu a každý kód, který píšeme, by měl být dobře čitelný sám o sobě, aniž by potřeboval komentář.

V MongoDB stačí Collection<T>.InsertOneAsync(T model) a máme hotovo.

Používat stejný model v DB a v UI není žádný zločin. Stačí se na to nekoukat jako na DB/business model ale jako na doménový model. UI modely se ale mohou rozlišovat a velmi často rozlišují a proto psát zvlášť UI modely a mapovat mezi UI a doménovými modely je v pořádku — ale pouze tam, kde se UI od domény opravdu liší a ne kategoricky všude, to je úplně k ničemu.

Pokud je UI model totožný s doménovým modelem, pak děláte věci správně, programujete doménově! Vaše UI je totožné s tím, jak jsou data uložená v databázi a jak jsou reprezentována v paměti. Nemáte vůbec s ničím moc práce a to, co se po vás chtělo, je v kódu zapsáno správně nejen správně sémanticky, ale i graficky.

Pokud nastane situace, kdy v důsledku autorizace různí uživatelé musí vidět různá data nebo kdy nad existující strukturou agregátů lze vytvořit zjednodušující pohledy tak to nevadí. Prostě vytvořite UI model/endpointy s takovou strukturou, jakou potřebujete a proveďte potřebnou transformaci v obou směrech. Práce, kterou trávíte psaním takového kódu, odpovídá tomu, co je pro projekt potřeba udělat.

Pokud je UI oddělené od nějakého API tak se na API nedělají UI modely ale RequestModely – filozofie je ale stejná. Pokud UI skrz API pracuje s naprosto identickým modelem, proč bychom měli v API posílat něco jiného?

Jakmile máte zadání, vaše architektura by měla být taková, že většinu času strávíte čas psaním produktivního kódu a ne psaním architektury.

MediatR

V rámci agregátů používám populární a známý MediatR. Je to v jádru velmi jednoduchý projekt, který nedělá nic světoborného ale to co dělá, dělá správně.

V rámci agregátu má každá operace svůj vlastní request a pro každý request existuje jeho request handler, ve kterém je tato operace zpracovávána. IRequestHandler je interface, takže jedna třída může obsahovat kód pro zpracování více requestů.

Na obrázku výše mám dva handlery: LocationHandler a LocationPictureHandler které zpracovávají všechny requesty tohoto agregátu. Toto rozdělení je čistě orientační.

Místo handlerů jsem mohl psát klasické služby a fungovalo by to úplně stejně, není v tom absolutně žádný rozdíl. MediatR používám, protože:

  • sjednocení mechanismu pro vyvolávání událostí – kromě toho, že MediatR umí příjmat requesty a zpracovávat je v request handlerech (1:1) tak umí přijímat notifikace a v notification-handlerech (1:N). Notifikace využiji pro vyvolávání událostí, nad kterými mohu psát read modely.
  • služby reprezentující metody agregátu v jedné třídě jsou gigantické a partial class zápisu se snažím vyhnout, pokud nejde o generovaný kód. Request může být v RequestHandleru který zpracovává víc requestů, nebo v odděleném handleru, který zpracovává jen ten daný request.
  • requesty, které vstupují do handlerů, jsou obyčejné třídy které lze hezky mapovat na online endpointy. Pokud se online endpoint musí odlišovat, pak související mapping probíhá v daném endpoint projektu (MVC, gRPC…).

Nevýhodou tohoto zápisu je, že musíte psát zvlášť request pro ságy a v ságách musíte psát requesty pro konkrétní agregáty.

Na obrázku výše jsou dva zvýrazněné requesty:

  • CreateLocationSagaRequest
  • CreateLocationRequest

Používám konvenci, že pokud se request/request handler týká ságy, má suffix SagaRequest nebo SagaRequestHandler zatímco u agregátů je suffix pouze Request nebo RequestHandler.

CreateLocationSagaRequest vstupuje do handleru ságy. V kontextu ságy ještě nejsem v konkrétním agregátu, mám zde ale dovoleno vytvářet zámky, číst z jiných agregátů a dělat si vlastně úplně cokoliv napříč celou doménou — nesmím ale provádět přímý zápis do žádného repozitáře, to mohu dělat pouze skrz RequestHandlery konkrétních agregátů.

Na příkladu výše před vytvořením objektu Location v doméně je nutné přečíst nějakou konfiguraci a provést různé validace na základě hodnot z jiných agregátů. To je možné udělat pouze v sáze. V rámci agregátu Location dochází už pouze k lokálním validacím. RequestHandler agregátu má přístup ke svému repozitáři ale už k ničemu jinému.

Validace

Poslední, o čem chci v tomto článku mluvit, jsou validace.

Pokud píšeme API, se kterým komunikuje jeden frontend, který je odpovědný za to, že před odesláním validuje data, tak je dle mého naprosto v pořádku používat klasické vyhazování Exceptions. Každá zalogovaná a vyhozená exceptiona je totiž indikátorem chyby buď na backendu nebo na frontendu. V každém případě jde o výjmečný stav, který někdo musí fixnout.

Pokud jsme v API na které se napojuje prakticky kdokoliv a každý si ho volá jakkoliv chce a nemůžeme se od frontendu spolehnout absolutně vůbec na nic (nebo jsme ve strašidelném prostředí, ve kterém je API zodpovědné za správné validační hlášky, které pak frontend pouze zobrazuje) tak je nutné myslet na to, že vyhazování Exception je značný performance-hit a vyhazování validačních chyb je lepší napsat nějak jinak.

V MediatRu lze každou IResponse obalit do nějaké DomainResponse nebo ValidatedResponse která obsahuje výsledek dané operace. Je však dle mého důležité snažit se držet validace co nejblíž v operacích které potřebují validace vykonávat. Nějaký „validátor“ by se mělo vyplatit psát až pouze tehdy, pokud máme opravdu v kódu více než jedno místo, kde jedna a tatáž validace musí proběhnout.

Validace je část kódu, jež reprezentuje doménu a proto bychom se zvlášť u validací měli soustředit na to, že kód je sémanticky co nejjasnější a že význam psaného kódu pokud možno přímo vyjadřuje myšlenky zadavatele/businessu.

Mé začátky s vývojem pro mobilní zařízení

Flutter vs. Xamarin.Forms

Poprvé v životě pracuji na mobilní aplikaci. Protože má appka fungovat jak pro Android tak pro iOS tak ji píšu v Xamarinu, konkrétně Xamarin.Forms.

Jsem oproti Flutteru v nevýhodě, protože Xamarin není tak moc populární, ale nakonec jdu do toho, protože znám dobře C# a znám dobře Visual Studio. Zkusil jsem nanečisto udělat projekt ve Flutteru ale je toho prostě příliš mnoho, co bych se musel naučit a já už bych raději byl produktivní, než abych trávil nejméně jeden či dva měsíce plácáním se s pro mě kompletně neznámým stackem.

Popularita Flutter vs. Xamarin vs. Electron

Microsoft udržuje Xamarin celkem intenzivně naživu a přestože je Xamarinská komunita zjevně mnohem menší než Flutterovská tak rozhodně není až zas tak malá, že by se nevyplatilo do vývoje přes Xamarin investovat.

Android vs. iOS

Flutter ani Xamarin.Forms by nemusel existovat, kdyby neexistoval Apple. Všichni by měli Androida a byl by pokoj.

Vlastně: oni by ty iPhony ani tak nevadily, kdyby se Apple nechoval jak Microsoft v historii se svým Internet Explorerem. Vývoj pro obě platformy naráz – Android a iOS – fakt připomíná dělání webů v minulosti, kdy se prostě muselo při vývoji velmi často zohledňovat, že v prohlížečích fungovalo spoustu věcí jinak a Internet Explorer byla vždycky kategorie sama o sobě.

Microsoft se poučil, Internet Explorer zabil a nahradil jej Edge (což je jen Chrome s jiným oblekem), koupil Github a opensourcoval celou svoji vývojářskou platformu .NET, vyrobil VS Code a Visual Studio nabízí zdarma (pokud nejste větší firma). Microsoft pochopil, že na vývoj v nějaké closed-box platformy mu většina lidí sere a že cloud běží většinou na Linuxech.

Google to zjistil pravděpodobně do určité míry taky, vyvíjet pro Android lze na jakémkoliv OS a Android je vlastně jen upravený Linux, který je stejně jako Linux open-source.

Jenže ne Apple. V Applu furt pracují blbci co žijí v roce 2001. Vyvíjet pro iOS na Windowsech ani na Linuxech nelze. Tedy lze — pokud máte náladu na několika denní sraní se s qemu, Docker-OSX, kvm a podobný hackování — já s tím ztratil po několika dnech trpělivost, aspoň pod Windows 11/WSL2 to neběželo moc dobře (a bez podpory GPU) a nakonec na splátky budeme kupovat Maca, jen abychom vůbec aplikaci pro iOS zbuildili. Koukal jsem ještě na cloudové služby, kdy si za nějaký peníz Maca pronajmete ale vůbec se to nevyplatí, za ty peníze je užitečnější Maca splácet vlastního.

Visual Studio má pro to naštěstí celkem funkční tooling. Stačí, aby Mac byl dostupný v síti, zadá se jeho IP adresa, administrátorský účet s heslem a VS si tam doinstaluje vše potřebné. Při zbuildění iOS projektu se pak VS na Mac samo připojí, přes XCode si mobilní appku zbuildí a obrazovku mobilní appky, která běží nad XCodem v Macu, vám zobrazí vtipně přes vzdálenou plochu.

Vývoj mobilní aplikace

Jakmile má člověk nakonfigurované správně prostředí pro vývoj, běží vše dobře. XAML a MVVM jsem už prakticky zapomněl, naposledy jsem v něm dělal pro jednoho klienta WPF aplikace, ale to bylo ještě v dobách .NET Frameworku 4.6. Pořád jsem si byl ale jistej, že proniknout zpátky do XAMLu pro mě bude pořád efektivnější, než se učit s Flutterem.

Ve WPFku jsou pro mě věci takové intuitivnější. Nikdy jsem nebyl moc frontenďák ale naučil jsem se dělat weby a desktopové aplikace pro Windows přes Winforms a WPF.

Dělat na telefon je něco jiného. Ve Windows máte koncepty oken, modálních oken, okno s focusem. V telefonu ale žádná okna nejsou a místo toho se pracuje s navigačním stackem — okno, které se vám aktuálně zobrazuje, je okno které je umístěné na vrcholu navigačního stacku, do kterého buď okna přidáváte, nebo z něj okna odebíráte.

https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/navigation/hierarchical-images/pushing.png
Přidávání do navigačního stacku (zdroj)
https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/navigation/hierarchical-images/popping.png
Odebírání z navigačního stacku (zdroj)

V Xamarin.Forms je ale ještě jedna vyfikundace navíc – AppShell. Je to taková běžná struktura, kdy máte v mobilní aplikaci navigační panel vlevo a záložky vespod nebo nahoře. Díky AppShellu můžete libovolně kombinovat a zanořovat položky menu a záložky. Zajímavý je, že stránky, na který přes tyhle záložky a položky menu odkazujete, nejsou součástí navigačního stacku — AppShell má svůj vlastní navigační systém, kdy každá stránka vaší mobilní aplikace je identifikovaná nějakou route adresou, na kterou můžete odkazovat.

Navigace přes klasický navigátor

// navigace na stranku MyPage --- vlozi se do navigacniho stacku
await Navigation.PushAsync(new MyPage());

// navigace na predchozi stranku --- aktualni stranka se odebere z navigacniho stacku
await Navigation.PopAsync();

Navigace přes AppShell

// navigace na stranku MyPage
Shell.Current.GoToAsync("mypage");

// navigace na predchozis stranku
await Shell.Current.GoToAsync("..");

API klient – protokol a tooling

Mobilní aplikace většinou volá nějaké API, že jo. Ta naše aplikace není výjimkou.

REST API

V .NET Core se celkem intuitivně nabízí REST přes JSON. ASP.NET to podporuje ve svých knihovnách, stačí napsat Controllery, člověk nemusí řešit žádné ruční serializace a deserializace, můžete rovnou psát své objekty a máte vystaráno.

Pak stačí přidat Swagger nugety a ze svých controllerů máte vygenerovanou celou OpenApi specifikaci a nemuseli jste hnout ani prstem.

Tooling: RestSharp vs HttpClient

Pro C# existuje několik možností ale za zmínku stojí dle mého jen tyto dvě:

openapi-generator umí udělat klienta, který pracuje s RestSharpem zatímco NSwag pracuje přímo s HttpClientem. S HttpClientem se musí pracovat obezřetně – ve výchozí konfiguraci dispose HttpClienta nechává otevřený socket v TIME_WAIT dokud nevytimeoutuje. Používat HttpClient jako singleton je také chyba, protože HttpClient nerespektuje zjevně TTL u DNS záznamu (i když issue na githubu na tohle téma je už uzavřený, těžko říct, jak to funguje třeba v .NET 6, to bych musel otestovat). HttpClient se má používat správně přes HttpClientFactory

Z dokumentace:

Although HttpClient implements the IDisposable interface, it’s designed for reuse. Closed HttpClient instances leave sockets open in the TIME_WAIT state for a short period of time. If a code path that creates and disposes of HttpClient objects is frequently used, the app may exhaust available sockets. HttpClientFactory was introduced in ASP.NET Core 2.1 as a solution to this problem. It handles pooling HTTP connections to optimize performance and reliability.

Zdroj

NSwag a jeho HttpClient je tedy problematický …ale podle mně ne zas tolik. Nejsem si jistý, jestli u mobilní aplikace pro koncového uživatele prakticky vůbec může dojít k vyčerpání socketů, kterých teoreticky může být až 65536 (pro každý port jeden). U mobilní aplikace bych čekal, že OS (ať už Droid nebo iOS) bude na visící timeouty a spojení ve stavech jako TIME_WAIT mnohem agresivnější.

API klient – GRPC

Původně jsem chtěl dělat API a klienta přes GRPC. Věděl jsem, že pro Visual Studio je tooling hotový a skrz nugety lze zprovoznit GRPC endpointy okamžitě.

GRPC má oproti REST obrovskou výhodu: je to binární protokol. Což znamená, že přenos dat, serializace a deserializace je z podstaty věci mnohonásobně rychlejší, než JSON REST.

GRPC jsem nakonec ale nepoužil. API chci totiž provozovat jako WebAppku na AppService na Azure a bohužel, z nějakého důvodu (nejspíš lenost) Microsoft nemá dokončenou implementaci HTTP/2 na Windows Server OS a kvůli tomu GRPC na WebAppkách nefunguje. A to ani když máte Linuxovou AppServicu — což jsem nejdřív upřímně nechápal, pravděpodobně je to ale tím, že každá AppServica či WebAppka ve skutečnosti běží jako ServiceFabric, což je Windows Server.

Mobilní aplikace a unhandled exceptions

Když máte v C# konzolovku nebo nativní, desktopovou aplikaci ať už pro Windows nebo pro Linux, je někdy užitečné napsat kus kódu, který handluje nezachycené exceptiony. V .NETu je tohle triviální.

AppDomain.CurrentDomain.UnhandledException += MyHandler;
TaskScheduler.UnobservedTaskException += MyHandler; //pro zachyceni nezachycenych exception bezicich v tascich

V Xamarinu ale platí úplně jiný režim. Jakákoliv exceptiona shodí aplikaci bez ohledu na to, zdali je nebo není zachycená. Tohle je zjevně rozdíl mezi Flutterem a Xamarinem. Flutterovská aplikace beží totiž ve svém vlastním enginu a každý vygenerovaný pixel patří Flutteru. Takže i exceptiony, které si ve Flutteru vyrobíte, si můžete ve Flutteru také zpracovat.

Xamarin ale kompiluje všechno do nativního kódu takže handlování exception probíhá podle pravidel dané platformy.

AppDomain, TaskScheduler — to všechno sice proběhne, jakmile dojde k vyhození exceptiony, jenže aplikace stejně spadne. Android má zjevně nějaký svůj způsob, jak unhandled exceptiony handlovat. Všechno jsem to ale zkoušel a nic nezabrání tomu, že aplikace kompletně žuchne.

V iOSu to navíc údajně ani nejde, což zatím nemohu otestovat. Swift údajně nemá podporu pro zachycení nezachycených exception a někde jsem našel (teď se mi to nedaří dohledat), že Objective-C při vyhození exceptiony už není v obnovitelném stavu a jediné, co v tomto stavu ještě lze udělat, je zapsat někam lokálně log s chybou.

API klient bez exceptions

Takže v Xamarin prostředí vyhození exceptiony bezpodmínečně vede ke shození celé aplikace. Před shozením aplikace můžu exceptionu zalogovat ale to je vše – aplikace prostě žuchne.

Bomba.

V takovém případě se potřebuji exceptionám vlastně totálně vyhnout. Což je stejně správně. try/catch bloky jsou náročné na výpočetní výkon a navíc v UI aplikaci by se vůbec exceptiony neměly používat. Validace musí být součástí business logiky a jakýkoliv „nevalidní stav“ je něco, co nemělo vyhazovat exceptiony, natož shazovat celou aplikaci.

Průser je v tom, že NSwag vybleje klienta, který chrlí exceptiony jak hovado. openapi-generator používá v tomto ohledu trochu lepší strategii skrz staticky nastavený ExceptionHandler.

Pokud programujete REST api, pak pro každý endpoint vracíte různé HTTP status kódy. 200 pro vytažení dat, 204 pro změny, 400 pro validaci, 403 pro autorizaci atd. apod. Generátory mají tendenci na vše, co není 2xx, vyhodit exceptionu.

Co s tím?

Generované modely, vlastní API klient

Zkoušel jsem různé konfigurace a nastavení i s openapi-generatorem, (který jsem kvůli jiným důvodům nechtěl použít), ale nakonec jsem to vzdal, vždycky jsem narazil na nějaké dost podstatné limitace, které mi vadily a rozhodl jsem se napsat si vlastního klienta.

Modely ale naštěstí psát nemusím. Konfigurace NSwagu obsahuje parametry generateClientClasses a generateClientInterfaces. Pokud tyto parametry nastavím na false tak mi NSwag vygeneruje pouze request/response modely ale žádné klienty mi necpe.

Jednoduchého klienta jsem si napsal takto:

  • Při zavolání jakékoliv API metody dodávám zároveň funkci, která zpracovává validní odpověď (HTTP 2xx) a volitelně funkci, která zpracovává jakoukoliv jinou, než nevalidní odpověď (HTTP 4xx, 5xx).
  • Ve výchozím chování v případě nevalidní odpovědi chci, aby mi to vyplivlo nějaký popup, ve kterém je zobrazená generická zpráva o tom, co se z API vyplivlo. Toto chování ale mohu kdykoliv upravit.

Nemusím se tedy bát, že by mi NSwag vyhodil nějakou exceptionu a ani se nemusím starat o to, jak metody NSwagu wrapovat do nějakého try/catche, kterému se stejně chci v kódu vyhnout, pokud je to možné. Jasně – musím si napsat vlastního klienta ale protože kód pro mobil má sdílenou knihovnu s API tak URL endpointů mohu napsat staticky (parametrizované endpointy v tomhle použití budou muset být metody, což není vůbec hezké ale to jsem schopný zkousnout).

Ukázka volání API:

await Api.Get<GuideRequest>(url: Endpoints.User.GetLastGuideRequest, async (gr) =>
{
	if (gr != null && gr.State == GuideRequestState.Waiting)
	{
		LayoutState = LayoutState.Custom;
		CustomState = "RequestPending";
	}
	else
	{
		LayoutState = LayoutState.Success;
	}
});

Nastavení prefixu v RedLock.net nugetu

RedLock.net je knihovna pro lockování nad Redisem skrz algoritmus, který umí locky využívat distribuovaně napříč několika redis instancemi. Což v tuto chvíli nepotřebuji ale je dobré začít implementovat s něčím, co funguje i nad jednou instancí a je to potenciálně škálovatelné.

Locky, které knihovna vytváří, mají nastavený prefix redlock: což se mi úplně nehodí, společně s lockama jsem chtěl ještě ukládat informaci o počtu čekajících vláken pro monitoring. Nakonec to není tak těžké, prefix lze změnit skrz veřejnou propertu. Stačí jen při inicializaci RedLockFactory v IoC nastavit toto.

sc.AddSingleton(sp =>
{
    var redLockMx = (RedLockMultiplexer)(ConnectionMultiplexer)sp.GetService<IConnectionMultiplexer>();
    redLockMx.RedisKeyFormat = "myprefix:{0}";
    return RedLockFactory.Create(new List<RedLockMultiplexer> { redLockMx });
});

Bacha na to, že RedLockMultiplexer má implicitní konverzi z ConnectionMultiplexer

A taky bacha na to, že u prefixu nesmíte zapomenout na {0} což nahradí název locku samotného.

Přecházím z CosmosDB na MongoDB

CosmosDB je NoSQL databázový systém který provozuje Microsoft na Azure. Chtěl jsem na něm stavět novou aplikaci z následujících důvodů:

  • CosmosDB je PaaS (Platform As A Service). Nechci vůbec řešit provoz konkrétních aplikací na serverech (IaaS), chci, aby všechno bylo čistě cloudové a pokud možno se nechci vůbec zabývat monitorováním VMek.
  • V CosmosDB člověk musí pochopit jen logiku toho, jakým způsobem volba partition klíče v dokumentu ovlivňuje distribuci mezi logickými a fyzickými partitions.
  • Skrz dobře zvolenou partition strategii pak v CosmosDB nemusíte vůbec řešit škálování a platíte pouze za propustnost v jednotkách RU (Request Units) za vteřinu.
    (Rok 2021: minimum je 400 RU/s, 100 RU/hodina = 0,008 USD = ~$23 USD za měsíc)
  • V serverless režimu platíte za určitý celkový počet RU a ne za propustnost. Server se škáluje automaticky.
  • Indexování nad všemi hodnotami dokumentu by default (v MongoDB je by default indexace pouze nad primárním klíčem).

Na CosmosDB se mi ale od začátku některé věci nelíbily.

  • Chybějící podpora hromadného smazání/aktualizace všech dokumentů.
  • V platebním režimu RU/s CosmosDB vyhazuje chybu při překročení této propustnosti. Při programování aplikace člověk musí myslet na to, že každé volání CosmosDB může tuto chybu vyprodukovat.
    • Jasně, aplikace by se měly stavět resilientně a v dobře postavené aplikaci by se vždy mělo počítat s pádem jakékoliv externí komponenty.
    • Jenže tohle prostě znamená práci navíc. Toto mě nutí monitorovat metriky RU/s a monitorovat, jestli je z pohledu této metriky aplikace pro koncové uživatele vůbec použitelná.
  • Serverless mi nešlo založit v data centru Germany West Central (Frankfurt). Ind z Azure podpory mi řekl, že na portálu to sice není ale že to založím přes az cli nebo powershell. To jsem zkoušel ale dostal jsem HTTP 503 Service Unavailable. Ve West Europe mi to fungovalo ale WE (Nizozemí) je o 10ms dál než Frankfurt a u API pro mobilní aplikace se každých 10ms počítá. Dál jsem to neřešil.
  • Úchylné SDK pro C#, které se nedá prostě používát normálně napřímo a člověk si spíš k němu musí napsat nějaké vlastní extension metody. Viz. kód níže z oficiálního ukázkového repa
QueryDefinition query = new QueryDefinition("SELECT * FROM Families f WHERE f.id = @id AND f.Address.City = @city");

List<Family> results = new List<Family>();
using (FeedIterator<Family> resultSetIterator = container.GetItemQueryIterator<Family>(...))
{
    //Abych dostal všechny resulty, musím čekat na HasMoreResults=false
    //a dokud je to true, musí přečíst dostupnou kolekci dat skrz ReadNextAsync.
    while (resultSetIterator.HasMoreResults)
    {
        FeedResponse<Family> response = await resultSetIterator.ReadNextAsync();
        results.AddRange(response);
    }
}

//Nelíbí se mi, že tohle je způsob, který MS prezentuje jako to, jak by se měl CosmosDB používat.
//Proč prostě by default neexistuje v SDK metoda, která vrací IEnumerable<Model> nebo IAsyncEnumerable<Model> která skrývá tento implementační detail s HasMoreResults a ReadNextAsync?
//Proč musím psát vlastní extension metody?

CosmosDB Emulator = naprostý konec

Všechny výše zmíněné nevýhody pro mě ale stále nebyly tak významné. Automatická škálovatelnost schovaná za jednotky propustnosti RU/s a serverless varianta jsou prostě fakt skvělá lákadla. Jako programátor jsem tak nucený se soustředit pouze na to, že správně používám klíče a mám rozdistribuované partition klíče tak, aby CosmosDB mohl automatizovaně shardovat moje data bez větších omezení podle libosti. (Nutno dodat, že nemám zkušenosti s tím, jak CosmosDB doopravdy partitionuje ve vysoké zátěži, vycházím z informací zde a zde.)

Důvod, proč od CosmosDB odcházím je ten jejich posranej emulator.

Like seriously.

Microsoft místo toho, aby napsal emulátor který emuluje chování skutečného CosmosDB tak podle mého názoru vzali zdrojáky, kterým provozují skutečný CosmosDB na infrastruktuře Azure, ten zdroják jenom totálně ořezali a vznikla tak naprostá sračka, se kterou se nedá dobře pracovat.

Pokud si stáhnete emulator napřímo pod Windowsama a z C# kódu se na ten emulator připojíte, všechno funguje celkem dobře. Některý věci teda trvaj fakt relativně dlouho: funkce jako CreateDatabaseIfNotExistsAsync nebo CreateContainerIfNotExistsAsync trvají stovky milisekund až vteřiny. Funkce pro vytvoření databáze/kontejnerů jsou fakt zrovna podstatný pro psaní integračních testů (o tom ale víc níže).

Nefunkční a nepoužitelný docker container a vynucené HTTPS

Co už ale začne bejt dost na hovno je rozeběhnutí CosmosDB v docker containeru. Taková věc je totiž fakt docela potřeba, že jo, kvůli integračním testům. Já prostě nechci provozovat integrační testy proti ostré, placené verzi na cloudu. Já chci v rámci CI v nějaké build pipelajně rozchodit CosmosDB docker kontejner a proti němu spustit integrační testy.

Jenže ejhle.

Existují 2 verze docker kontejneru s CosmosDB emulátorem. Jedna je pro Windows, jehož image má 3GB a v nějakém build agentovi to prostě zabere deset minut, než se stáhne a nastartuje. Další verze je Linuxová, ta je podstatně menší.

Obě verze mají ale problém v tom, že CosmosDB emulátor generuje HTTPS certifikát při každém startu nejspíš proti IP adrese kontejneru. Takže v daném prostředí je nutné po nastartování kontejneru certifikát importovat. Takže musíte v CI pipelajně řešit a testovat sadu příkazů jako je tato:

ipaddr="`ifconfig | grep "inet " | grep -Fv 127.0.0.1 | awk '{print $2}' | head -n 1`"
curl -k https://$ipaddr:8081/_explorer/emulator.pem > ~/emulatorcert.crt
cp ~/emulatorcert.crt /usr/local/share/ca-certificates/
update-ca-certificates

A to prostě zabírá mrtě zbytečnýho času tohle v CI pipelajnách otestovat a zprovoznit. Velmi brzy zjistíte, kvůli problémům popsaným v kapitolách níže, že ten kontejner je prostě kurevsky nestabilní.

Proč to prostě nemůže fungovat hned? Proč nemůžu udělat něco jako docker run -p 27017:27017 mongo a všechno začne magicky fungovat, aniž bych se musel srát s nějakými certifikáty?

Řešením je vypnout ověřování certifikátu na straně aplikace ale s linuxový docker containerem se mi to stejně nepodařilo stabilně zprovoznit.

Divná a nesmyslná omezení

Na téhle stránce se dočtete podivnost, kterou je emulátor zatížen. Napovídá to tomu, že Microsoft nedělal žádný emulátor ale vzal zdrojáky z kódu, který provozuje CosmosDB na Azure infrastruktuře a nějak ho brutálně ořezal, aby to vůbec bylo použitelné. Některé věci dávají smysl z logiky věci, např. žádná podpora consistency levelů. Správný emulátor by dle mého názoru měl simulovat pouze API a neměl by vůbec obsahovat žádný sdílený kód s emulovanou věcí.

Nejzvláštnější je toto:

The emulator is not a scalable service and it doesn’t support a large number of containers. When using the Azure Cosmos DB Emulator, by default, you can create up to 25 fixed size containers at 400 RU/s (only supported using Azure Cosmos DB SDKs), or 5 unlimited containers. For more information on how to change this value, see Set the PartitionCount value article.

https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator?tabs=ssl-netstd21#differences-between-the-emulator-and-the-cloud-service

Proboha proč? Proč mě ten emulátor omezuje na 25 kontejnerů s fixní propustností nebo jen 5 kontejnerů s neomezenou propustnosti? Proč nemůžu založit neomezené množství kontejnerů? Proč si nemůžu nastavit propustnost tak jak chci stejně jako v ostré verzi? Tohle je zrovna věc, kterou emulátor vůbec emulovat vůbec nemusí nebo jen volitelně. Vždyť i všechny ty kvóty lze support requestem rozšířit.

Okay, pokud to chci zvýšit, musím při zapnutí emulátoru nastavit ručně PartitionCount argument. Tohle už je technologický detail emulátoru, který nechci řešit a absolutně mě nezajímá. V cloudové verzi vůbec žádný PartitionCount neřešíte tak proč to musím řešit v emulátoru?

Nepredikovatelné HTTP 503 errory

Při vývoji C# aplikace náhodně dochází k HTTP 503 chybám. Na tohle prostě nevím, co říct víc. Když jsem zkusil přejít na ostrou, cloudovou verzi CosmosDB tak k těm chybám nedocházelo.

Nepoužitelnost pro automatizované integrační testy

V rámci integračního testu chci postavit kompletní infrastrukturu aplikace se všemi souvisejícími komponentami jako databáze, cache apod. a proti této simulaci chci postavit čistou databázi a nad touto databází provést nějaký test (toto má smysl pouze u projektů kde dává smysl koncept „čistého prostředí“).

U integračních testů je v pořádku, že nějakou dobu trvají. Nevadí mi, pokud integrační test běží vteřinu ale už mi vadí, pokud běží desítky vteřin. U CosmosDB jsem zjistil, že prostě opakované volání vytvoření databáze, vytvoření kontejneru a následně shození kontejneru a shození databáze se prostě nasčítává až na desítky vteřin a to jen při hrstce testů.

Vzhledem k těm limitacím musíte taky hodně myslet na to, že nad emulátorem prostě nelze pouštět integrační testy paralelně. CosmosDB se z toho úplně sesype, jak nějaká traumatizovaná žába a emulátor vám začne vracet HTTP 503 i při vysoce nastaveném PartitionCount. Když jsem všech těch 10 integračních testů v xUnitu hodil do stejné [Collection] tak, aby se spouštěly v sérii za sebou, tak vše fungovalo ale běželo to skoro minutu.

Přecházím na MongoDB

S CosmosDB mi došla trpělivost, hlavně tedy kvůli tomu emulátoru. Je to nástroj, na který se nedá spolehnout a pokud databázové prostředí, nad kterým chci pracovat, nemohu dobře provozovat zdarma a lokálně z emulátoru a jsem nucený řešit záležitosti emulátoru, který mi má život zjednodušit a ne ztížit, pokud se nemohu soustředit na vývoj aplikace a musím řešit technologické záležitosti emulátoru, pak je prostě načase jít jinam.

Rozhodl jsem se pro MongoDB konkrétně MongoDB Atlas protože chci PaaS a vážně se mi nechce se starat o žádná VMka.

Jak se nastartuje lokálně MongoDB?

docker run --name mongo -p 27017:27017 mongo

A funguje to.

A jak se k němu připojím?

new MongoClient("mongodb://localhost:27017");

A funguje to. A neřeším žádný hovadiny.

Jak rychle běží integrační testy? Rychle a paralelně, 20 integračních testů je za vteřinu hotových.

MongoDB stránkování pro C#

Následující kód jsem od někud ukradl a trochu modifikoval. Jedná se o extension metodu, kterou lze zavolat nad IMongoCollection<TDocument> a vrátí to výsledek typu ListResult<TDocument> (jeho definice je níže) který obsahuje:

  • data
  • celkový počet dokumentů (estimatedDocumentCount má rychlost O(1))
  • filtrovaný počet dokumentů
  • počet stránek

Nepodařilo se mi ale zjistit, jak sloučit ten estimatedDocumentCount s tím agregovaným výstupem. V tuto chvíli tato extension metoda volá MongoDB 2x: nejdřív to vrátí agregovaný výstup skrz aggregate a pak se k tomu připojí ten estimatedDocumentCount. Líbilo by se mi, kdyby to bylo jen 1 volání ale nepřišel jsem na to, jak ten estimatedDocumentCount do toho aggregate výstupu dostat.

public static async Task<ListResult<TDocument>> ListResultAsync<TDocument>(
        this IMongoCollection<TDocument> collection,
        int pageIndex = 0,
        int pageSize = 10,
        FilterDefinition<TDocument> filterDefinition = null,
        SortDefinition<TDocument> sortDefinition = null)
    {
        var countFacet = AggregateFacet.Create("count",
            PipelineDefinition<TDocument, AggregateCountResult>.Create(new[]
            {
                PipelineStageDefinitionBuilder.Count<TDocument>()
            }));

        var stages = new List<PipelineStageDefinition<TDocument, TDocument>>();
        if (sortDefinition != null)
            stages.Add(PipelineStageDefinitionBuilder.Sort(sortDefinition));
        stages.AddRange(new[]
        {
            PipelineStageDefinitionBuilder.Skip<TDocument>(pageIndex * pageSize),
            PipelineStageDefinitionBuilder.Limit<TDocument>(pageSize)
        });
        var dataFacet = AggregateFacet.Create("data", PipelineDefinition<TDocument, TDocument>.Create(stages));

        var aggregationQuery = collection.Aggregate();
        if (filterDefinition != null)
            aggregationQuery = aggregationQuery.Match(filterDefinition);

        var aggregation = await aggregationQuery.Facet(countFacet, dataFacet).ToListAsync();

        var count = aggregation.First()
            .Facets.First(x => x.Name == "count")
            .Output<AggregateCountResult>()
            .FirstOrDefault()
            .Count;

        var data = aggregation.First()
            .Facets.First(x => x.Name == "data")
            .Output<TDocument>();

        return new ListResult<TDocument>
        {
            Data = data,
            TotalRowsUnfiltered = await collection.EstimatedDocumentCountAsync(),
            TotalPagesFiltered = (int)Math.Ceiling((double)count / pageSize),
            TotalRowsFiltered = count
        };
    }

A zde je ten ListResult<TDocument>

public class ListResult<T>
{
    public long TotalRowsUnfiltered { get; set; }
    public long TotalRowsFiltered { get; set; }
    public int TotalPagesFiltered { get; set; }
    public IEnumerable<T> Data { get; set; }
}

Míra abstrakce

Z minulých dílů už víte, že programovací jazyk není nic jiného, než určitým domluveným kódem psaný text, který se pak skrz kompilátor převádí do strojového kódu.

Jedno důležité členění programovacích jazyků, které je dle mého důležité pochopit, je míra abstrakce.

Nízká nebo žádná míra abstrakce

Programovací jazyk, který má nízkou nebo téměř žádnou míru abstrakce je vlastně jen převlečené zádávání instrukcí pro procesor ale v trochu přehlednější formě, než po jednotlivých bitech.

Níže následuje ukázka (zdroj: Wikipedie) pro srovnání.

  • Vlevo je strojový kód funkce pro výpočet Fibonacciho čísla v x86 instrukční sadě. Pro zkrácení místo 32-bitového zápisu instrukce 10001011010101000010010000001000 jsou jednotlivé instrukce uvedeny v hexadecimální formě.
  • Vpravo je zápis té samé funkce v „assembly“ v x86. Assembly se nepovažuje úplně za konkrétní programovací jazyk, je to spíš čitelnější forma zápisu svázaná s konkrétní instrukční sadou. Tzn. assembly pro x86 se bude lišit od assembly pro ARM64.

Strojový kód (x86, výpočet Fibonacciho čísla)

8B542408
83FA0077
06B80000
0000C383
FA027706
B8010000
00C353BB
01000000
B9010000
008D0419
83FA0376
078BD989
C14AEBF1
5BC3

Assembly (x86, výpočet Fibonacciho čísla)

_fibonacci:
        movl $1, %eax
        xorl %ebx, %ebx
.fibonacci_loop:
        cmpl $1, %edi
        jbe .fibonacci_done
        movl %eax, %ecx
        addl %ebx, %eax
        movl %ecx, %ebx
        subl $1, %edi
        jmp .fibonacci_loop
.fibonacci_done:
        ret

Psaní v assembly vyžaduje, že se vyznáte v instrukční sadě konkrétního čipu/procesoru, nad kterým programujete. To abyste se nezbláznili ze psaní strojového kódu napřímo.

Tak se místo toho zblázníte v assembly.

Na příkladu tohoto jednoduchého prográmku to zas tak složité není. x86 procesory obsahují různé „registry“ mezi kterými přesouváte bity (movl) a provádíte nad nimi bitové operace (xorl, addl, subl) a zároveň skáčete mezi instrukcemi (jbe, jmp, ret). Jak konkrétně se v x86 pracuje je mimo rozsah tohoto návodu.

Teoreticky je to možné napsat v assembly komplikovanější software, ve kterém zobrazujete nějaké grafické prvky, čekáte na vstup z klávesnice/myši a podobně, prakticky je ale téměř nereálné to rozchodit v rozumném čase.

Další nevýhodou psaní kódu s nízkou mírou abstrakce je fakt, že váš kód je svázán nejenom s konkrétní instrukční sadou ale i s konkrétní verzí instrukční sady a někdy i s konkrétním modelem procesoru.

Výhody a nevýhody nízkoúrovňového programování

Výhody:

  • máte možnost využít naplno fyzické možnosti konkrétního čipu nebo procesoru. Váš software může být plně optimalizovaný a extrémně rychlý.

Nevýhody:

  • je to šíleně složité a pracné, komplexnější software je prakticky nemožné napsat v rozumném čase.
  • váš software poběží jen na konkrétní instrukční sadě, na konkrétním typu procesoru ale i na konkrétním modelu čipu/procesoru. To nevadí, pokud programujete software pro řadu nějakých robotů se stejným čipem, ale o tomto typu programování tento návod není.

Vysoká míra abstrakce

„Vysokoúrovňové“ programovací jazyky umožňují psát v kodu, který je blíže k přirozené řeči a zbavují nás závislosti na konkrétní instrukční sadě. Už nás nezajímají konkrétní instrukce pro procesor, protože kód, který napíšeme, přeloží do strojového kódu kompilátor.

Níže je ukázka funkce pro výpočet Fibonacciho čísla v programovacím jazyce C.

int fibonacci(int n)
{
    if (n <= 1)
        return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

Všimněte si, že ten program je podstatně kratší! Zabírá pouze 6 řádků textu zatímco v Assembly ten samý kód zabírá 14 řádků textu. Navíc v něm vidíte anglická slova jako „if“ nebo „return„. Zároveň se zde vyskytuje „int“ a z minulých kapitol byste už měli vědět, že „int“ reprezentuje celé 32 bitové číslo. (pozn.: v tuto chvíli není třeba kódu rozumět)

Tento kód se ale musí do strojového kódu zkompilovat pomocí kompilátoru. Přestože kompilátory jsou jedny z nejsložitějších programů, které na světě existují, tak nikdy nedokážou žádnou aplikaci zkompilovat opravdu optimálně (snad jen kromě velmi jednoduchých, ukázkových programů, jako ten výše).

V důsledku toho lze říct, že jakýkoliv software, který se postaví skrz zkompilovaný programovací jazyk, téměř nikdy nevyužívá 100% možnosti výkonu procesoru pro úlohu, kterou chce vykonat.

Ono to ve skutečnosti nevadí. Procesory jsou brutálně rychlé a moderní kompilátory jsou velmi dobře udělané. Zkušeného „běžného“ ajťáka vůbec nezajímá, jak vypadá strojový kód vygenerovaný kompilátorem. Celá pointa programovacího jazyka je, že chceme vyrábět software v kódu, který je mnohem přehlednější, srozumitelnější a intuitivnější, než psaní v assembly/strojovém kódu. V kódu, se kterým je jednodušší psát mnohem komplexnější software.

Výhody a nevýhody vysokoúrovňového programování

Výhody

  • V programovacím jazyce je možné napsat mnohem komplexnější software
  • Kompilátory podporují celou řadu instrukčních sad. Náš software je možné provozovat na více zařízeních.

Výhody

  • V programovacím jazyce je možné napsat mnohem komplexnější software
  • Kompilátory podporují celou řadu instrukčních sad. Náš software je možné provozovat na více zařízeních.

Shrnutí

  • Programovací jazyky se podle míry abstrakce dělí na nízkoúrovňové a vysokoúrovňové
  • Nízkoúrovňové jazyky jsou jen o něco čitelnější způsob, jak zapisovat instrukce přímo ve strojovém kódu.
  • Vysokoúrovňové jazyky jsou na strojovém kódu nezávislé. Jsou konstruovány hlavně tak, aby byly přehledné, intuitivní a aby se v nich dal psát komplexnější software.