Prinsip Tanggung Jawab Tunggal (SRP) dengan Laravel

Prinsip SRP (Satu Prinsip Tanggung Jawab) adalah salah satu prinsip dasar untuk menulis kode yang didukung. Pada artikel ini, saya akan menunjukkan bagaimana menerapkan prinsip ini menggunakan contoh bahasa PHP dan kerangka Laravel.

Seringkali, ketika menjelaskan model pengembangan MVC, tugas besar yang tidak masuk akal ditugaskan ke controller. Mendapatkan parameter, logika bisnis, otorisasi, dan respons.

Tentu saja, dalam artikel dan buku ini digambarkan sebagai contoh, tetapi sering dianggap sebagai ajakan untuk bertindak dalam proyek kerja. Pendekatan ini pasti akan mengarah pada pertumbuhan kelas yang tidak terkendali dan sangat menyulitkan dukungan kode.

Prinsip tanggung jawab bersama


Sebagai contoh kita akan mengambil tipe agak "kasar", tetapi sering bertemu controller.

class OrderController extends Controller
{
    public function create(Request $request)
    {
        $rules = [
            'product_id' => 'required|max:10',
            'count' => 'required|max:10',
        ];
        $validator = Validator::make($request->all(), $rules);
        if ($validator->fails()) {
            return response()->json($validator->errors(), 400);
        }

         //  
        $order = Order::create($request->all());

        //   

        //  sms    

        return response()->json($order, 201);
    }
}

Dalam contoh ini, dapat dilihat bahwa pengontrol mengetahui terlalu banyak tentang "menempatkan pesanan", juga dipercayakan dengan tugas memberi tahu pembeli dan memesan barang.

Kami akan berusaha keras untuk memastikan bahwa pengontrol hanya perlu mengendalikan seluruh proses dan tidak ada logika bisnis.

Untuk memulainya, kami akan memvalidasi parameter dalam kelas Permintaan terpisah.

class OrderRequest extends Request
{
    public function rules(): array
    {
         return [
             'product_id' => 'required|max:10',
             'count' => 'required|max:10',
         ];    
    }
}

Dan pindahkan semua logika bisnis ke kelas OrderService

public class OrderService
{
    public function create(array $params)
    {
        //  

        //  

        //     sms
        $sms = new RuSms($appId);

        //  ,    
        $sms->send($number, $text);
     }
}

Menggunakan Wadah Layanan, kami akan menerapkan layanan kami di controller.

Akibatnya, kami memiliki pengontrol "tipis".

class OrderController extends Controller
{
    protected $service;
	
    public function __construct(OrderService $service)
    {
	$this->service = $service;	
    }

    public function create(OrderRequest $request)
    {
        $this->service->create($request->all());

        return response()->noContent(201);
    }
}

Sudah lebih baik. Semua logika bisnis telah dipindahkan ke layanan. Pengontrol hanya tahu tentang kelas OrderRequest dan OrderService - set informasi minimum yang diperlukan untuk melakukan tugasnya.

Tapi sekarang layanan kami juga perlu refactoring. Mari kita ambil logika mengirim sms ke kelas terpisah.

class SmsRu
{
    protected $appId;

    public function __constructor(string $appId)
    {
        $this->appId = $appId;
    }

    public function send($number, $text): void
    {
          //     
    }
}    

Dan mengimplementasikannya melalui konstruktor

class OrderService
{
    private $sms;

    public function __construct()
    {
	$this->sms = new SmsRu($appId);	
    }

    public function create(array $params): void
    {
          //  

          //  

          //  ,    
          $this->sms->send($nubmer, $text);
    }
}    

Sudah lebih baik, tetapi kelas OrderService masih tahu terlalu banyak tentang mengirim pesan. Kami mungkin perlu mengganti penyedia pesan di masa depan atau menambahkan tes unit. Kami melanjutkan refactoring dengan menambahkan antarmuka SmsSender , dan menentukan SmsRu melalui penyedia SmsServiceProvider .

interface SmsSenderInterface
{
    public function send($number, $text): void;
}

class SmsServiceProvider implements ServiceProviderInterface
{
    public function register(): void
    {
        $this->app->singleton(SmsSenderInterface::class, function ($app) {
            return new SmsRu($params['app_id']);
        });
    }
}

Sekarang layanan ini juga dibebaskan dari detail yang tidak perlu

class OrderService
{
    private $sms;

    public function __construct(SmsSenderInterface $sms)
    {
	$this->sms = $sms;	
    }

    public function create(): void
    {
          //  

          //  

          //  ,    
          $this->sms->send($nubmer, $text);
    }
}    

Arsitektur Berbasis Acara


Mengirim pesan bukan bagian dari proses pembuatan pesanan dasar. Pesanan akan dibuat terlepas dari pesan yang dikirim, mungkin juga di masa depan opsi pembatalan pemberitahuan sms akan ditambahkan. Agar tidak membebani OrderService dengan detail pemberitahuan yang tidak perlu, Anda dapat menggunakan Pengamat Laravel . Kelas yang akan melacak peristiwa dengan perilaku spesifik dari model Pesanan kami dan menetapkan semua logika pemberitahuan pelanggan.

class OrderObserver
{
    private $sms;

    public function __construct(SmsSenderInterface $sms)
    {
	$this->sms = $sms;	
    }

    /**
     * Handle the Order "created" event.
     */
    public function created(Order $order)
    {
        $this->sms->send($nubmer, $text);
    }

Jangan lupa untuk mendaftar OrderObserver di AppServiceProvider .

Kesimpulan


Saya mengerti bahwa saya menggambarkan hal-hal yang agak dangkal di posting ini, tetapi saya ingin menunjukkan bagaimana prinsip ini diterapkan pada kerangka Laravel.

Prinsip SPR adalah salah satu yang paling penting dan membuka jalan bagi yang lain. Ini diperlukan untuk digunakan, terutama karena kerangka Laravel menyediakan semua alat yang diperlukan.

All Articles