Především díky že jste si v tomto možná trochu hektickém předvánočním čase udělali čas na tento článek.
Díky diskuzními příspěvku jednoho z vás (díky kolego a zdravím ;-) ) jsem si uvědomil, jak lze dobře objasnit architekturu a celou myšlenku a filozofii PHP Jet. Mluvím hodně o modularitě, ale to může být dosti matoucí pojem, protože pojem „modul“ se vyskytuje v různých platformách a je chápan různě. Ovšem pojem microservices architecture to ve skutečnosti vystihuje mnohem lépe!
Ale pokud chcete, tak se nejprve mrkněte na zajímavé přednášku dvou kolegů o praktických zkušenostech s microservices: video 1 a video 2. Jeden naznačuje značnou až extrémní složitost, druhý kolega má rovnou skvělou sarkastickou přednášku – styl mi trochu připomíná Monty Python.
A proč tady na blogu nejprve brojím proti tomu, když je něco (zbytečně) složité a teď vytahuji dvě přednášky, které jsou o tom jak něco může být opravdu složité a přinášet komplikace? (viz hlavně 2. video) Protože ta myšlenka a architektura jako taková vůbec není špatná – naopak. Přijde mi to přirozené, ale nesmí se to hnát ad absurdum.
Pravděpodobně víte o co jde, ale pro pořádek stručně objasním, že jde o architekturu systému, kdy aplikaci netvoří monolit, ale různé služby, které mezi sebou komunikují a to pomocí různých API, front zpráv a událostí a tak dále. O úskalích, které to v praxi přináší mluví kolegové v těch přednáškách na základě jejich praktických zkušeností. Je to složité na infrastrukturu, deployment, řízení projektu a tak dále. Ale oni mají microservices architekturu ad absurdum – tedy každá mikro služba má třeba i svůj virtuální server, své API atd. Do tohoto extrému teď rozhodně nepůjdeme, ale inspirujeme tím nejlepším co na té filozofii je.
Ta architektura a myšlenka má řadu výhod. Například můžete zcela jasně někomu delegovat vývoj určité části aplikace / komponenty systému / části projektu určitému člověku nebo týmu. Lze se jasně určit kdo za co zodpovídá, kde má kdo pracovat a jak a především již jednou odvedená práce se dá ideálně mnohokrát použít. Rovněž je to dobré při revizích a testování. Pokud nemáte „šílenou“ infrastrukturu, tak je to vlastně dobré i na deploymennt, protože může být více zřejmé jaké komponenty projektu a proč a kdy je třeba nasadit do provozu (měnili jsme pouze modul X.Y, je jasné že budeme nasazovat ten a tak dále). A celkově je to dobré pro rozvoj aplikace, protože se jednotlivé komponenty mohou vyvíjet nezávisle – bez vlivu jedné na druhou. Pokud tedy nedojde k zásadní změně v celém systému, ale k tomu se také dostanu.
A přesně to vše umožňuje PHP Jet při jeho maximálním a správném použití. Nahlíží na aplikaci (lépe řečeno na projekt) vlastně jako na soubor mikroslužeb, nebo lépe řečeno mikroapliklací. S tím rozdílem, že tyto komponenty spolu komunikují přímo (ovšem na Jetu by bylo snadné implementovat i microservices systém v plném rozsahu se vším všudy – i s API, ale to teď nechme být) a jsou zasazené do společného rámce MVC systému bází a stránek a mezi komponentami systému je úzká vazba (třídy se spolu „baví“ přímo, ne přes API a nějaký aplikační protokol či front zpráv v rámci infrastruktury).
V PHP Jetu naopak nejsou žádné routy, žádné směrování URL na funkce a tak dále. Teda … Trochu kecám … Není to tak úplně pravda, ale pro teď to nekomplikujme – routy v Jet nejsou primární věc, ale věc podružná (týká se mikroaplikací / modulů samotných) – vysvětlím později. Prostě pro teď úplně zapomeňte na to, že logika aplikace je nějak přímo vázána na HTTP požadavek. Pro teď zapomeňte na něco jako ~/routes/web.php a na další podobné věci. To není v PHP Jet primární směr uvažování o aplikaci.
V PHP Jet je modulární vlastně vše. Vše je mikroslužba, nebo mikroaplikace. I když je Jet navržen jako jeden celek, tak je to celek poskládaný z „dílků“, nad kterými máte plnou kontrolu. A můžete využít již předpřipravené „dílky“, nebo si udělat své vlastní. Tedy velice snadno si můžete do firmy vyvinout moduly na to co děláte stále dokola a ty používat na X projektech a pouze je přizpůsobovat. Sami si na PHP Jet můžete udělat své CMS s jeho moduly, nebo e-shop řešení a tak dále a vše stále recyklovat (a postupně vylepšovat, chcete-li).
To se týká i „vnitřností“ Jetu, kde se k tomu všemu se přidává ještě známý pojem dependency injection, kontejnery a také fasády a továrny. Ale o tom si povíme někdy příště. Ovšem je důležité si uvědomit, že tak jak je „poskládána“ aplikace, tak je poskládán i Jet a není problém si udělat třeba vlastní implementaci stránek a bází a zachovat při tom architekturu i API systému. Ale o tom opravdu víc příště.
Pojďme si to ukázat názorně a to rovnou na zcela nové e-shop platformě vyvíjené na PHP Jet, protože to už je něco reálného a hlavně něco daleko komplexnějšího a lépe se na tom vystihne podstata myšlenky a celé architektury.
Každý e-shop má pochopitelně produktový katalog, který tvoří kategorie, parametriace, produkty a nad tím vším asi tak miliarda funkcí a „fičurek“. E-shop je také o objednávkách a jejich zpracování, rovněž o desítkách funkcí určených pro marketing, napojení na další systémy a tak dále. Z toho všeho si teď jako příklad vytáhnu pouze problematiku samotných produktů z produktového katalogu chystané platformy Jet Shop.
Jak říká kolega v jedné z přednášek na které odkazuji v úvodu článku, tak aplikace tvořená mikroslužbami má sice řadu více-méně nezávislých komponent, ale ať děláte co děláte, tak za tím vším je databáze. Ano, může to být databázový cluster z více „mašin“, ale stále je to prostě jedna databáze. Pro pořádek: Ve skutečnosti by to šlo udělat tak, že různé entity budou mít různé databáze. Ale nekomplikujme to.
Zde je tedy něco, co si dovolím nazvat základní entitou. Jedná se o obecnou třídu (třídy) představující danou entitu a její obecný model. Tedy to jaké vlastnosti (informace) entita má. V případě zboží například název, popis, ceny a mnoho dalších parametrů. Neméně důležitou součástí základní entity je obecná bussiness logika nad těmito daty a informacemi (v případě e-shopu a zboží například cenotvorba, či určení dostupnosti zboží a tak dále). Nic méně a nic více. Základní entita neřeší žádné věci vázané na konkrétní HTTP požadavek, žádnou konečnou formu zobrazení informací a tak dále. Řeší prostě obecnou práci s daty a informacemi a obecné algoritmy nad tím.
V případě Jet Shop je zboží reprezentováno hlavní třídou JetShop\Product, které je samozřejmě DataModel a která využívá další subentity jako je JetShop\Product_ShopData (informace vázané na konrkrétní shop a jeho lokalizaci – tedy název, popis, cena, sazba DPH a další), JetShop\Product_Parametr (parametry parametrizace) a tak dále.
Pochopitelně jsou vhodně využity i traity, protože obecná entita je sama o sobě obsáhlá. Tedy i základní entita je rozdělena na subentity a vlastně by se klidně dalo říct na další mikroslužby. Například logika cenotvorby je samostatný trait, logika dostupnosti další trait, práce s variantami další a tak dále.. Tedy i zde je aplikována logika mikroslužeb – tedy netvořit jeden megacelek, ale snažit se problém dělit na menší, přehledné, jednoznačné a snáze pochopitelné a udržitelné části.
Entita by se dala popisovat dlouze a obsáhle, ale to pro teď není účelem tohoto článku.
Když vytvořím tuto základní entitu, tak mám obecnou možnost přidávat produkty, mazat, editovat, validovat jejich data, mám implementovanou bussiness logiku cen (vím tedy kolik bude produkt stát v určité sitauci), umím určit jeho dostupnost (je skladem? a kdy skladem bude?) a tak dále. To vše pochopitelně z pohledu aplikace, ne z pohledu UI. A na základně základní entity (samotné poskládané z mikroslužeb) se již mohou stavět „mikroslužby“ a „mikroaplikace“ – tedy konkrétní operace, konkrétní UI a tak dále.
A pro pořádek: Tyto třídy jsou umístěné v adresáři k tomu určeném. Tedy ~/application/Classes/. To je adresář určený právě pro obecné entity, obecné třídy.
Díky základní entitě umí aplikace se zbožím obecně pracovat a teď je otázka co se se zbožím bude dít v reálném světě. To už řeší jednotlivé moduly / mikroaplikace / mirkoslužby:
Pracovníci e-shopu rozhodně budou potřebovat efektivní a mocný nástroj pro správu produktů. To existuje ve formě modulu Admin.Catalog.Products a už se to nedá nazvat mikroaplikace. Je to projekt sám o sobě a ad absurdum to může mít svého vlastního vývojáře a „vlastníka produktu“. Je to něco, na co uživatelé budou „koukat“ celé dny, protože je to jejich pracovní náplň. Tedy zde už není tak důležité datové modelování a bussiness logika, ale UI/UX a samozřejmě také rychlost aplikaci, rovněž i rychlost implementace toho co uživatelé – pracovníci potřebují.
Zboží je pochopitelně nutné exportovat na vše možné od Heureky, přes Google a Seznam až po všemožné další služby. K tomu slouží moduly Exports.*. Každý typ exportu je samostatná mikroaplikace, která data ze základní entity transformuje do požadovaného datového formátu (zpravidla XML). A opět může být někdo, kdo má tuto oblast na starosti, plně se stará o své má jasně dané pole působnosti.
Exporty vůbec nemusí být aplikace reagující ha HTTP požadavek, ale CLI aplikace.
Pochopitelně nesmí chybět napojení na různá tržiště. Toto napojení se stará o aktualizaci nabídky produktů, skladové dostupnosti a tak dále.
Zpravidla jde o několik modulů. Některé jsou nástroje do administrace, některé CLI mikroaplikace zajišťující samotnou synchronizaci dat a tak dále.
A pochopitelně je třeba synchronizovat ceníky, skladové blokace a zásoby a tak dále … Opět další modul, či moduly a CLI mikroaplikace a opět samostatné téma.
Pochopitelně musí existovat „mikroaplikace“ určená pro zákazníky, pomocí které budou procházet nabídku e-shopu, filtrovat, zobrazovat a tak dále.
To pochopitelně není vše, ale účelem článku není popsat část platformy Jet Shop, ale vysvětlit, proč nahlížím na aplikace tak jak nahlížím a proč je PHP Jet takový jaký je.
Jak je z popisu patrné, tak takový projekt nesmí být monolit už z principu. Respektive nedokážu si představit, že bych takový projekt realizoval jinak než s pomocí modulárního uvažování – architektury a technologie, které je tato filozofie vlastní a klade na ní důraz.
A zároveň je patrné jaké to má výhody. Představme si teď, že na projektu pracuje opravdu velká firma, která má možnost na každou z komponent delegovat vlastní mikrotým lidí. Každý ten mikrotým se může věnovat své práci a má jasně dané kde se v projektu pohybuje. V rámci PHP Jet je to dané tím, že je aplikace ideálně rozdělená na moduly mající své adresáře. Jeden mikrotým se nebude plést do práce mikrotýmu druhému. Když se mění jedna komponenta, neovlivňuje to komponentu druhou. Na jakých to poběží URL a na jaké HTTP požadavky to bude reagovat? To v tomto kontextu vůbec není důležité. I to je zapouzdřené v modulech a MVC. Prostě práce je dobře izolována a jasně rozdělena na všech úrovních.
Ale co když se má změnit něco obecného? Třeba přijde nová cenotvorba? Konkrétně: Co se bude dít až nějaká země bude přecházet z národní měny na EURa? Ponechme si pro názornost představu, že každá komponenta má svůj mikrotým vývojářů. Změna tak podstatné věci jako je například zmíněná změna cenotvorby v souvislosti se změnou měny vlastně znamená změnu všeho – nebo minimálně analýzu dopadů a testování. Ale ta změna musí opět začít u základní entity. A je to něco, co donutí v takové situaci mikrotými vývojářů spolu komunikovat. Projekt se v takovém případě za účelem vyřešení problému změní přirozeně (a dočasně) z izolovaných mikroprojektů v projekt spojený, kde spolu prostě všichni musí mluvit a musí si vše ujasnit. A správná komunikace je základ úspěchu! Další výhodou je, že k implementaci takových změn prostě musí dojít nejprve na jednom místě – v třídách základní entity. Tím je přirozeně zařízeno, že v týmu dohodnutá změna bude implementována jednotně a jednoznačně, s přihlédnutím k potřebám všech „mikroslužeb“ / „mikroaplikací“ a bude také všemi zkontrolována a prověřena. Suma sumárum i při tak náročné změně je přirozeně jasné kde změna začíná, proč a jak se to děje a jaký má být postup implementace změn. Architektura systému tedy determinuje postup vývoje a ten je pak přirozený, efektivní a více hladký (pochopitelně pokud to všichni správně pochopí a šéf to dobře uřídí).
Pryč s teoretizováním o Jet Shopu, na který si (zatím) nemůžete sáhnout. V ukázková aplikaci se kterou se PHP Jet distribuován najdete totéž, pouze v daleko menším měřítku. Mimo jiné ukázková aplikace obsahuje malý systém pro psaní článků a na ten si můžete „sáhnout“ okamžitě.
To zahrnuje následující:
Všimněte si prosím, že vůbec neřeším na jakých URL jsou jednotlivé komponenty. Ne že by to nebylo důležité, ale pro teď to nehrálo vůbec žádnou roli. Struktura aplikace z hlediska URL adres a navigace je také důležitá, ale je to samostatné téma. To jestli jsou články vypsány na URL X, nebo URL Y, nebo kde je REST API, kde je administrace …, to nemá co do činění se základní logikou aplikace. Tedy nesouvisí to s tím jak se operuje s daty a informacemi a logikou a algoritmy nad tím. Ale jak jsem již řekl, tak struktura a navigace online aplikace je věc důležitá, ale je to samostatné téma, které s modularitou systému spolupracuje – ovšem nic nediktuje. Pojďme se na to kouknout.
Pro jednoduchost teď zůstaňme u práce s články v ukázkové aplikaci.
Kontrolní otázka: Hraje pro aplikační logiku nějakou roli, zda mám zobrazování článků na URL https://jet.lc/clanky/, nebo https://jet.lc/en/articles/, nebo se rozhodnu vytvořit si blog na adrese https://jet.lc/blog/?
Nehraje, proč by to také mělo hrát nějakou roli? Proč by ta URL vůbec měla být svázána s aplikační logikou? Vždyť by mě to pouze omezovalo … Modul / mikroaplikace Content.Articles.Browser funguje stále stejně ať je na jakékoliv URL. Stejně jako TODO list, který jsem vytvářel jako ukázku v minulém článku. Ano, modul se může přizpůsobit tomu na jaké stránce zrovna je umístěn a podle řady kritérií (např podle parametrů stránky – viz minulý článek), ale to už je čistě věc toho jak je modul implementován. Ale to jestli je základní URL kde modul je taková či onaká z pohledu modulu vůbec nehraje roli.
Myšlenka je následující: Jet MVC má dvě základní entity: bázi a stránku a pro úplnost dodejme, že v tom hraje i roli lokalizace, ale o tom si povíme víc příště.
Báze je základní entita. Ta určuje na jaké základní URL je například administrace, veřejná část, servisní skripty a další hlavní oblasti online aplikace. Bází může být libovolné množství, podle toho na kolik základních celků je projekt rozdělen. Báze také určuje to, jak se bude v dané části aplikace / projektu například bude chovat autentizace a autorizace (a další systémové komponenty) a to díky tomu, že při inicializaci vloží závislosti do kontejnerů v podobě fasád (budu psát příště, jak jsem již slíbil). Tedy přihlašování do administrace bude operovat s úplně jinou skupinou uživatelů a může se fakticky chovat zcela odlišně než se chová autentizace a autorizace REST API. Ovšem aplikace komunikuje s autentizací a autorizací stále přes stejné API. Ale to už odbočuji jinam :-) Prostě: Báze určuje, že na URL XYZ se nalézá ta a ta část projektu (web, administrace, API a tak dále) a že se ta část projektu v obecných věcech chová určitým způsobem.
Stránka je již „něco“ na nějaké URL. Tedy opravdu prostě stránka. Každá stránka spadá pod určitou bázi, opět nese řadu metainformací (například jaký má mít základní titulek, popis v navigaci, ale třeba i ikonu, pokud je třeba a tak dále) a stránka má pochopitelně především obsah. Krom toho, že obsah stránky může být v základní situaci jednoduše statický, tak stránka na sebe primárně váže moduly, tedy mirkoaplikace.
Pochopitelně v tom hraje podstatnou roli i fakt, že stránka má nějaké rozvržení / layout což není nic jiného než view skript a layoutů si můžete vytvořit kolik chcete a potřebujete. Najdete je například v adresáři ~/application/bases/web/layouts. Každá báze má svou sadu layoutů. Tedy báze admin má své layouty v adresáři ~/application/bases/admin/layouts a tak dále. Pochopitelně si můžete vytvářet báze jak chcete a kolik chcete a to samé platí o layoutech. Specifikem layoutů oproti běžným view skriptům je fakt, že mohou (respektive mají) definovat pozice na které se následně umisťuje další obsah a také to, že jsou schopny rovnou vložit určité obecné moduly. To znamená, že třeba o drobečkovou navigaci a její zobrazení se také stará modul, který však rovnou určí layout. A takových modulů může layout vložit kolik potřebuje, stejně jako je možné definovat tolik pozic pro obsah kolik je třeba.
Zpět ke stránce. Stránka na sebe tedy váže layout a také moduly. Stránka určuje, že na ní „poběží“ ten a ten modul – mikroaplikace a že výstup bude umístěn na určitou pozici v layoutu.
Tedy v Jet Studiu si snadno naklikáte stránku. Nebo její definici vytvoříte ručně – Jet Studio není povinné používat! A na stránku „pověsíte“ moduly, které umístíte na jednu z pozic, které nabízí layout stránky. Nic víc na tom není. Je to takto primitivní.
Systém Jet MVC a báze a stránky řeší pořádně a důkladně to co řešit má. Tedy strukturu aplikace, navigaci v rámci aplikaci (včetně tvorby odkazů), nese celou řadu meta informací a to vše je možné díky továrnám rozšířit či reimplementovat se zachováním základního API a tedy funkčnosti celého systému (a už zase odbočuji ;-) – o tom příště …)
Ovšem nějaké routy? Pojmenované routy? Routy v odkazech? Mega-definice rout (pro velký projekt) v nějakém souboru? Ne … Pracuje se s metainformacemi bází a stránek (i při vytváření URL) a nemá to přímý vliv na aplikační logiku. Protože je to prostě stále web, ale web poskládaný z mikroaplikací.
Ale ano, routy v Jet jsou ( k tomu se záhy konečně dostanu ), ale je to vnitřní věc modulů – mikroaplikací. V žádném případě to není globální záležitost, žádný adresář routes prostě nehledejte. Místo něj jsou tu adresáře „application/bases“ a v podadresáře bází adresáře „pages“, ale to vše primárně determinuje navigační uspořádání celého projektu a pochopitelně také to, kde jaký modul „poběží“.
Tak a teď jak je to s těmi routami v PHP Jet co nejsou a vlastně jsou:
Tedy: Ano, bez routování by to nešlo a hned si ukážeme jak je v PHP Jet toto řešeno. Ale určitě není žádný důvod aby se routovací mechanismus výpisu článků (například) míchal s routovacím mechanismem čehokoliv jiného - a to jakkoliv, ani pomocí group a podobných záležitostí – pro mé projekty takový přístup prostě není ideální.
Pojďme mrknout jak to PHP Jet řeší konkrétně.
Základní princip je jednoduchý. Každý kontroler modulu dědí od třídy Jet\MVC_Controller (případně od některého ze specializovaných potomků jako je například Jet\MVC_Controller_REST). Každý kontroler má metody resolve() : bool|string a právě ta modulu slouží k jeho vlastnímu routování.
Funguje to tak, že Jet MVC určí o jakou se jedná bázi, o jakou se jedná stránku a jaké jsou na stránce moduly a tedy i kontrolery modulů. (Celé je to velice optimalizovaný a rychlý proces.) Před tím než Jet jednotlivé kontrolery modulů „zavolá“ (tedy zavolá jejich konkrétní metody představující požadovanou akci), tak nejprve zavolá právě onu metodu resolve(). Pokud tuto metodu implementujete, tak v ní máte možnost implementovat libovolný rozhodující algoritmus. Můžete rozhodnout na základě čehokoliv co se stane – jaká metoda kontroleru (tedy akce) bude zavolána.
Metoda resolve() má návratovou hodnotu bool, nebo string. To znamená následující:
Stále platí, že základem je metoda resolv(), kde je možné implementovat libovolný rozhodovací algoritmus. Ale v praxi je implementace takových mechanismů neustále dokola stále to samé – tedy pořádná otrava. Proto přichází na řadu mikrorouter kontroleru. Ale pěkně popořadě.
Výchozí implementace metody resolve() má tuto podobu:
public function resolve(): bool|string { $router = $this->getControllerRouter(); if( !$router ) { return true; } return $router->resolve(); }
Tedy kontroler ve výchozím stavu předpokládá, že metoda getControllerRouter() může vrátit instanci routeru a ten pak provede vyhodnocení.
Router je instance třídy Jet\MVC_Controller_Router což je tak zvaný mikrorouter kontroleru.
Ve své podstatě je tento mikrorouter opravdu jednoduché. Nedefinují se v něm a priory typy a URL HTTP požadavků – ne, na to opět v kontextu Jet prosím zapomeňte. Mikrorouteru kontroleru definujete primárně akci kontroleru a na ní navážete rozhodovací mechanismus.
Ukažme si to konrkétně na ukázkové aplikaci:
Modul Content.Articles.Browser, a jeho hlavní kontroler (třída JetApplicationModule\Content\Articles\Browser\Controller_Main) má akci „detail“ pro zobrazení detailu článku, tedy metodu detail_Action(): void.
Každý článek má svou URL odvozenou od titulku.
Tedy například: https://jet.lc/clanky/konecne-budou-bile-vanoce.html
Část URL „https://jet.lc/“ určuje bázi (tedy zde bázi web), část URL „clanky/“ určuje stránku na které je modul Content.Articles.Brwoser. A kontroler tohoto modulu / mikroaplikace musí ověřit, zda je to opravdu URL nějakého „rozkliknutého“ článku. Pochopitelně URL báze a stránky modul již nezajímá – o to se postaral Jet. Kontorler a jeho router se tedy bude zajímat o zbytek URL, to jest o „konecne-budou-bile-vanoce.html“ a pokud je to opravdu URL článku, tak jej zobrazí. Pokud ne, je to neplatná URL a s tím už opět naloží Jet.
Cílem tedy je URL článku (pokud je platná) namapovat na metodu:
JetApplicationModule\Content\Articles\Browser\Controller_Main->detail_Action()
Ovšem namapování akce proběhne již s použitím mikrouteru kontoleru. Proto v uvedené třídě najdete metodu getControllerRouter(), která má (ve zkrácené verzi) toto podobu:
public function getControllerRouter(): MVC_Controller_Router { if( !$this->router ) { $this->router = new MVC_Controller_Router( $this ); $path = MVC::getRouter()->getUrlPath(); $this->router->addAction( 'list' ) ->setResolver( function() use ( $path ) { ..... } ); $this->router->addAction( 'detail' ) ->setResolver( function() use ( $path ) { if( $path == '' ) { return false; } $current_article = Content_Article::resolveArticleByURL( $path, MVC::getLocale() ); if( !$current_article ) { return false; } $this->article = $current_article; MVC::getRouter()->setUsedUrlPath( $path ); MVC::getPage()->setCacheContext($current_article->getId()); return true; } ); } return $this->router; }
Teď si to trochu rozeberme:
O mikrorouteru kontroleru by se toho dalo napsat ještě víc, ale to bych zde suploval dokumentaci.
Ano, v PHP Jet jsou routy, ale nahlíží se na ně jinak:
Doufám, že se mi dnes podařilo trochu více přiblížit jak je PHP Jet navržen a proč tomu tak je.
Teď pár obecných věcí.
Tentokrát jsem nevytvářel žádné video – v tomto případě jsem k tomu neviděl důvod. Ale děkuji moc (nevím zda můžu jmenovat, ale kolega ví) za rady se zvukem a až příště budu video opět vytvářet, tak věřím, že to bude lepší.
Momentálně pro vás chystám další věc – nástroj pro jednoduchý deployment, který již mnoho let používáme, ale připravuji jeho zbrusu novou verzi určenou k uvedení na světlo světa. Samozřejmě je nástroj postavený plně na Jet. Deployer v dohledné době uveřejním a dám k dispozici a napíši o tom článek.
Dále zvažuji cesty, jak zřídit fórum. Každopádně pro zatím platí, že kdo má dotazy, může kontaktovat přímo mou maličkost e-mailem (viz stránky php-jet.net)
Zatím vám všem děkuji za pozornost, některým i za pomoc a přeji vám všem klidný předvánoční a vánoční čas! Brzy se opět ozvu.
Díky za další díl článku, a opět textový, paráda :-)
Přijde mi, že v první polovině v podstatě jen neortodoxně popisujete Loose Coupling (neboli "mikroservicy v jednom procesu") a MVC (controller zprostředkovává rozhraní s HTTP/CLI/okolním světem a model je ta, jak vy říkáte "business logika").
Mít jednotlivé části stránky mountovatelné na libovolný prefix, aniž by to musel řešit sám controller, je super věc, která mi třeba v Symfony docela chybí. V mém neexistujícím frameworku, který už mezitím trochu existuje ( https://github.com/kostislav/unicorn-todolist/ ), je tohle taky jedna ze základních vlastností.
Ale to routování a controllery mi v Jetu pořád přijdou jako spousta zbytečné práce navíc. Controller, který zobrazuje článek dle URL, by v mém ideálním světě prostě měl vypadat nějak takhle:
class ArticleController { public function __construct( private readonly ArticleRepository $articleRepository ) { } @GET("/{id}") public function showArticle(string $id): Response { $article = $this->articleRepository->getArticleByUrl($id); if ($article === null) { throw new NotFoundException(); } else { return Response::template('article', [ 'article' => $article ]); } } }
Nemá smysl se tvářit, že akce controlleru jsou vyvolané něčím jiným než HTTP požadavkem, controller je přesně rozhraní mezi HTTP a zbytkem aplikace, kde jinde než tady by se mělo řešit HTTP?
(A všechen ten mutable global state, který Jet vyžaduje, mě trochu děsí - http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/ )
P.S. Jaký je vlastně důvod pro velice svérázné použití namespaců ve stylu Jet\MVC_Controller_Router? Je to pouze z historických důvodů? Pokud byste někdy chtěl Jet distribuovat jako Composer balík, tak tohle možná bude trochu problém.
Oprava - @GET("/{id}") má samozřejmě být #[GET('/{id}')], nepřepnul jsem dostatečně z Javy :-) (a příspěvkek mi nejde editovat)
Jaký je vlastně důvod pro velice svérázné použití namespaců ve stylu Jet\MVC_Controller_Router? ... Pokud byste někdy chtěl Jet distribuovat jako Composer balík, tak tohle možná bude trochu problém.
V tomto případě to žádný problém nebude. Dá se classmap.
No, dá "se".. on by ho musel někdo (autor) vyrobit a udržovat, což by pro autora asi byl další důvod, proč Composer balíček nevyrobit. Co koukám, tak PSR-0 autoloader to nakonec nejspíš zvládne, ale že by to pro uživatele bylo nějak moc intuitivní, nevim nevim.
Rozdrobenost projektu na desítky nebo stovky namespaces, kdy potom existuje 15 tříd s generickými nejednoznačnými názvy ala "Client", "Request", "Response", "Adapter", "Manager", "Repository", "Factory" apod. v různých namespacech je do značné míry specifická pro PHP a často otravná - zejména když se smíchá mnoho composer balíků třetích stran dohromady (aka najít ten správný "Client" v seznamu mnoha namespace). Zvolená konvence je podle mě pragmatická - třídy nekolidují s cizími knihovnami (účel namespace), schizofrenie generických názvů tříd fakticky neumožňuje a zároveň jednoznačně určuje členění kódu do složek.
Můžete se zkusit nechat inspirovat zde: https://phpfashion.com/best-practices-pro-jmenne-prostory-v-php
Autor zvažoval podobná kritéria.
To je obecný problém - nejednoznačnost/nesrozumitelnost pojmenování čehokoli.
Ad článek - připadá mi, že bod 4 je tam částečně v rozporu s bodem 1 a hlavně jeho důsledek je ve větších projektech značný a zbytečný "voser".
"místo Nette\Http\HttpRequest raději Nette\Http\Request" - v praxi - chci vytvořit HTTP request. Pokud by se třída jmenovala "HttpRequest" (nebo "Http_Request"), vyskočí mi při psaní "new HttpR" buď okamžitě nebo budou vedle jasně odlišitelné položky (HttpResponse například) - potvrdím, IDE udělá import a mám hotovo. Pokud začnu psát "new Req", vyskočí mi všechny requesty ze všech NS (a všech composer balíků) a hledám, který je asi ten správný. Následně IDE udělá autoimport. Abych dostal kód do tvaru dle autora článku, musím se vrátit na začátek souboru, dohledat, kde se provádí import, ručně ho upravit (zkrátit), vrátit se zpátky na původní místo a ručně připsat poslední fragment namespace. A takto na to musím myslet já i každý člen týmu při každém pokusu o import. Pro tým je třeba samozřejmě nějak srozumitelně definovat pravidla, kdy dělat import a kdy řešit import z částečného namespace - každý člověk bude mít jiný cit pro to, co považuje za již srozumitelné a co ne.
Plus - u composer balíků třetích stran (a jejich composer dependencies a jejich composer dependecies ....) si jmenné konvence stejně vybírat nelze a generické "Request" třídy se začnou množit jak houby po dešti. :-)
Všimněte si, že vaše argumentace stojí na tom jak vám bude IDE našeptávat při psaní. To je nejméně důležité.
Ne "dá se", ale že se prostě použije volba classmap. Jedinou věc, kterou jsem chtěl uvést na pravou míru je, že to není problém. Ani pro autora, ani pro uživatele.
To, zda je to takto dělané hezké je jiná otázka, a k té se nevyjadřuji.
Přečteno 20 811×
Přečteno 18 665×
Přečteno 17 855×
Přečteno 17 610×
Přečteno 16 340×