Eelmisel nädalal kirjutasin kuid lubasin rääkida, kuidas seda osaliselt kasulikult kasutada. Selleks püüan analüüsida, kuidas seda malliosa projektides tavaliselt kasutatakse. Minimaalne vajalik meetodite komplekt repositooriumi jaoks:
<?php
interface PostRepository
{
public function getById($id): Post;
public function save(Post $post);
public function delete($id);
}Siiski, reaalses projektis, kui repositooriumide kasutamine on otsustatud, lisatakse neile sageli meetodeid seadmete valimistamiseks:
<?php
interface PostRepository
{
public function getById($id): Post;
public function save(Post $post);
public function delete($id);
public function getLastPosts();
public function getTopPosts();
public function getUserPosts($userId);
}Neid meetodeid võiks ellu viia Eloquent'i skoopide kaudu, kuid koormata isendusklasse enda valikukohustustega — ei ole parim idee ning selle kohustuse väljatõstmine repositooriumi klassidesse tundub loogiline. Kas see tõesti on nii? Ma jagasin selle liidese visuaalselt kaheks. Esimene osa meetoditest kasutatakse kirjaoperatsioonide jaoks.
Tavalised kirjutamise operatsioonid on:
- uue objekti konstruktsioon ja meetodi ehk PostRepository::save
- PostRepository::getById, tegevus koos olendiga ja pärandamine PostRepository::save
- meetodiga PostRepository::delete
Kirjaoperatsioonides ei ole valikumeetodeid kasutuses. Lugemisoperatsioonides aga kasutatakse ainult meetodeid get*. Kui lugeda Interface Segregation Principle (täht I ühes SOLID), siis on selge, et meie liides on liiga suur ja täidab vähemalt kahte erinevat kohustust. On aeg jagada see kaheks. Meetod getById on mõlemas vajalik, kuid rakenduste keerukuse kasvades on nende teostused erinevad. Seda näeme veidi hiljem. Kirjaosa kasutu olemusest kirjutasin eelmisel nädalal, seega unustan selle siin lihtsalt.
Kuid lugemisosa ei tundu sugugi kasutu, kuna isegi Eloquent'i puhul võib siin olla mitu rakendust. Kuidas nimetada klassi? Võib-olla ReadPostRepository, kuid mustriga Repository on see juba vähe seotud. Lihtsalt võib nimetada PostQueries:
<?php
interface PostQueries
{
public function getById($id): Post;
public function getLastPosts();
public function getTopPosts();
public function getUserPosts($userId);
}Selle rakendamine Eloquentiga on üsna lihtne:
limit(/*some limit*/)
->get();
}
/**
* @return Post[] | Collection
*/
public function getTopPosts()
{
return Post::orderBy('rating', 'desc')
->limit(/*some limit*/)
->get();
}
/**
* @param int $userId
* @return Post[] | Collection
*/
public function getUserPosts($userId)
{
return Post::whereUserId($userId)
->orderBy('created_at', 'desc')
->get();
}
}Liidese peab siduma rakendusega, näiteks classis AppServiceProvider:
app->bind(PostQueries::class,
EloquentPostQueries::class);
}
}See klass on juba kasulik. See täidab oma kohustuse, vabastades kas kontrollereid või olendi klassi. Kontrollers on seda võimalik kasutada järgmiselt:
$postQueries->getLastPosts(),
]);
}
} Meetod PostsController::lastPosts lihtsalt küsib endale mingit rakendust PostsQueries ja töötab selle kallal. Pakkujas sidusime selle klassi PostQueries EloquentPostQueries ja kontrollers täiendab seda klassi. Kujutame nüüd ette, et meie rakendus on muutunud väga populaarseks. Tuhanded kasutajad avavad minutis lehe viimaste postituste jaoks. Kõige populaarsemaid postitusi loetakse samuti väga sageli. Andmebaasid ei suuda selliste koormustega väga hästi hakkama saada, seetõttu kasutatakse standardset lahendust — vahemälu. Lisaks andmebaasile hoitakse andmete mingit koopia kindlates operatsioonides optimeeritud salvestuses —
Kujutame ette, et meie rakendus on saanud väga popiks. Tuhanded kasutajad avavad iga minuti jooksul lehe, kus on viimased postitused. Kõige populaarsemaid postitusi loetakse samuti väga tihti. Andmebaasid ei suuda selliste koormustega väga hästi toime tulla, mistõttu kasutatakse standardlahendust — vahemälu. Koos andmebaasiga hoitakse teatud operatsioonide jaoks optimeeritud salvestuses ka andmete koopiat — memcached või redis.
Vahemälu loogika on tavaliselt üsna lihtne, kuid selle rakendamine EloquentPostQueries'is ei ole kuigi õige (vähemalt Single Responsibility Principle). Tunduvalt loogilisem on kasutada mustrit Dekoraator ja rakendada vahemälu peamise tegevuse kaudu dekoreerimisega:
base = $base;
$this->cache = $cache;
}
/**
* @return Post[] | Collection
*/
public function getLastPosts()
{
return $this->cache->remember('last_posts',
self::LASTS_DURATION,
function(){
return $this->base->getLastPosts();
});
}
// teised meetodid on praktiliselt samad
}Ärge pöörake tähelepanu liidesele Repository konstruktoris. Arusaamatul põhjusel otsustati nii nimetada vahemälule liidese Laravelis.
Klass CachedPostQueries rakendab ainult vahemälu. $this->cache->remember kontrollib, kas sellist salvestust ei ole vahemälus ning kui ei ole, kutsub tagasi ja salvestab vahemälles tagastatud väärtuse. Jäänud on vaid see klass rakendusse integreerida. Me peame veenduma, et kõik klassid, mis rakenduses nõuavad liidese rakendamist PostQueries saaksid klassi eksemplari CachedPostQueries. Ent ise CachedPostQueries peab konstruktoris saama parametritena klassi ja kontrollers täiendab seda klassi., kuna ta ei saa töötada ilma "päris" rakenduseeta. Muudame AppServiceProvider:
app->bind(PostQueries::class,
CachedPostQueries::class);
$this->app->when(CachedPostQueries::class)
->needs(PostQueries::class)
->give(EloquentPostQueries::class);
}
}Kõik minu soovid on üsna loomulikult iseloomustatud pakkujas. Nii oleme rakendanud vahemällu salvestamist meie päringute jaoks, kirjutades vaid ühe klassi ja muutes konteineri konfiguratsiooni. Ülejäänud rakenduse kood ei ole muutunud.
Muidugi, täielik vahemällu salvestamine nõuab veel kehtetuks tegemist, et kustutatud artikkel ei jääks veebilehele veel mõneks ajaks, vaid eemaldataks kohe. Kuid need on juba pisiasjad.
Kokkuvõtteks: kasutasime mitte ühte, vaid lausa kahte mustrit. Muster Command Query Responsibility Segregation (CQRS) pakub täielikult eristada lugemis- ja kirjutamistegevused liidese tasemel. Tulin selle juurde läbi Interface Segregation Principle, mis näitab, et ma oskan mustrite ja põhimõtetega oskuslikult manipuleerida ning tuua ühe teisest välja nagu teoreemi 🙂 Loomulikult ei ole iga projekt vajab sellist abstraktsiooni entiteetide päringute jaoks, kuid jagan teiega nippe. Rakenduse arendamise alguses võib lihtsalt luua klassi PostQueries tavalise rakendusega Eloquenti kaudu:
<?php
final class PostQueries
{
public function getById($id): Post
{
return Post::findOrFail($id);
}
// teised meetodid
}Kui tekib vajadus vahemällu salvestada, võib hõlpsasti luua liidese (või abstraktse klassi) selle klassi asemel PostQueries, kopeeri tema rakendus klassi ja kontrollers täiendab seda klassi. ja minna tagasi skeemile, mida ma varem kirjeldasin. Ülejäänud rakenduse koodi ei ole vaja muuta.
Kõik need nipid klasside ja liideste osas Sõltuvuse sisestamine ja CQRS on põhjalikult kirjeldatud . Samuti seal on mõistatus, miks kõik minu klassid selle artikli näidetes on märgitud kui final.
Allikas: habr.com
