A webes alkalmazásfejlesztések ma már nem csak egyszerűen céges honlapokat és hobby honlapokat jelentenek, hanem gyakran cégek működési infrastruktúrájuk egy részét vagy teljes egészét a webre költöztetik. Ez új kihívásokat jelent a fejlesztők részére.
- A megjelenítő mindig egy böngésző ( ez az úgynevezett vékony kliens)
- A webszerver szolgálja ki a kéréseket
- Az alkalmazásnak megfelelő sebességgel kell működnie (cache-ek alkalmazása, AJAX hívások alkalmazása)
- Biztonságos felhasználói beléptetés (session kezelés)
- az adatbevitel kliensoldali és szerveroldali ellenőrzése (validáció)
- A felhasználói felület logikusnak, egységes és egyértelmű legyen
- és még sok egyéb szempont...
A fenti szempontokat nem lehet hagyományos módon hatékonyan kiszolgálni, hanem meg kell tervezni és erre már jól bejáratott módszerek vannak.
Rétegelt architektúra
A webalkalmazások - ahogyan fent is írtam - olyan összetett kliens-szerver alkalmazások, amelyeknek sokféle szempontot kell egyidejűleg kiszolgálni. A bonyolultság miatt a fejlesztőknek olyan stratégiát kell követni, hogy a különböző típusú feladatokat különböző szoftver modulok oldják meg és a szoftverek együttműködése a modulok közötti kommunikációval valósul meg. az egyes modulokat rétegeknek (layer) hívjuk. Minden rétegnek megvan a maga feladata. Tipikus rétegződés:
Elnevezés | Feladat | Technológia |
---|---|---|
Megjelenítési réteg (Presentation layer) | A kliensben hozza létre a megjelenítendő kódot | Lehet kliens és szerveroldali kód is. |
↑ ↓ | ||
Üzleti logika (bussiness logic layer) |
| Általában szerver oldali kód. |
↑ ↓ | ||
Adatelérési réteg (Data layer) |
| Szerver oldali kód. Az adatbáziskezelő alkalmazás beletartozik általában. Lehet olyan rész, amely az Object Relation Model elvet követi |
A fenti rétegezésnek lehet olyan változata is, amikor a kódban vannak úgynevezett szolgáltatások (service), amelyeket a megjelenítéstől függetlenül elérhetővé akarunk tenni más alkalmazások számára. Ilyen lehet például egy internetes fizetési lehetőség, amikor a szolgáltatást elérhetővé tesszük valamilyen webalkalmazás számára. A megoldás jellemzői:
Nem egy adott kliensre fejlesztünk, hanem megfelelő mintákon keresztül elérhetővé tesszük a business layert.
Kevés, tömör függvényt (metódust) teszünk elérhetővé, hogy a külső fejlesztők egyszerűen tudják implementálni a kódot.
Kevés adatbázis hozzáférésre legyen szükség a használatához.
Általános kommunikációs protokollokat használunk (HTML GET, POST kérések, és standard válaszok: pl. JSON vagy XML stringekben a válaszok).
Tesztelés
A tesztelés a programozási feladatokban rendkívül fontos és elengedhetetlen. Az alábbiakban az egyes tesztelési formákat nézzük át röviden.
Egységtesztelés
A programozás során a fejlesztők először egységteszteket végeznek. Az egységteszt az teszteli, hogy egy-egy objektum vagy modul a megkívánt bemeneti paraméterekre a megfelelő kimeneti válaszokat adja. Egyszerű teszttel, általában a fejlesztő vagy a fejlesztésben részt vevő, a tesztelésért felelős fejlesztők írják azokat. az egységtesztekhez szükséges, hogy az egyes modulok specifikációja le legyen írva, stabil legyen és az esetleges változásokhoz a specifikációt is változtatni kell.
A tesztelő környezet általában automatizált és az egységtesztek lefutásakor hibanaplók keletkeznek, amelyek leírják, hogy melyik modul / függvény / metódus nem felelt meg a feltételeknek.
Ha az egységtesztek lefutnak hiba nélkül az azt jelenti, hogy külön-külön a függvények, metódusok az elvártaknak megfelelően viselkednek.
Integrációs tesztelés
Annak a tesztelése, hogy az alkalmazás különböző részei hogyan működnek együtt. Ellenőrzi az alkalmazás különböző moduljai közötti interfészeket és interakciókat. (Például egy böngészőben működő alkalmazás megfelelő kérésekre a megfelelő válaszokat kapja-e meg) Az integrációs tesztelés integrálja az alkalmazás részeit és egészként teszteli azokat. Az integrációs tesztelés az egységtesztelés után következik.
Ha egy alkalmazás különböző moduljai egységtesztelésen átmennek, akkor az integrációs teszt kideríti, hogy a hálózat, a hardver, az adatbázis közbeiktatásával minden jól működik-e.
Az integrációs tesztelés összetett, mert sokféle lehetőségre figyelni kell. Ez egy webalkalmazás esetén azt jelenti, hogy a kliensen minden lehetséges akciót elindítunk és vizsgáljuk, hogy a szerver a megfelelő válaszokat adja-e. Nagyon fontos, mert az megrendelő nem arra kíváncsi, hogy az egyes modulok jól működnek-e, hanem arra, hogy a rendszer jól működik-e.
Az integrációs tesztelés kimutathatja, hogy egyes funkciók nem az elvártak szerint működnek. Például lassú a hardver, az adatbázis kérések lassúak, a hálózati kiszolgálás lassú, stb. Kiderítheti a kódolási problémákat.
Integrációs tesztelés frontend esetén
A böngészőben lévő kód összes eseményét, akcióját le kell futtatni és megvizsgálni, hogy a szerver oldali válaszok megfelelő eredményt hoznak létre. Miket kell vizsgálni?
Megfelelő-e a böngésző az elvárásoknak
Megfelelő sebességű válaszok érkeznek-e a szervertől
Bejelentkezés, kijelentkezés, jogosultságkezelés
Minden űrlap tesztelése - (valid adatok legyenek csak, és a megfelelő kódolással, metódussal küldje a szervernek)
Minden AJAX hívás tesztelése (A megfelelő AJAX hívásra a megfelelő válasz érkezik-e)
Ha egy időben több kliens kapcsolódik a szerverhez, akkor az állapotok megfelelően változnak-e.
A felhasználóknak szóló hibaüzenetek megfelelőek-e
Integrációs tesztelés backend esetén
A rétegezett felépítésnek megfelelően a backendben az egyes rétegek közötti kapcsolatot kell tesztelni. ez alapesetben az üzleti logika és az adatelérési réteg kapcsolatát jelenti
Az adatelérési réteg által szolgáltatott adatok megfelelnek-e az üzleti logikának. Vagyis a kérésekre a megfelelő adatot kapja-e az üzleti logika, se többet-se kevesebbet.
Ha az üzleti logika és az adatelérési réteg más hardveren van, akkor a kettő közötti kapcsolat megfelelő-e? A párhuzamos elérések hatására nincsenek-e problémák.
Az üzleti logika és az adatelérési réteg kapcsolata optimális-e, sebesség, memóriafoglalás szempontjából.
az adatelérési réteg meghívása és jogosultságkezelés megfelelően működik-e
A frontend felől érkező kérések megfelelőek-e és azokat az üzleti logika megfelelően tudja átalakítani az adatelérési réteg felé kérésekre és a válaszokat is megfelelő formában tudja visszaküldeni a frontendnek.
A jogosultságkezelés megfelelően működik-e a frontend felől
SQL injection lehetséges-e
Megfelelő jogosultság nélkül adatok bevitele lehetséges-e?
Ha az alkalmazás a bussiness layer felett tartalmaz egy Service layert is, akkor további integrációs tesztek szükségesek.
A leírásoknak megfelelő adatokat fogadja-e service layer akkor azokat megfelelő módon továbbítja-e a bussiness layerbe
Ha nem megfelelő adatokat kap a service layer, akkor ő maga vagy a bussiness layer a megfelelő hibakódot adja--e vissza:
A service layer és bussiness layer kapcsolata elég gyors-e?
Körülbelül ezek azok a kérdések, amelyeket vizsgálni kell vagy lehet.
Frontend
Minden, ami a böngészőben fut. A böngészőben való megjelenítés a feladata.
Az alkalmazott technológia alapja szinte mindig HTML, CSS, Javascript, mivel a böngészők ezt a technológiát képesek kezelni. Ugyanakkor hagyományos módon az alapokból kiindulva gazdag frontend felületeket kialakítani meglehetősen nehézkes, sok melóval és hibalehetőségek tömkelegével jár. A világban sok olyan előregyártott szoftvercsomag létrejött, amelyek ezen a kérdésen segítenek, tehát bizonyos szabályok betartásával megírt library-ket használva viszonylag gazdag és jól használható felületeket lehet létrehozni.
Néhány komplex library:
Bootstrap (Elsősorban mobile felületekre optimalizált alkalmazások készítéséhez)
Angular.js (egy oldalas Applikációk létrehozására)
React.js (Enterprise szintű alkalmazások készítésére)
Vue.js (Keresztplatformos alkalmazások készítésére)
További Frameworkok leírása, tesztelése: https://technostacks.com/blog/best-javascript-frameworks
Egyszerűbb frontend library-k
jQuery - Egy Javascript könyvtár, amellyel eseménykezelést, CSS módosítást, AJAX hívásokat, HTML tartalom módosítást lehet egyszerűen és szabványosan végezni
Itt található még néhány felsorolása: https://hu.education-wiki.com/8862379-jquery-alternatives
A libraryk használata sokat segít a frontend alkalmazások fejlesztésében. A szükséges tulajdonságok felsorolása
Egyszerűen programozható legyen
Könnyű legyen a böngészőben változatos, de egységes kinézetű felhasználói interface-t készíteni. (pl. jQuery UI segítségével mindenfélét lehet csinálni)
Legyen lehetőség az adatok kliens oldali validálására (pl. a bevitt adat hossza, a bevitt adat típusa - numerikus / string -, milyen karaktereket tartalmaz, stb...)
AJAX hívásokat lehessen kezdeményezni és velük az oldal tartalmát és vagy kinézetét változtatni ( jQuery )
Lehetőség szerint minél kisebb overhead legyen (kevesebb kód, ami a böngészőbe átjön (jquery.min.js) használata
Cachelhető legyen a böngészőben a library kódja és az alkalmazás kódja is.
Multiplatformos megjelenítésre legyen alkalmas (Minden lényeges böngészőt támogasson és támogassa a mobil eszközöket, mivel ma már az internetes kliensek több, mint 50%.a mobil eszköz)
Templatek (sablonok) használata
Akár szerveroldali, akár kliensoldali frontend megoldásokat használ a fejlesztő szinte mindig sablonokat (template) használ a felület kialakításához.
A sablonok olyan, a HTML-hez hasonló kódot tartalmazó oldalak vagy oldal részletek, amelyekben úgynevezett helyőrzőket (placeholder) definiál a fejlesztő. Például az alábbi példában a helyőrző a {{yourName}} string.
<!doctype html>
<html ng-app>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
</head>
<body>
<div>
<label>Name:</label>
<input type="text" ng-model="yourName" placeholder="Enter a name here">
<hr>
<h1>Hello {{ yourName }}!</h1>
</div>
</body>
</html>
A helyőrzőbe a frontend alkalmazás valamelyik része a futáskor behelyettesíti az éppen aktuális stringet és a böngészőben már az aktuális érték - jelen esetben az aktuális név - jelenik meg. A fenti példát akár javascriptben is lehet kezelni például így:
var sablon = " a html sablon kódja...";
var placeholder = "{{yourName}}";
var actualname = "Gipsz Jakab";
var out = sablon.replace(placeholder, actualname);
Ebben a példában a Javascript kód kicseréli a sablon stringként beolvasott tartalmát és valamilyen módon kiteszi a weboldal egy részére. Az Angular.js ezt viszonylag egyszerű kóddal megoldja, amibe itt most nem megyek bele.
Backend
Minden, ami a szerver oldalon fut és nem a megjelenítéssel foglalkozik.
Általánosságban minden, ami a megjelenítési réteg alatt van az a Backend része.
ORM - Object Relation Model
Ha adatbázis-kezelőket használunk a tanulás során mindenkinek az SQL utasítások adatbázis szervernek való küldése jut eszébe és ez egyszerű alkalmazások (néhány tábla) megfelelő is lehet. A bonyolultabb, sok vagy folyamatos fejlesztést igénylő alkalmazások esetén, illetve ha a táblák számossága nagy célszerű azt az elvet követni, hogy a relációs adatbázis egyes tábláihoz, lekérdezéseihez objektumokat rendelünk. Az objektumok adattagjai, megfelelnek az adatbázis tábla vagy lekérdezés megfelelő mezőinek. Mivel az adatbázis mezőinek típusai gyakran nem illeszkednek az alkalmazott programozási nyelvhez (pl. a datetime erre jó példa), ezért a getter és setter függvények gondoskodnak arról, hogy az adatbázis adattípusának megfelelő típuskonverzióról.
Az ORM-et csakis objektum orientált programozási nyelven, objektumok segítségével lehet jól kivitelezni.
Az ORM előnyei, hátrányai
Előnyök
A forráskód elvonatkoztat az adatbázis szerkezetétől, leegyszerűsíti és felgyorsítja a fejlesztés folyamatát
Az adatbázis szerkezetének változása vagy akár más adatbázis-kezelőre való áttérés nem fogja a forráskódot lényegesen módosítani. Csak az adatbázis <=> objektumok kapcsolatát leíró - általában XML - fájlokat kell módosítani.
Az adatelérés biztonságos lesz, azaz nem kell gondoskodni a tranzakciókezelésről és az SQL injection ellen is véd
Hátrányok
Azért a kód növeli az overhead-et, vagyis nagyobb lesz a kód futás közben, ami leterhelt rendszer esetén számíthat. ezt a szerver oldali cache-elési módszerek csökkenthetik
Az adatelérési réteg vastagabb lesz, tehát teljesítményt csökkenthet
Meg kell tanulni a használatát, ami a fejlesztő idejéből elvesz
ORM a programozási nyelvekben
Már régóta léteznek ingyenesen elérhető stabil ORM megoldások a különböző OOP programozási nyelveken. Néhányat felsorolok
- Java: Hibernate
- . NET: Entity Framework
- PHP: Doctrine
- Python: Peewee
Egy példa a PHP alapú Doctrine ORM libraryból
Először fel kell térképezni az adatbázist és létre kell hozni a megfelelő osztályokat.
Az alábbi kód létrehozza a Termek táblának megfelelő osztályt.
<?php
// src/Termek.php
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="products")
*/
class Termek
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue
*/
private $id;
/**
* @ORM\Column(type="string")
*/
private $name;
// .. egyéb kódok
}
Az XML fájl írja le az adatbázis tábla és az osztály kapcsolatát
<!-- config/xml/Termek.dcm.xml -->
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Termek" table="termek">
<id name="id" type="integer">
<generator strategy="AUTO" />
</id>
<field name="name" type="string" />
</entity>
</doctrine-mapping>
Az ORM objektumainak elkészítésekor vagy a már említett getter és setter metódusokat használunk vagy pedig olyan metódusokat, amelyek a viselkedést írják le. Az alábbi példa egy PHP alapú, Doctrine nevű ORM mintapéldájából származik, ahol a metódusok a viselkedést írják le.
<?php
// create_termek.php <name>
require_once "bootstrap.php"; //Betöltődik a doctrine
//A parancssorból átvesszük a beszúrandó rekord nevét
$ujTermekNeve = $argv[1];
//Létrehozzuk az adatbázis tábla objektumát
$termek = new Termek();
//Beállítjuk az új rekord név mezőjét
$termekt->setName($ujTermekNeve);
//Utasítjuk az EntityManager segítségévvel a kódot, hogy új rekordot szúrunk be.
$entityManager->persist($termek);
//Itt végzi el valóban az új adatok átvitelét az adatbázisba
$entityManager->flush();
//
echo "Létrejöbb termék ID-je: " . $termek->getId() . "\n";
?>
A példában egy új terméket akarunk felvinni az adatbázisba. Parancssori alkalmazásról van szó. A parancssorból így hívjuk meg:
php create_termek.php ORM
php create_termek.php DBAL
Az átadott paraméter az "ORM" és a "DBAL", vagyis két sort írunk az adatbázisba.
A script futatása során betöltődik a Doctrine , majd átveszi a paramétert a parancssorból. Beállítja az új rekordban a név mezőt.
Beállítja az új rekord név mezőjét, majd a persist metódus segítségével közli, hogy itt egy INSERT fog lefutni, ezért a tranzakciókezelést állítsa be az adatbázisban. A flush() hatására fog valóban lefutni az insert és utána le lehet kérni az új rekord ID-jét.
Egy teljes lista lekérdezése így néz ki:
<?php
// list_termekek.php
require_once "bootstrap.php";
$termekLista = $entityManager->getRepository('Termek');
$termekek = $termekLista->findAll();
foreach ( $termekek as $termek ) {
echo sprintf("-%s\n", $termek->getName());
}
Itt található a példa további része: https://www.doctrine-project.org/projects/doctrine-orm/en/current/tutorials/getting-started.html
CRUD - Create, Read, Update, Delete
Az adatbázisokat használó alkalmazások egy-egy adatbázis táblával kapcsolatban tipikusan az alábbi feladatokat végzik el.
Feladat | SQL megfelelője |
---|---|
Create | INSERT |
Read | SELECT |
UPDATE | UPDATE |
DELETE | DELETE |
Az adatbázisokat fejlesztő alkalmazáscsomagok egy része képes arra, hogyha a modell rétegben definiáljuk a szükséges adatbázis táblákat, akkor a fejlesztő rendszer automatikusan legenerálja azt a kódot, amellyel a fenti tipikus adatbázis-kezelő műveletek elvégezhetők. Ezt nevezzük CRUD-nak. Ilyenek például:
PDO CRUD
phpGrid
Laravel
A CRUD application nem csak az adatbázis kezelő függvényeket írja meg, hanem azt a Frontend kódot is, amely táblázatos formában, űrlapokon keresztül képes kezelni az adatbázist.
REST
REST = Representation State Transfer. Ez egy elosztott szoftverfelépítés típus, amelynek lényege, hogy az egyes szoftverkomponensek egymással elosztva kommunikálnak. A legjobban a világháló felel meg ennek, hiszen a webalkalmazások backendje általában egy szerveren van, a frontend pedig egy böngésző kliensen és a HTTP protokollon keresztül szabványosan kommunikálnak.
A HTTP protokoll esetén a kliensek GET vagy POST hívásokat indítanak a szerver felé és a válaszokat JSON, XML, vagy string formájában kapják vissza.
RESTFull egy rendszer, ha az alábbi feltételeknek megfelel. A webalkalmazások megfelelnek az elveknek.
- Kliens-szerver architektúra (a kliensek a mi esetünkben a böngészők)
- Állapotmentes - A kérést fogadó szerver nem tárol adatot a kliensről. A mi esetünkben amikor egy kliens kérést küld a szervernek, az a küldött adatok alapján ki tudja szolgálni azt.
Megjegyzés: A sessionkezelés ellenére a webalkalmazások megfelelnek ennek a feltételnek, mert a session cookie a böngészőben tárolódik és a kéréskor elküldi a szervernek! - Gyorsítótárazás (Cache) - A böngészők a kérések alapján cache-elik az adatokat és előfordulhat, hogy egy kérést nem is küldi el valójában a szervernek, hanem cache-ből kiszolgálja
- Réteges felépítés - Feljebb már leírtuk a rétegeket.
- Egységes interface-ek.
- Az erőforrások azonosítása: Az erőforrásokat a webalkalmazások URI-k segítségével azonosítják
- Erőforrások manipulációja: Ha van jogosultsága a kliensnek, akkor a kéréseken keresztül módosítja a szerver oldali erőforrások állapotát
- Önleíró üzenetek - Az üzenetek elegendő információt tartalmaznak a z üzenet feldolgozásához. (Pl. a Coockie-kban a böngésző elküldi a média típusát és a szerver felismeri, hogy milyen média - mobiltelefon, asztali böngésző, stb.)
- Hipermédia - Az alkalmazás állapotának motorja. A kliensek csakis azokat az állapotokba kerülhetnek, amelyeket a szerver küld a válaszokban. (pl. A kliens AJAX hívásával JSON vagy XML válaszokat kap és az alkalmazás a válasz alapján módosítja az állapotot a kliensben)
Itt van egy jegyzet a kérdéskörről:Web architektúrák