พื้นที่เก็บข้อมูลที่มีประโยชน์กับ Eloquent?

สัปดาห์ที่แล้วฉันเขียน บทความเกี่ยวกับความไร้ประโยชน์ของเทมเพลต Repository สำหรับเอนทิตี Eloquentอย่างไรก็ตาม เขาสัญญาว่าจะบอกฉันว่าจะใช้บางส่วนให้เป็นประโยชน์ได้อย่างไร เมื่อต้องการทำเช่นนี้ ฉันจะพยายามวิเคราะห์ว่าเทมเพลตนี้มักใช้ในโครงการอย่างไร ชุดวิธีการขั้นต่ำที่ต้องการสำหรับพื้นที่เก็บข้อมูล:

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

อย่างไรก็ตาม ในโครงการจริง หากมีการตัดสินใจใช้พื้นที่เก็บข้อมูล มักจะเพิ่มวิธีการเรียกข้อมูลบันทึกเข้าไป:

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

วิธีการเหล่านี้สามารถนำไปใช้ผ่านขอบเขต Eloquent ได้ แต่การบรรทุกคลาสเอนทิตีมากเกินไปโดยมีความรับผิดชอบในการดึงข้อมูลออกมาเองนั้นไม่ใช่ความคิดที่ดีที่สุด และการย้ายความรับผิดชอบนี้ไปยังคลาสพื้นที่เก็บข้อมูลก็ดูสมเหตุสมผล เป็นอย่างนั้นเหรอ? ฉันแบ่งอินเทอร์เฟซนี้ออกเป็นสองส่วนด้วยสายตาโดยเฉพาะ ส่วนแรกของวิธีการจะถูกใช้ในการดำเนินการเขียน

การดำเนินการเขียนมาตรฐานคือ:

  • การสร้างวัตถุใหม่และความท้าทาย PostRepository::บันทึก
  • พื้นที่เก็บข้อมูลหลังโพสต์::getByIdการจัดการเอนทิตีและการเรียก PostRepository::บันทึก
  • ความท้าทาย PostRepository::ลบ

การดำเนินการเขียนไม่ได้ใช้วิธีการดึงข้อมูล ในการดำเนินการอ่าน จะใช้เฉพาะเมธอด get* เท่านั้น ถ้าอ่านเรื่อง หลักการแยกส่วนต่อประสาน (จดหมาย I в มูลฝอย) จะเห็นได้ชัดว่าอินเทอร์เฟซของเราใหญ่เกินไปและทำหน้าที่ที่แตกต่างกันอย่างน้อยสองประการ ถึงเวลาหารมันด้วยสอง วิธี getById เป็นสิ่งจำเป็นในทั้งสองอย่าง แต่เมื่อแอปพลิเคชันมีความซับซ้อนมากขึ้น การใช้งานก็จะแตกต่างออกไป เราจะเห็นสิ่งนี้ในภายหลัง ฉันเขียนเกี่ยวกับความไร้ประโยชน์ของส่วนการเขียนในบทความที่แล้ว ดังนั้นในบทความนี้ฉันจะลืมมันไป

ดูเหมือนว่าส่วน Read จะไม่ไร้ประโยชน์สำหรับฉัน เนื่องจากแม้แต่ Eloquent ก็อาจมีการใช้งานหลายอย่างที่นี่ จะตั้งชื่อชั้นเรียนว่าอะไร? สามารถ อ่าน PostRepositoryแต่ไปที่เทมเพลต กรุ เขามีความเกี่ยวข้องเพียงเล็กน้อยแล้ว คุณสามารถเพียงแค่ โพสต์แบบสอบถาม:

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

การใช้งานกับ Eloquent นั้นค่อนข้างง่าย:

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

อินเทอร์เฟซจะต้องเชื่อมโยงกับการใช้งาน เช่น ใน AppServiceProvider:

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

ชั้นเรียนนี้มีประโยชน์อยู่แล้ว เขาตระหนักถึงความรับผิดชอบของเขาโดยการบรรเทาทั้งผู้ควบคุมหรือคลาสเอนทิตี ในคอนโทรลเลอร์สามารถใช้งานได้ดังนี้:

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

วิธี PostController::lastPosts แค่ขอนำไปปฏิบัติบ้าง กระทู้สอบถาม และทำงานร่วมกับมัน ในผู้ให้บริการที่เราเชื่อมโยง โพสต์แบบสอบถาม กับชั้นเรียน EloquentPostQueries และคลาสนี้จะถูกแทนที่ในคอนโทรลเลอร์

ลองจินตนาการว่าแอปพลิเคชันของเราได้รับความนิยมอย่างมาก ผู้ใช้หลายพันคนต่อนาทีเปิดหน้าเว็บพร้อมสิ่งพิมพ์ล่าสุด สิ่งพิมพ์ยอดนิยมก็มีการอ่านบ่อยมากเช่นกัน ฐานข้อมูลจัดการโหลดดังกล่าวได้ไม่ดีนัก ดังนั้นจึงใช้โซลูชันมาตรฐาน - แคช นอกเหนือจากฐานข้อมูลแล้ว สแน็ปช็อตข้อมูลบางอย่างจะถูกจัดเก็บไว้ในพื้นที่จัดเก็บข้อมูลที่ปรับให้เหมาะสมสำหรับการดำเนินการบางอย่าง - memcached หรือ Redis.

