Hyödyllisiä tietovarastoja Eloquentilla?

Viime viikolla kirjoitin artikkeli Repository-mallin hyödyttömyydestä Eloquent-kokonaisuuksilleHän lupasi kuitenkin kertoa minulle, kuinka käyttää sitä osittain hyödykseen. Tätä varten yritän analysoida, kuinka tätä mallia yleensä käytetään projekteissa. Vähimmäisvaatimus arkiston menetelmät:

<?php
interface PostRepository
{
    public function getById($id): Post;
    public function save(Post $post);
    public function delete($id);
}

Kuitenkin todellisissa projekteissa, jos päätettiin käyttää arkistoja, niihin lisätään usein menetelmät tietueiden noutamiseksi:

<?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);
}

Nämä menetelmät voitaisiin toteuttaa Eloquent-sopeuksien kautta, mutta entiteettiluokkien ylikuormittaminen vastuulla itsensä hakeminen ei ole paras idea, ja tämän vastuun siirtäminen arkistoluokille näyttää loogiselta. Onko näin? Olen erityisesti jakanut tämän käyttöliittymän visuaalisesti kahteen osaan. Ensimmäistä osaa menetelmistä käytetään kirjoitusoperaatioissa.

Normaali kirjoitustoiminto on:

  • uuden kohteen rakentaminen ja haaste PostArkisto::tallenna
  • PostRepository::getById, kokonaisuuden manipulointi ja kutsuminen PostArkisto::tallenna
  • haaste PostRepository::delete

Kirjoitustoiminnot eivät käytä hakumenetelmiä. Lukuoperaatioissa käytetään vain get* -menetelmiä. Jos luet aiheesta Käyttöliittymän erotteluperiaate (kirje I в SOLID), silloin käy selväksi, että käyttöliittymämme on liian suuri ja suorittaa ainakin kaksi eri tehtävää. On aika jakaa se kahdella. Menetelmä getById on välttämätön molemmissa, mutta kun sovellus muuttuu monimutkaisemmaksi, sen toteutukset vaihtelevat. Näemme tämän vähän myöhemmin. Kirjoitin kirjoitusosan hyödyttömyydestä edellisessä artikkelissa, joten tässä unohdan sen yksinkertaisesti.

Read-osa ei minusta näytä niin hyödyttömältä, koska jopa Eloquentille voi olla useita toteutuksia. Mikä luokalle annetaan nimeksi? Voi ReadPostRepository, vaan malliin säilytyspaikka hänellä on jo vähän merkitystä. Voit vain PostQuerys:

<?php
interface PostQueries
{
    public function getById($id): Post;
    public function getLastPosts();
    public function getTopPosts();
    public function getUserPosts($userId);
}

Sen toteuttaminen Eloquentilla on melko yksinkertaista:

<?php
final class EloquentPostQueries implements PostQueries
{
    public function getById($id): Post
    {
        return Post::findOrFail($id);
    }

    /**
    * @return Post[] | Collection
    */
    public function getLastPosts()
    {
        return Post::orderBy('created_at', 'desc')
            ->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();
    }
}

Käyttöliittymä tulee liittää toteutukseen, esimerkiksi sisään AppServiceProvider:

<?php
final class AppServiceProvider extends ServiceProvider 
{
    public function register()
    {
        $this->app->bind(PostQueries::class, 
            EloquentPostQueries::class);
    }
}

Tämä luokka on jo hyödyllinen. Hän ymmärtää vastuunsa purkamalla joko valvojat tai kokonaisuusluokan. Ohjaimessa sitä voidaan käyttää seuraavasti:

<?php
final class PostsController extends Controller
{
    public function lastPosts(PostQueries $postQueries)
    {
        return view('posts.last', [
            'posts' => $postQueries->getLastPosts(),
        ]);
    }
} 

menetelmä PostsController::lastPosts pyydän vain toteutusta PostsQuerys ja toimii sen kanssa. Linkittämässämme palveluntarjoajassa PostQuerys luokan kanssa EloquentPostQueries ja tämä luokka korvataan ohjaimella.

Kuvittelemme, että sovelluksestamme on tullut erittäin suosittu. Tuhannet käyttäjät minuutissa avaavat uusimmat julkaisut sisältävän sivun. Suosituimpia julkaisuja luetaan myös usein. Tietokannat eivät käsittele tällaisia ​​kuormia kovin hyvin, joten ne käyttävät standardiratkaisua - välimuistia. Tietokannan lisäksi tietty datan tilannekuva tallennetaan tiettyjä toimintoja varten optimoituun tallennustilaan - memcached tai redis.

