DTO یا  Data Transfer Objects چیست؟ با نمونه کد PHP

DTO یا Data Transfer Objects چیست؟ با نمونه کد PHP

DTO یا  Data Transfer Objects چیست؟

DTO یا  Data Transfer Objects یکی از اجزای سازنده ی برنامه نویسی شی گراست. این قابلیت همین الان هم در بسیاری از فریمورک های نوشته شده توسط زبان های مختلف با پیاده سازی ها و موارد استفاده ی متفاوت نوشته شده است.  در PHP از DTO برای نگه داری و  انتقال  اطلاعات بین پردازه های مختلف و لایه های مختلف برنامه استفاده می شود.

مارتین فولر بزرگ، اولین بار این الگو را در کتابی با عنوان Patterns of Enterprise Application Architecture معرفی کرد. پس اگر خواستید با این الگو بیشتر آشنا بشوید، می توانید در این کتاب به دنبال آن بگردید.

شاید بتوان گفت مفیدترین موارد استفاده از DTO ها، حفظ ساختار داده در درخواست ها و انتقال داده از کنترلر (Controller) به لایه ی سرویس برنامه است. 

در ادامه قصد دارم به بعضی از موارد استفاده از DTO به صورت عملی اشاره کنم تا بتوانید درک بهتری از آن بدست بیاورید.

مثال بدون Data Transfer Objects 

فرض کنید ما یک برنامه ای نوشتیم که قرار است این بخش از آن بتواند اطلاعات بیمار را در دیتابیس ذخیره کنیم. به کد کنترلر زیر که بدون DTO نوشته شده است دقت کنید:

class StorePatientController
{
    public function __invoke(
        StorePatientRequest $request
    ) {
        // You get only validated data from the request
        $validated = $request->validated();
         
        // Now you have an array like this
        $data = [
            'first_name' => $validated['first_name'],
            'last_name' => $validated['last_name'],
            'address' => $validated['last_name'],
            'health_card_id' => $validated['last_name'],
            'family_physician' => $validated['family_physician'],
            'clinic' => $validated['clinic'],
            'phone' => $validated['phone'],
            'email' => $validated['email'],
            // ..........
        ];
 
        // Now you process the data to the business layer
        $result = new StorePatientService($data);
 
        // And you end it with a response
        return response()->json([
            'message' => 'Your patient some succesfully created'
        ], 201);
    }
}

در این کد از برخی ویژگی های  لاراول استفاده شده است که اگر با لاراول آشنا نیستید نگران نباشید چون این موضوعات ربطی به بخش DTO که می خواهیم درباره اش با هم صحبت کنیم ندارد.

به طور کلی در کد بالا ابتدا داده های رسیده از طریق درخواست (Request)، اعتبارسنجی می شود و با داده هایی که از این فیلتر اولیه عبور کرده اند، آرایه ای از داده های بیمار ساخته می شود و سپس به سرویسی برای ذخیره سازی داده های بیمار در دیتابیس سپرده می شود و در نهایت هم یک پاسخ (Response) با کد 201 برگردانده می شود که حاوی یک پیام است.

همانطور که در کد بالا مشاهده می کنید، نتیجه ی اعتبار سنجی درخواست به یک آرایه ی بزرگ تبدیل می شود که ضمن اینکه نگهداری و کار با آرایه ها ممکن است بعضی وقت ها بسیار دشوار باشد، به دلیل تغییرپذیر بودن این اطلاعات، ممکن است چالش های زیادی هم برای مان در زمان ذخیره سازی در دیتابیس و دیگر مواقع ایجاد کند.

مثال پیاده سازی کد با DTO

حالا قصد داریم کاربرد DTO را در این مثال ببینم. در ابتدا آرایه را به یک آبجکت تغییرناپذیر تبدیل می کنیم.

class StorePatientDto
{
    public function __construct(
        public readonly string $firstName,
        public readonly string $lastName,
        public readonly string $address,
        public readonly ?string $healthCardId,
        // ..........
    ) {}
}

حالا کنترلر ما به کد زیر تغییر می کند:

class StorePatientController
{
    public function __invoke(
        StorePatientRequest $request
    ) {
        // You get only validated data from the request
        $validated = $request->validated();
         
        // Now you create an object like this
        $data = new StorePatientDto(
            firstName: $validated['first_name'],
            lastName: $validated['last_name'],
            address: $validated['address'],
            healthCardId: $validated['health_card_id'],
            // ..........
        );
 
        // Now you process the data to the business layer
        $result = new StorePatientService($data);
 
        // And you end it with a response
        return response()->json([
            'message' => 'Your patient some succesfully created'
        ], 201);
    }
}

تا اینجای کار توانستید DTO را پیاده سازی کنید، ولی می توانید کنترلرتان را تمیزتر هم بنویسید و می توانید وظیفه ی تبدیل آرایه به آبجکت را به کلاس DTO بسپارید. فقط حواستان باشد که با اینکار دارید کلاس DTO ای می نویسید که فقط آرایه دریافت می کند. البته برای حل این مشکل هم می توایند برنامه تان را به صورت Generic بنویسید.  پیشنهاد می کنم در برنامه های توسعه فردی تان یادگیری اصول Generic Programming را هم قرار بدهید.

با این تغییرات کلاس DTO شما به چنین کدی تبدیل می شود:

class StorePatientDto
{
    public readonly string $firstName;
     
    public readonly string $lastName;
 
    public readonly string $address;
 
    public readonly string $healthCardId;
 
    // ..........
     
    public function __construct(public readonly array $patientData)
    {
        $this->firstName = Arr::get($patientData, 'first_name');
        $this->lastName =  Arr::get($patientData, 'last_name');
        $this->address =  Arr::get($patientData, 'address');
        $this->healthCardId =  Arr::get($patientData, 'healthCardId');
        // ..........
    }
}

حالا کد کنترلر تمیز شده شما هم به این صورت خواهد بود:

class StorePatientController
{
    public function __invoke(
        StorePatientRequest $request
    ) {
        // You get only validated data from the request
        $validated = $request->validated();
         
        // Now you create an object like this
        $data = new StorePatientDto($validated);
 
        // Now you process the data to the business layer
        $result = new StorePatientService($data);
 
        // And you end it with a response
        return response()->json([
            'message' => 'Your patient some succesfully created'
        ], 201);
    }
}

بسیار خب، حالا دیگه اطلاعات را به گونه ای به لایه ی سرویس برنامه تان منتقل می کنید که هیچ نگرانی از تغییرات احتمالی در ساختار یا اطلاعات آنها ندارید. زیرا یک آبجکت غیرقابل تغییر تحویل سرویس داده اید.

اگر تونستیم این الگو را تا این حد تمیز پیاده سازی کنیم بخاطر نوع متغیر  readonly ارائه شده در PHP 8.1 به بعد است.

جالبه بدونید نوشتن کدهای Data Transfer Objects و Value Objects در زبان PHP روز به روز آسان تر شده است.  در این باره در سکان پلاس دیگری بیشتر باهم صحبت خواهیم کرد.

پیشنهاد میکنم در ادامه ی مقاله ی Practical PHP Patterns: Data Transfer Object را مطالعه کنید.

منبع:

https://draganatanasov.com/2022/11/12/data-transfer-objects-in-php/

 

 

از بهترین نوشته‌های کاربران سکان آکادمی در سکان پلاس


online-support-icon