Java javë shkrova , por premtova të tregoj se si mund të përdoret pjesërisht me dobi. Për këtë do të përpiqem të analizoj se si zakonisht përdoret ky shabllon në projekte. Grupi minimal i nevojshëm i metodave për regjistrimin:
<?php
interface PostRepository
{
public function getById($id): Post;
public function save(Post $post);
public function delete($id);
}Megjithatë, në projekte reale, nëse është vendosur të përdoren regjistrat, shpesh shtohen metoda për seleksionimin e të dhënave:
<?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);
}KĂ«to metoda mund tĂ« realizoheshin pĂ«rmes scopes Eloquent, por mbingarkimi i klasave tĂ« entiteteve me detyra pĂ«r seleksionimin e vetvetes â nuk Ă«shtĂ« njĂ« ide shumĂ« e mirĂ« dhe transferimi i kĂ«saj pĂ«rgjegjĂ«sie nĂ« klasat e regjistrave duket logjik. A Ă«shtĂ« kĂ«shtu? E kam ndarĂ« kĂ«tĂ« interfejs vizualisht nĂ« dy pjesĂ«. Pjesa e parĂ« e metodave do tĂ« pĂ«rdoret nĂ« operacione shkrimi.
Operacionet standarde të shkrimit janë:
- konstruktimi i një objekti të ri dhe thirrja e PostRepository::save
- PostRepository::getById, manipulimet me entitetin dhe thirrja e PostRepository::save
- thirrja PostRepository::delete
NĂ« operacionet e shkrimit nuk ka pĂ«rdorim tĂ« metodave tĂ« seleksionit. NĂ« operacionet e leximit, megjithatĂ« pĂ«rdoren vetĂ«m metodat get*. NĂ«se lexoni mbi Interface Segregation Principle (shkronja I nĂ« SOLID), atĂ«herĂ« do tĂ« kuptoni se interfejsi ynĂ« doli shumĂ« i madh dhe kryen tĂ« paktĂ«n dy pĂ«rgjegjĂ«si tĂ« ndryshme. ĂshtĂ« koha tĂ« ndahen nĂ« dy. Metoda getById nevojitet nĂ« tĂ« dyja, megjithatĂ« me ndĂ«rlikimin e aplikacionit realizimet e saj do tĂ« jenĂ« tĂ« ndryshme. KĂ«tĂ« do ta shohim pak mĂ« vonĂ«. PĂ«r pafunĂ«sinĂ« e pjesĂ«s shkrimore kam shkruar nĂ« artikullin e kaluar, kĂ«shtu qĂ« nĂ« kĂ«tĂ« unĂ« do ta harroj atĂ«.
Pjesa e leximit më duket e dobishme, pasi edhe për Eloquent mund të ketë disa realizime. Si të quhet klasa? Mund të quhet ReadPostRepository, por për shabllonin Repository atë ka marrë pak lidhje. Mundet thjesht PostQueries:
<?php
interface PostQueries
{
public function getById($id): Post;
public function getLastPosts();
public function getTopPosts();
public function getUserPosts($userId);
}Realizimi i tij me Eloquent është relativisht i thjeshtë:
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();
}
}Interfejsi duhet të lidhet me implementimin, për shembull në AppServiceProvider:
app->bind(PostQueries::class,
EloquentPostQueries::class);
}
}Ky klasë është tashmë e dobishme. Ai realizon përgjegjësinë e tij, duke lehtësuar kështu ose kontrollorët, ose klasën e entitetit. Në kontrollor mund të përdoret kështu:
$postQueries->getLastPosts(),
]);
}
} Sanitizer.replaceElementWithChildren() PostsController::lastPosts thjesht kërkon një implementim për veten e tij PostsQueries dhe punon me të. Në ofrues kemi lidhur PostQueries me klasën EloquentPostQueries dhe në kontrollor ky klasë do të vendoset.
Le të imagjinojmë që aplikacioni ynë është bërë shumë popullor. Mijëra përdorues në minutë hapin faqen me publikimet më të fundit. Publikimet më të njohura gjithashtu lexohen shumë shpesh. Bazat e të dhënave nuk e menaxhojnë mirë një ngarkesë të tillë, prandaj përdoren një zgjidhje standarde - keq. Përveç databazës, një Kopje e të dhënave ruhet në një magazinë të optimizuar për operacione të caktuara - memcached ose redis.
Logjika e keq-keshimit zakonisht nuk është aq e komplikuar, por ta implementosh atë në EloquentPostQueries nuk është shumë e drejtë (në çdo rast për shkak të Parimi i Përgjegjësisë së Vetme). Më natyrshëm është të përdorim modelin Dekorator dhe të realizojmë keq-keshimin si dekorim të veprimit kryesor:
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();
});
}
// metodat e tjera janë pothuajse të njëjta
}Mos i kushtoni vëmendje ndërfaqes Repository në ndihmësin. Për një arsye të panjohur, kështu vendosën ta quajnë ndërfaqen për ruajtjen në Laravel.
Klasa CachedPostQueries implementon vetëm ruajtjen. $this->cache->remember kontrollon nëse ky regjistrim është në cache dhe nëse nuk është, thërret callback dhe ruan vlerën e kthyer në cache. Na mbetet vetëm të integrojmë këtë klasë në aplikacion. Na nevojitet që të gjitha klasat që kërkojnë implementimin e ndërfaqes PostQueries të fillojnë të marrin një ekzemplar të klasës CachedPostQueries. Megjithatë, vetë CachedPostQueries si parametrin në konstruktori duhet të marrë klasën EloquentPostQueries, pasi nuk mund të punojë pa një implementim "të vërtetë". Ndryshojmë AppServiceProvider:
app->bind(PostQueries::class,
CachedPostQueries::class);
$this->app->when(CachedPostQueries::class)
->needs(PostQueries::class)
->give(EloquentPostQueries::class);
}
}Të gjitha dëshirat e mia përshkruhen mjaft natyrshëm në provajder. Kështu, ne implementuam ruajtjen për kërkesat tona duke shkruar vetëm një klasë dhe duke ndryshuar konfigurimin e enës. Kodi i aplikacionit të mbetur nuk është ndryshuar.
Sigurisht, për një implementim të plotë të ruajtjes është e nevojshme gjithashtu të implementohet invalidimi, në mënyrë që artikulli i fshirë të mos mbetet në faqen e internetit për një kohë të gjatë por të fshihet menjëherë. Por kjo është një gjë e vogël.
PĂ«rfundimi: ne pĂ«rdorĂ«m jo njĂ«, por dy shabllone. Shablloni Command Query Responsibility Segregation (CQRS) ofron tĂ« ndahet plotĂ«sisht operacionet e leximit dhe shkruarjes nĂ« nivelin e ndĂ«rfaqeve. UnĂ« arrita te ai pĂ«rmes Interface Segregation Principle, qĂ« tregon se unĂ« manovroj me shabllone dhe parime dhe nxjerr njĂ« nga tjetĂ«rin si njĂ« teoremĂ« đ Sigurisht, jo çdo projekt ka nevojĂ« pĂ«r njĂ« abstraksion tĂ« tillĂ« pĂ«r zgjedhjen e entiteteve, por do tĂ« ndaj me ju njĂ« hile. NĂ« fazĂ«n fillestare tĂ« zhvillimit tĂ« aplikacionit, thjesht mund tĂ« krijoni njĂ« klasĂ« PostQueries me njĂ« implementim tĂ« zakonshĂ«m pĂ«rmes Eloquent:
<?php
final class PostQueries
{
public function getById($id): Post
{
return Post::findOrFail($id);
}
// metoda të tjera
}Kur të lindë nevoja për ruajtje, me një lëvizje të lehtë mund të krijoni një ndërfaqe (ose klasë abstrakte) në vend të kësaj klase PostQueries, implementimin e saj ta kopjoni në klasën EloquentPostQueries dhe të kaloni në skemën që e kam përshkruar më parë. Kodi tjetër i aplikacionit nuk duhet të ndryshohet.
Të gjitha këto trukime me klasa, ndërfaqe, Dependency Injection dhe CQRS janë përshkruar në detaje në . Atje është edhe zgjidhja e enigmatike përse të gjitha klasat e mia në shembujt e këtij artikulli janë markuar si final.
Burimi: habr.com