Välimuistilogiikka ei yleensä ole niin monimutkaista, mutta sen toteuttaminen EloquentPostQueriesissa ei ole kovin oikein (jos vain siksi Yhden vastuun periaate). On paljon luonnollisempaa käyttää mallia Sisustusarkkitehti ja käytä välimuistia päätoiminnon koristeena:

<?php
use IlluminateContractsCacheRepository;

final class CachedPostQueries implements PostQueries
{
    const LASTS_DURATION = 10;

    /** @var PostQueries */
    private $base;

    /** @var Repository */
    private $cache;

    public function __construct(
        PostQueries $base, Repository $cache) 
    {
        $this->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();
            });
    }

    // другие методы практически такие же
}

Ohita käyttöliittymä säilytyspaikka rakentajassa. Jostain tuntemattomasta syystä he päättivät nimetä Laravelin välimuistin käyttöliittymän tällä tavalla.

Luokka CachedPostQueries toteuttaa vain välimuistin. $this->cache->muista tarkistaa, onko tämä merkintä välimuistissa ja jos ei, soittaa takaisin ja kirjoittaa palautetun arvon välimuistiin. Jäljelle jää vain toteuttaa tämä luokka sovellukseen. Tarvitsemme kaikki luokat, jotka sovelluksessa pyytävät käyttöliittymän toteutusta PostQuerys alkoi saada esimerkkiä luokasta CachedPostQueries. Hän itse kuitenkin CachedPostQueries konstruktorin tulee saada luokka parametriksi EloquentPostQuerieskoska se ei voi toimia ilman "todellista" toteutusta. Muutamme AppServiceProvider:

<?php
final class AppServiceProvider extends ServiceProvider 
{
    public function register()
    {
        $this->app->bind(PostQueries::class, 
            CachedPostQueries::class);

        $this->app->when(CachedPostQueries::class)
            ->needs(PostQueries::class)
            ->give(EloquentPostQueries::class);
    }
}

Kaikki toiveeni on kuvattu luonnollisesti palveluntarjoajassa. Näin ollen toteutimme välimuistin pyynnöillemme vain kirjoittamalla yhden luokan ja muuttamalla säilön kokoonpanoa. Muu sovelluskoodi ei ole muuttunut.

Tietenkin välimuistin täysimääräiseksi toteuttamiseksi on myös tarpeen toteuttaa mitätöinti, jotta poistettu artikkeli ei roiku sivustolla jonkin aikaa, vaan se poistetaan välittömästi. Mutta nämä ovat pieniä asioita.

Bottom line: emme käyttäneet yhtä, vaan kahta mallia. Näyte Command Query Responsibility Segregation (CQRS) ehdottaa luku- ja kirjoitustoimintojen erottamista kokonaan toisistaan ​​käyttöliittymätasolla. Tulin hänen luokseen Käyttöliittymän erotteluperiaate, mikä viittaa siihen, että käsittelen taitavasti malleja ja periaatteita ja johdan niitä toisista teoreemana :) Tietenkään jokainen projekti ei tarvitse tällaista abstraktiota entiteettien valinnassa, mutta jaan tempun kanssasi. Sovelluksen alkuvaiheessa kehittämiseen, voit yksinkertaisesti luoda luokan PostQuerys tavallisella toteutuksella Eloquentin kautta:

<?php
final class PostQueries
{
    public function getById($id): Post
    {
        return Post::findOrFail($id);
    }

    // другие методы
}

Kun välimuistin tarvetta ilmenee, voit yksinkertaisella liikkeellä luoda käyttöliittymän (tai abstraktin luokan) tämän luokan tilalle PostQuerys, kopioi sen toteutus luokkaan EloquentPostQueries ja siirry aiemmin kuvaamaani järjestelmään. Sovelluskoodin muuta osaa ei tarvitse muuttaa.

Kaikki nämä temput luokilla, käyttöliittymillä, Riippuvuusinjektio и CQRS kuvattu yksityiskohtaisesti kohdassa kirjani "Monimutkaisten verkkosovellusten arkkitehtuuri". On myös ratkaisu arvoitukseen, miksi kaikki tämän artikkelin esimerkeissä olevat luokat on merkitty lopullisiksi.

Lähde: will.com

Lisää kommentti