
2016-09-24
MVVM - A Magento ütőkártyája
A Magento 2 megnövekedett erőforrásigényének ellenére gyorsabb oldal betöltődésre képes, mint elődje az MVC szerkezetre épített Magento 1. Az új Magento teljes egészében eldobta az MVC architektúrát és egy másik tervezési mintát implementált: az MVVM patternt. Az MVVM-nek köszönhetően új lehetőségek nyíltak meg a Magentoban, amelyek akár az ERP és mobil integrációk fejlesztési idejét is csökkenthetik.
Az MVVM a következő szint
Az MVVM a webes világban először a Node.js megjelenésével vált széles körben ismertté, majd PHP nyelven a Zend enterprise keretrendszere implementálta elsőként ezt a tervezési mintát. Az MVVM patternt fontosnak tartják, mivel nagy és komplex rendszerek tervezhetők vele. Más programozási nyelvekben különböző jogilag védett enterprise specifikációk egészítik ki az eredetileg elképzelt MVC hiányosságait. A PHP világában a mai napig a nagy keretrendszerk közül a Zend az egyetlen, amely egyszerre több tervezési mintát is támogat köztük az MVVM-et.
MVVM vs. MVC
Fontos tudni, hogy az MVVM tervezési minta nem újabb az MVC-nél. Az MVVM eredetileg asztali és mobil alkalmazásokban használt, széles körben elterjedt tervezési minta, míg az MVC-t általános webfejlesztés során használják. Az általános szó, ez esetben megkérdőjelezi ennek a mintának a Magentoban való létjogosultságát, hiszen a Magento számos, a PHP világában szokatlanul komplex megoldást alkalmaz.

Az MVC három fő rétegből épül fel: a Kontrollerből, a Modellből, és View-ból. A Kontrollert az URL határozza meg, amely betölti a modelleket és tovább adja őket a view-oknak. Anélkül, hogy belemennénk a részletekbe az MVC lényege, hogy egyetlen URL-hez rendel egy teljes oldalt. A teljes oldal betöltődése hosszú ideig is eltarthat, valamint az URL-ekre épített üzleti logika nehezen vihető át másik URL-re. Például az az URL, amely betölt egy termék listát a vásárló számára, nem alkalmas a termék lista betöltésére egy ERP számára, pedig ugyanarra a backend logikára lenne szükség mind két esetben.
Ezzel szemben az MVVM esemény orientált. Az URL-ek nem kontrollereket jelképeznek, valamint az üzleti logika szeparálásra kerül külön kliensre és szerver oldalra. Már a Magento 1-ben is voltak események, így csupán idő kérdése volt, hogy mikor tűnik el a kontroller központú MVC struktúra. Az MVVM-ben a modellek egy ún. View-Modellen keresztül kommunikálnak egymással. Nézzük, hogyan működik mindez a Magento 2-ben.

Magento 2 MVVM
Ha felhasználói szemmel nézzük, akkor azt látjuk, hogy maga az oldal gyorsabban betöltődik, illetve nem egyszerre, hanem részenként. Megeshet, hogy először a termék lista töltődik be, utána a kategória leírás. Ez az aszinkronitás. A leglátványosabban ez a folyamat a kosár oldalon látható, amikor a vásárló számára azonnal a legfontosabb adat töltődik be: a kosár tartalma. Ezt követően a Magento backend még dolgozik és azt számolja, hogy mennyibe kerülhet a szállítási díj és van e esetleg kedvezmény a kosárban, majd betölti az eredményeket a jobb oldali hasábba.
Ha kicsit tapasztaltabb felhasználói szemmel vizsgáljuk meg az eseményeket, akkor láthatjuk, hogy a böngésző ilyenkor rengeteg, akár ötven-száz URL-t hív meg, köztük HTML és Javascript fájlokat. Vannak olyan URL-ek, amelyek többször is meghívódnak - ilyenkor a Magento frontend a backendre vár.

