Blog | O Doctrine 2 Best Practices

O Doctrine 2 Best Practices

Jakub Kulhán dával na Twitteru odkaz na slajdy o nejlepší praktikách v Doctrine.

Protože se o práci s databází zajímám, slajdy jsem si prošel. A některé praktiky mi přijdou dobré, některé věci bych řešil jinak. Určitě si projděte i ty slajdy, já reaguju jen na to, co mě dovedlo k zamyšlení.

Můj přístup je hodně ovlivněný tím, že dělám primárně v Clojure, že používám Postgres a že se snažím být efektivní.

OO-first

Tento přístup jsem sám dlouhodobě razil a zkoušel. Má výhodu v tom, že všechno napíšu jednotně – objektově – a okolí se přizpůsobí. Funguje to, má to dvě ale, na která narazím později.

Jsou to:

  • zbytečný kód
  • neefektivní využití

Entities should work without db

Tato myšlenka vychází z představy, že aplikace si drží svůj stav ve svých datových strukturách, je nezávislá a technicky, pokud by běžela stále a nepotřebovala by dotazovat žádná další data, by se mohla od db úplně odpojit.

Ono je pěkné udělat si všechno v aplikaci, jenže tím vyvstává několik problémů.

Během té doby, než si stihnu udělat svoje, se mi můžou stavy jednotlivých klientů rozjet. Čím delší dobu s db nemluvím, tím pravděpodobněji a tím víc.

Navíc (což je věc spíš pro funcionálního vývojáře) dělám aplikaci stavovou, ikdyby dovedla být bezstavová. To je v FP antipattern, ale to tu asi nebudu moc rozvíjet, Doctrine je OO-first.

The database is just saving things

Představte si, že najmete extrémně talentovaného vývojáře a pak ho necháte celé dny jen řadit soubory od největšího. Tak tohle je přístup, kdy z databáze uděláte jen hloupé uložiště.

Databáze není jen „saving things“. Vývojář si volí vyvážení toho, co chce mít v aplikaci a co v databázi. Chci mít triggery v aplikaci, nebo v databázi (já většinou v db)? Chci mít validaci v aplikaci nebo v databázi (většinou v aplikaci)? Reimplementace vlastností db v aplikaci sice obvykle zvedá horizontální škálovatelnost (kterou v ČR skoro nikdo nepotřebuje), ale za cenu výkonu, produktivity vývojáře a možností některých optimalizací.

Nevyužít dovedností databáze je nevyužitý potenciál. Snadno se vám může stát, že budete psát stovky řádek kódu tam, kde by se dalo zavolat jedno trochu chytřejší SQL. S pomocí WITH RECURSIVE můžu snadno procházet stromy a grafy přímo v databázi. Místo toho, abych na db pálil stovky dotazů, které práce s grafem často způsobí, pošlu jeden dotaz.

Design entities first

Nadesignovat nejdřív entity předpokládá, že vím, jak entity použiju.

Mě se ustálil úplně jiný přístup.

Do databáze dám data ve 3. normální formě. Do view dostávám data tak, jak je šablona chce a použije, žádná zbytečná data do šablony nejdou.

Opakují se mi datové struktury, které používám ve view.

Po čase, když se mi ty datové struktury ustálí, vytvořím z nich pojmenované entity. Do té doby jsou to jen amorfní pole. Entita je v mém aktuálním způsobu fungování definována použitím, ne daty samotnými.

Define mappings after designing the entities

Kdybych chtěl být tvrdý, řekl bych, že nemám žádné mappings. Není to úplná pravda. Mám mappings mezi tím, co je v databázi a tím, co potřebuje view nebo algoritmu, do kterého data jdou. Je to konverze, ke které by stejně došlo.

V žádném případě nemám nic jako mapování mezi databázovou strukturou a nějakými akademickými entitami, o jejichž použití ještě nemám nejmenší tušení.

ORM, která tohle dělají a vývojáře do toho nutí, tím plýtvají časem člověka i výkonem počítače. Když chci něco dát do AJAXu, tak ten response z pole serializuju rovnou do JSONu a žádný mezikrok z mapováním nemám.

Keep collections hidden in your entities

Jasně, je to logický krok, když chcete vytvořit čistě objektový svět, ve kterém není žádná databáze, nejsou primitivní typy, nic takového. Jen objekty o jejichž vnitřní implementaci nic nevíme.

Mě se ukázalo, že tahle akademická čistota obvykle vede ke zbytečné mezivrstvě, která mi nepřijde ekonomicky racionální.