ตรรกะการแคชมักจะไม่ซับซ้อนขนาดนั้น แต่การใช้งานใน EloquentPostQueries นั้นไม่ถูกต้องมากนัก (หากเพียงเพราะว่า หลักการความรับผิดชอบเดียว). การใช้เทมเพลตจะเป็นธรรมชาติมากกว่ามาก มัณฑนากร และใช้แคชเป็นการตกแต่งสำหรับการดำเนินการหลัก:

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

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

ละเว้นอินเทอร์เฟซ กรุ ในตัวสร้าง ด้วยเหตุผลที่ไม่ทราบสาเหตุ พวกเขาจึงตัดสินใจตั้งชื่ออินเทอร์เฟซสำหรับการแคชใน Laravel ด้วยวิธีนี้

ชั้นเรียน CachedPostQueries ใช้แคชเท่านั้น $นี่->แคช->จำไว้ ตรวจสอบว่ารายการนี้อยู่ในแคชหรือไม่ หากไม่เป็นเช่นนั้น ให้โทรกลับและเขียนค่าที่ส่งคืนไปยังแคช สิ่งที่เหลืออยู่ก็คือการนำคลาสนี้ไปใช้กับแอปพลิเคชัน เราต้องการคลาสทั้งหมดที่อยู่ในแอปพลิเคชันเพื่อขอการใช้งานอินเทอร์เฟซ โพสต์แบบสอบถาม เริ่มได้รับตัวอย่างของชั้นเรียน CachedPostQueries. อย่างไรก็ตามเขาเอง CachedPostQueries ตัวสร้างจะต้องได้รับคลาสเป็นพารามิเตอร์ EloquentPostQueriesเนื่องจากมันไม่สามารถทำงานได้หากไม่มีการใช้งาน "จริง" เราเปลี่ยนแปลง 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);
    }
}

ความปรารถนาทั้งหมดของฉันได้รับการอธิบายไว้ในผู้ให้บริการอย่างเป็นธรรมชาติ ดังนั้นเราจึงใช้การแคชสำหรับคำขอของเราโดยการเขียนคลาสเดียวและเปลี่ยนการกำหนดค่าคอนเทนเนอร์ รหัสแอปพลิเคชันที่เหลือไม่มีการเปลี่ยนแปลง

แน่นอนว่าในการใช้แคชอย่างสมบูรณ์นั้นจำเป็นต้องดำเนินการทำให้ใช้งานไม่ได้เพื่อให้บทความที่ถูกลบไม่ค้างบนไซต์ในบางครั้ง แต่จะถูกลบทันที แต่สิ่งเหล่านี้เป็นเรื่องเล็กน้อย

บรรทัดล่าง: เราไม่ได้ใช้เทมเพลตเดียว แต่ใช้สองเทมเพลต ตัวอย่าง การแยกความรับผิดชอบในการสืบค้นคำสั่ง (CQRS) เสนอให้แยกการดำเนินการอ่านและเขียนออกจากกันอย่างสมบูรณ์ในระดับอินเทอร์เฟซ ฉันมาหาเขาผ่าน หลักการแยกส่วนต่อประสานซึ่งแนะนำว่าฉันใช้รูปแบบและหลักการอย่างเชี่ยวชาญและนำมาจากอีกทฤษฎีหนึ่งเป็นทฤษฎีบท :) แน่นอนว่าไม่ใช่ทุกโครงการที่ต้องการนามธรรมในการเลือกเอนทิตี แต่ฉันจะแบ่งปันเคล็ดลับกับคุณ ในระยะเริ่มแรกของการสมัคร การพัฒนา คุณสามารถสร้างคลาสได้ง่ายๆ โพสต์แบบสอบถาม ด้วยการใช้งานตามปกติผ่าน Eloquent:

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

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

เมื่อจำเป็นต้องแคชเกิดขึ้น ด้วยการย้ายง่ายๆ คุณสามารถสร้างอินเทอร์เฟซ (หรือคลาสนามธรรม) แทนที่คลาสนี้ได้ โพสต์แบบสอบถามให้คัดลอกการใช้งานไปยังชั้นเรียน EloquentPostQueries และไปที่โครงการที่ฉันอธิบายไว้ก่อนหน้านี้ ไม่จำเป็นต้องเปลี่ยนรหัสแอปพลิเคชันที่เหลือ

เทคนิคทั้งหมดนี้ด้วยคลาส อินเทอร์เฟซ การฉีดพึ่งพา и ซีคิวอาร์เอส อธิบายโดยละเอียดใน หนังสือของฉัน “สถาปัตยกรรมของแอปพลิเคชันเว็บที่ซับซ้อน”. นอกจากนี้ยังมีวิธีแก้ปริศนาว่าทำไมชั้นเรียนทั้งหมดของฉันในตัวอย่างสำหรับบทความนี้จึงถูกทำเครื่องหมายว่าเป็นที่สิ้นสุด

ที่มา: will.com

เพิ่มความคิดเห็น