Az aszinkronitásnak ez a megoldása drasztikusan növeli a HTTP kérések számát, így a Magento 2 használatára kizárólag nagy mennyiségű HTTP kérés kiszolgálásra alkalmas Nginx szerver javaslott.
Az MVVM egyik előnye, hogy könnyű aszinkron megoldásokat alkalmazni benne, így a Magentoban is hamarabb töltődhetnek be a fontos tartalmak. Például a termék lista fontos, hiszen a tartalma indexelésre kerül a Google-ben és a felhasználó is azonnal látni szeretné. Azonban a termék lista melletti kívánság lista már kevésbé fontos, hiszen a tartalma felhasználó függő, azaz nem kerül indexelésre a Google-ben, valamint a megjelenítése is több időbe kerülhet, ezért felesleges lenne a termék lista betöltődését visszatartani addig, amíg a backend összeállítja a kívánság listát. Míg a Magento 1-ben az egyik lekérdezés vár a másikra, addig Magento 2-ben a lekérdezések egy része úgy érkezik ki a frontendre, ahogyan az éppen befejeződött vagyis a műveletek kevesebb ponton blokkolják egymást, köszönhetően az aszinkronitásnak.
Az implementáció közelebbről
Ha közelebbről megvizsgáljuk a Magento 2 MVVM szerkezetét, akkor észrevehetjük, hogy a Magento 1-el ellentétben itt nincsenek kontrollerek. 2.0-tól kezdve a kontroller kizárólag egy névtér, amely actionöket tartalmaz. Az actionök első ránézésre hasonlítanak a kontrollerekhez, azonban egy actionhöz kizárólag egy URL tartozhat. Az action, ahogy a neve is utal rá, mindig egyetlen műveletet hajt végre, kizárólag az URL egyedi lekezelésére használatos osztály.
Viszont a Magento 2 MVVM még nem lenne valódi MVVM, ha minden esetben kellene actionöket implementálnunk. A Magentoban mint sok mindent, az actionöket is képes lekezelni a Magento Core magic rendszere, így elkerülve az actionök elbúrjánzását és kontrollerként való viselkedését. Itt jön képbe az egyik új réteg - a szerviz réteg.
Képzeljük el, hogy egy új modul megvalósítására van szükség. Egy olyan modult kell gyártanunk, amely a termékeket listázza látogatottság alapján. Ilyenkor a Magento 2 szabályait követve egy API interface-t kell írnunk, amelyben meghatározzuk, hogy később milyen függvényekre lesz szükség. A modellben pedig implementáljuk az API interface-t és megírjuk az előbbi függvényekhez tartozó lekérdezéseket. A Modell-API-Intreface-t úgy kell megírnunk, hogy az minden általunk támasztott igényt képes legyen kielégíteni az üzleti logika szintjén. Ha ezzel meg vagyunk, akkor már csak a hozzáféréseket kell definiálnunk, hogy a backend 100 százalékban kész legyen. Erre szolgál az api.xml, ahol egy általunk kitalált, nem létező actiont kell megadnunk és hozzá az API interface egyik függvényét. Ha az action útvonala V1-el kezdődik, akkor az URL-t a Magento 2 magic rendszere fogja lekezelni. A Magento magától megtalálja a modulunk api.xml fájljában az URL-t és a hozzá tartozó szervizet. A HTTP kérésben küldött változókat pedig automatikusan átalakítja PHP változókká és átadja őket a függvényünknek. A függvény visszatérési értéke - mivel szervizről van szó - egy PHP változó, amit a Magento HTTP válaszként küld vissza. Ha a kérés JSON alapú volt, akkor a Magento a választ is JSON-re alakítja át, ha XML kérést kapott, akkor XML választ ad.
Ezzel a megoldással teljesen kikerülnek a képből az ősi kontrollerek és az action implementációk. A frontend dönti el, hogy a JSON objektumokat a Magento mely részére rendereli ki. Erre a folyamatra szolgál a Magentoba épített függőségeket lekezelő RequireJS valamint az esemény orientációt és a sablon rendszert lekezelő KnockoutJS.
Tehát a frontend felépítését nagy részben maga a frontend végzi el a szerver helyett, míg a szerveren a folyamatok a szervizekkel kezdődnek, amelyek a fentebb kifejtett MVVM tervezési mintát használva, függetlenek a frontendről küldött HTTP változóktól. Vagyis a Magento 2-ben a frontend és a backend teljesen szeparálásra került egymástól, a két oldalt a Magento Core automatikusan hozza össze.
A Pénztár az MVVM koronája

A Magento 2-ben a pénztár egy üres oldal, amely a tartalmát REST API hívásokkal tölti fel a már fentebb említett módszerrel. A Magento 1-ben a pénztár egyetlen hatalmas HTML form volt, míg Magento 2-ben több formból áll össze és mindegyik form külön URL-re küld adatokat, ahol a form adatok - pl. számlázási adatok - sessionbe kerülnek tárolásra. Rendelés leadáskor már csak a rendelési kérelem kerül küldésre a szerver irányába, ahol a sessionből kiolvasott adatokból jön létre a rendelés. Így maga a rendelés leadása valamelyest gyorsabb lesz, illetve biztonságosabb, hiszen nehezebb kívülről adatokat injektálni magába a rendelésbe. Mivel minden szerviz külön URL-en elérhető, így a pénztár vagy a rendelés leadás könnyeben implementálható mobil alkalmazásokba vagy ERP rendszerekbe.
A folyamatosan fejlődő Magento 2 legmodernebb része a pénztár, amelynek a frontendje MUI-val (Magento User Interface) épül fel, backendje pedig kontrollerek és actionök helyett magic REST API kérésekkel kerül meghívásra.