Minimalistické řešení se podívá na použití a vrátí data, která pro daný účel potřebuju. U toho je minimum konverzí, které neslouží mému účelu. To je můj stávající přístup. Pokud vytahuju data, která potřebuju a pokud potřebuju kolekci, vrátím si kolekci a pracuju s kolekcí.

Entities should always be valid

S tím souhlasím. Jen na to koukám asi jinak, než autor. Mě z jeho přístupu přijde, že objekt sám má jen takové metody, které vždy končí validním stavem.

Protože já exponuju datové struktury ven, nechávám je volně měnit. V atomické změně musí být na konci i validační krok. Tzn. místo toho, abych měl metodu v objektu (čisté OOP), aplikuju existujícící funkce na datovou strukturu (FP přístup) a na konci měnící funkce zavolám validaci té entity a ona buď projde, nebo vyhodí výjimku.

Takový přístup mi snižuje duplikaci kódu, nemusím přenášet metody do objektu, místo toho znovupoužívám funkce, které jsem si napsal už dříve. Celkem to šetří čas, hlavně pokud se entity nějak podobají.

Favour immutable entities

Ano, samozřejmě. Jenže pokud vím, že nějaká data hodlám desetitisíckrát upravit, udělám je radši tranzientní (in-place editovatelné) a postarám se, aby mutace proběhla pouze v jednom threadu, ostatní thready můžou jen číst.

Čímž se dostáváme k další věci. Výhody immutable se ukážou v okamžiku, kdy píšete konkurenční paralelizovaný kód. To v PHP nijak moc nejde, možnosti jsou omezené a nikdo příčetný to nedělá. Takže ten ústřední benefit z toho není. V jiných jazycích, kde člověk provozuje více threadů, dává immutable daleko větší smysl. Takhle mi přijde, že autor skočil na populární trend a dává do PHP něco, co PHP nepotřebuje.

Avoid soft deletes

Příliš silné tvrzení.

Autor se dříve odkazoval na to, aby člověk zvážil event sourcing. Ano, když máte ES, nepotřebujete soft deletes. Jenže implementovat event sourcing je řádově těžší než soft deletes (každé je na něco jiného). Někdy jediné, co chcete, je definovat si k tabulce users view v_active_users, který se chytá na „deleted_at“ IS NULL. Vše ostatní je pak jednoduché. Své dotazy jen píšete proti danému view.

Soft deletes bych nepoužil tam, kde mi přibývá spousta řádek, které potom mizí. Moc často se mi nestává, že by to ale bylo čisté použití databáze. Většinou, když se tohle stane, někdo zneužívá databázi tam, kde má být nějaká fronta.

Eager loading is useless

Já si myslím, že to, co potřebujete, není lazy loading, ale async přístup k databázi. Lazy loading občas ušetří nějaká data. Prostě proto, že (jak jsem kritizoval už dříve) jsem si navymýšlel akademické entity obsahující data, která nepotřebuju. Tím, že je nenačítám, snižuju dopad toho, že jsem něco udělal neoptimálně už předtím.

V mém přístupu nemá lazy loading žádný význam. Z databáze mi lezou data, která potřebuju pro view, algoritmy a neobsahují nic navíc.

Pracuju s kurzorem do db, takže se načítají postupně, tímto lazy jsou, ale jinak, než je nejspíš myšleno v článku.

Mnohdy se ale vyplatí získat data asynchronně, zejméně pokud mám data číst z pomalého nebo nespolehlivého zdroje. Dám je načíst, zatím si udělám jiné věci a nakonec se podívám, jestli mi to už přišlo.

Závěr

Způsob, jak pracuju s databází teď, je hodně odlišný od mainstreamu, ale kód, který z toho leze, je krátký, udržovatelný, velmi výkonný.

Jaký je váš přístup k databázi?

Předejte zkušenosti i dalším a sdílejte tento článek!

Následující článek


Jiří Knesl
Business & IT konzultant

Jiří Knesl poprvé začal programovat v roce 1993. Od té doby, díky skvělým učitelům a později zákazníkům, měl možnost neustále růst v oboru vývoje webových aplikací a informačních systémů. v roce 2002 se přidal zájem o ekonomii a v roce 2006 o organizaci práce. Vším tím se konstantně profesně zabývá jak ve svém podnikání, tak i u zákazníků. Za posledních 5 let vydal na tato témata přes 400 článků.

Prohlédněte si moje reference

Mám zkušenosti z rozsáhlých projektů pro korporace, velké podniky, střední i malé firmy, ale i pro startupy v cloudu. Zvyšoval jsem jejich know-how, pomáhal nastavovat jejich organizační strukturu, byl lektorem a mentorem v náročných situacích. Podívejte se, jak vidí můj přínos samotní klienti.

Sledujte mé postřehy na sociálních sítích