DTO Pattern
الDTO PatternأوData Transfer Object Pattern، هو نمط تصميم برمجي يستخدم لنقل البيانات المنظمة بين أجزاء مختلفة من التطبيق. DTO هو كائن بسيط يحمل البيانات دون الكشف عن models أو database entities أو request الداخلية أو المصفوفات الأولية في كل مكان في قاعدة الكود.
يتم استخدام DTOs بشكل شائع في التطبيقات الموجهة للكائنات، ومشاريع APIs، ومشاريع Laravel، وتطبيقات Symfony، وclean architecture، وطبقات الخدمة، ومعالجات command، والتكاملات الخارجية، وسير عمل تحويل البيانات. إنها تساعد المطورين على جعل تدفق البيانات أكثر وضوحًا وأمانًا وأسهل في الصيانة.
مقدمة
في المقالات السابقة من سلسلة OOP وDesign Patterns، ناقشنا أنماطًا مثل MVC Pattern، Service Layer Pattern، Repository Pattern، Dependency Injection، Command Pattern، Observer Pattern، Strategy Pattern، وAdapter Pattern، وFacade Pattern.
يعد DTO Pattern مفيدًا بشكل خاص عندما تنتقل البيانات بين هذه الطبقات. على سبيل المثال، قد يتلقى controller بيانات request، ويمررها إلى خدمة، وقد تمرر الخدمة بيانات منظمة إلى محول repository، أو command، أو event، أو محول API خارجي.
بدون DTOs، غالبًا ما يقوم المطورون بتمرير المصفوفات الأولية عبر التطبيق. قد ينجح هذا في البداية، ولكن مع نمو المشروع، تصبح المصفوفات أكثر صعوبة في الفهم، ويصعب التحقق من صحتها، ويسهل إساءة استخدامها. يحل DTOs هذه المشكلة عن طريق إعطاء البيانات بنية واضحة.
ما هو DTO؟
DTO، أو Data Transfer Object، هو كائن تم إنشاؤه بشكل أساسي لنقل البيانات من طبقة إلى أخرى. وعادةً ما يحتوي على خصائص وأحيانًا أساليب مساعدة بسيطة، لكن لا ينبغي أن يحتوي على منطق العمل المعقد.
بعبارات بسيطة، DTO عبارة عن حاوية منظمة للبيانات.
على سبيل المثال، بدلاً من تمرير مصفوفة تسجيل المستخدم بمفاتيح مثل الاسم والبريد الإلكتروني وكلمة المرور والهاتف، يمكن للمطور إنشاء RegisterUserDto. يحدد هذا الكائن بوضوح البيانات المطلوبة لتسجيل المستخدم.
لماذا يعد DTO Pattern مهمًا
يعد DTO Pattern مهمًا لأنه يجعل نقل البيانات واضحًا. عندما تتلقى إحدى الطرق مصفوفة، ليس من الواضح دائمًا المفاتيح المتوقعة. قد يفتقد المصفوفة قيمًا أو يحتوي على قيم إضافية أو يستخدم أسماء مفاتيح غير صحيحة.
باستخدام DTO، يتم تعريف البيانات المتوقعة في class واحد. وهذا يحسن إمكانية القراءة ويقلل من الأخطاء.
يساعد DTOs أيضًا على حماية models الداخلي. بدلاً من تمرير كائنات database models أو request في كل مكان، يمكن للتطبيق تمرير كائنات بيانات نظيفة تحتوي فقط على القيم المطلوبة لعملية معينة.
مشكلة بدون DTOs
تخيل أن خدمة تسجيل المستخدم تتلقى مصفوفة أولية:
class UserRegistrationService
{
public function register(array $data): User
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => password_hash($data['password'], PASSWORD_BCRYPT),
]);
}
}يعمل هذا الرمز، لكن الخدمة لا تقوم بوضوح بتوصيل البيانات التي تتوقعها. هل تحتاج المصفوفة إلى رقم هاتف؟ هل يشمل الدور؟ هل تم التحقق من صحة البريد الإلكتروني بالفعل؟ ماذا يحدث إذا كان مفتاح كلمة المرور مفقودًا؟
المصفوفات الأولية مرنة، لكن هذه المرونة يمكن أن تخلق أخطاء مخفية. يوفر DTOs عقدًا أكثر وضوحًا.
الحل مع DTO Pattern
يمكن أن يجعل DTO البيانات المتوقعة واضحة:
class RegisterUserDto
{
public function __construct(
public string $name,
public string $email,
public string $password
) {
}
}يمكن للخدمة الآن تلقي DTO بدلاً من المصفوفة الأولية:
class UserRegistrationService
{
public function register(RegisterUserDto $dto): User
{
return User::create([
'name' => $dto->name,
'email' => $dto->email,
'password' => password_hash($dto->password, PASSWORD_BCRYPT),
]);
}
}هذا الرمز أكثر وضوحا. يخبر توقيع الطريقة المطورين بالضبط بنوع البيانات المطلوبة.
الغرض الرئيسي من DTOs
الغرض الرئيسي من DTOs هو نقل البيانات بأمان ووضوح بين الطبقات. لم يتم تصميم DTOs ليحل محل entities أو models أو الخدمات التجارية. وهي مصممة لحمل البيانات في بنية يمكن التنبؤ بها.
يمكن استخدام DTOs لنقل البيانات بين:
وحدات التحكم والخدمات.
الخدمات وrepositories.
الأوامر ومعالجات command.
الأحداث وlisteners.
طبقات التطبيق الخارجية APIs.
نماذج وحالات استخدام التطبيق.
منطق المجال وطبقات العرض.
وهذا يجعل تدفق البيانات أسهل في الفهم وأسهل في الصيانة.
مثال DTO الأساسي في PHP
يوضح المثال التالي DTO البسيط لإنشاء منتج:
class CreateProductDto
{
public function __construct(
public string $name,
public string $description,
public float $price,
public int $categoryId,
public bool $isActive = true
) {
}
}يحدد DTO جميع البيانات اللازمة لإنشاء منتج. بدلاً من تمرير مصفوفة غير واضحة، يمكن للتطبيق تمرير كائن منظم بقوة.
استخدام DTO في الخدمة
class ProductService
{
public function create(CreateProductDto $dto): Product
{
return Product::create([
'name' => $dto->name,
'description' => $dto->description,
'price' => $dto->price,
'category_id' => $dto->categoryId,
'is_active' => $dto->isActive,
]);
}
}تعتمد الخدمة الآن على كائن بيانات واضح. يؤدي ذلك إلى تحسين إمكانية القراءة وتقليل فرصة استخدام مفاتيح المصفوفة الخاطئة.
DTOs وسلامة النوع
يعمل DTOs على تحسين أمان النوع لأن الخصائص يمكن أن تحتوي على أنواع صريحة. على سبيل المثال، يمكن تعريف السعر كـ float، وcategorId كـ int، وisActive كـ bool.
باستخدام المصفوفات الأولية، يمكن تمرير القيم الخاطئة بسهولة. يمكن تمرير سلسلة حيث من المتوقع وجود عدد صحيح، أو قد يكون المفتاح المطلوب مفقودًا.
يساعد DTOs المكتوب في اكتشاف المشكلات مبكرًا وتسهيل فهم الكود في IDEs وأدوات التحليل الثابتة.
DTOs في PHP 8+
قدم PHP 8 ترويج خاصية constructor، مما يجعل كتابة DTOs أسهل. بدلاً من تحديد الخصائص وتعيينها يدويًا، يمكن للمطورين تعريفها مباشرةً في ملف constructor.
class UpdateProfileDto
{
public function __construct(
public string $name,
public ?string $phone,
public ?string $city,
public ?string $country
) {
}
}يعد بناء الجملة هذا نظيفًا وعمليًا لـ DTO classes.
للقراءة فقط DTOs في PHP
في PHP الحديثة، يمكن جعل DTOs للقراءة فقط لمنع التغييرات العرضية بعد الإنشاء.
readonly class CreateOrderDto
{
public function __construct(
public int $userId,
public array $items,
public string $paymentMethod,
public ?string $couponCode = null
) {
}
}يكون DTO للقراءة فقط مفيدًا عندما تظل البيانات مستقرة بعد إنشائها. يمكن أن يمنع هذا التعديلات غير المتوقعة أثناء تدفق التطبيق.
إنشاء DTOs من المصفوفات
تتلقى العديد من التطبيقات البيانات كمصفوفات من حمولات HTTP requests أو النماذج أو APIs أو JSON. يمكن أن يوفر DTO طريقة مصنع ثابتة لإنشاء نفسه من مصفوفة.
class RegisterUserDto
{
public function __construct(
public string $name,
public string $email,
public string $password
) {
}
public static function fromArray(array $data): self
{
return new self(
name: $data['name'],
email: $data['email'],
password: $data['password']
);
}
}يؤدي هذا إلى الحفاظ على تعيين المصفوفة في مكان واحد ويجعل رمز الخدمة أكثر نظافة.
باستخدام DTO من Array
$dto = RegisterUserDto::fromArray([
'name' => 'Adnan Mehrat',
'email' => 'adnan@example.com',
'password' => 'secret',
]);
$user = $registrationService->register($dto);تتلقى الخدمة DTO منظمًا بدلاً من صفيف أولي.
DTOs في Laravel
غالبًا ما يقوم مطورو Laravel بتمرير بيانات request التي تم التحقق من صحتها كمصفوفات للخدمات. وهذا أمر شائع ومقبول في المشاريع الصغيرة. ومع ذلك، يمكن لـ DTOs أن يجعل الكود أكثر وضوحًا في التطبيقات الأكبر حجمًا.
يمكن لـ Laravel controller إنشاء DTO من طلب نموذج:
class RegisterController extends Controller
{
public function store(
RegisterUserRequest $request,
UserRegistrationService $service
) {
$dto = RegisterUserDto::fromArray($request->validated());
$user = $service->register($dto);
return response()->json($user);
}
}يعالج طلب النموذج validation، ويحمل DTO البيانات التي تم التحقق من صحتها إلى طبقة الخدمة.
طلب نموذج Laravel باستخدام DTO
تتمثل الطريقة المفيدة في إضافة طريقة داخل طلب النموذج والتي تُرجع DTO:
class RegisterUserRequest extends FormRequest
{
public function rules(): array
{
return [
'name' => ['required', 'string'],
'email' => ['required', 'email'],
'password' => ['required', 'min:8'],
];
}
public function toDto(): RegisterUserDto
{
return RegisterUserDto::fromArray($this->validated());
}
}يصبح controller أنظف:
$user = $service->register($request->toDto());يؤدي هذا إلى إبقاء إنشاء validation وDTO قريبًا من طبقة request مع الحفاظ على استقلالية الخدمة عن تفاصيل HTTP.
DTOs في Symfony
يمكن لتطبيقات Symfony استخدام DTOs مع النماذج وحمولات controllers وrequest وMessenger commands وAPI وطبقات الخدمة.
على سبيل المثال، يمكن لـ controller إنشاء DTO من بيانات request وتمريرها إلى الخدمة:
class RegisterController
{
public function __invoke(Request $request, UserRegistrationService $service): Response
{
$dto = new RegisterUserDto(
name: $request->request->get('name'),
email: $request->request->get('email'),
password: $request->request->get('password')
);
$user = $service->register($dto);
return new JsonResponse(['id' => $user->getId()]);
}
}يعد DTOs مفيدًا أيضًا في Symfony Messenger عندما يقوم commands بنقل البيانات إلى المعالجات.
DTO Pattern وAPIs
DTOs مفيدة جدًا في تطوير API. غالبًا ما يتلقى APIs بيانات request ويعيد بيانات response. يمكن لـ DTOs تنظيم كل من الإدخال والإخراج.
بالنسبة للإدخال، يمكن أن يمثل DTO البيانات المطلوبة لإنشاء مورد أو تحديثه. بالنسبة للمخرجات، يمكن أن يمثل DTO تنسيق response الذي يجب إرجاعه إلى العميل.
يمنع هذا التطبيق من كشف database models الداخلي مباشرة من خلال API.
مثال على الاستجابة DTO
يمكن لـ response DTO تحديد البيانات التي يجب إرجاعها إلى العميل:
class UserResponseDto
{
public function __construct(
public int $id,
public string $name,
public string $email
) {
}
public static function fromUser(User $user): self
{
return new self(
id: $user->id,
name: $user->name,
email: $user->email
);
}
public function toArray(): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
];
}
}يمكن لـ API إرجاع الحقول المحددة في DTO فقط، بدلاً من الكشف عن المستخدم الكامل model.
DTO Pattern وAPIs خارجية
تعتبر DTOs مفيدة عند التكامل مع APIs خارجية. غالبًا ما تقوم APIs خارجية بإرجاع البيانات بتنسيقات لا تتطابق مع بنية التطبيق الداخلية.
يمكن للمحول تحويل بيانات response الخارجية إلى DTO داخلي. يمكن لبقية التطبيق بعد ذلك العمل ببنية مستقرة.
class PaymentResultDto
{
public function __construct(
public bool $successful,
public string $transactionId,
public ?string $errorMessage = null
) {
}
}يمكن لمحول الدفع إرجاع PaymentResultDto بدلاً من إرجاع المصفوفات الأولية الخاصة بالموفر.
DTO Pattern وAdapter Pattern
يعمل DTO Pattern بشكل جيد مع Adapter Pattern. يقوم المحول بتوصيل الأنظمة غير المتوافقة، ويمكن لـ DTO تحديد تنسيق البيانات الداخلي الذي يستخدمه التطبيق.
على سبيل المثال، قد يقوم Stripe وPayPal بإرجاع هياكل response مختلفة. يمكن لكل محول تحويل الموفر response إلى نفس PaymentResultDto. ثم يقوم التطبيق بمعالجة نتائج الدفع بشكل متسق.
يؤدي هذا إلى إبقاء التفاصيل الخاصة بالموفر معزولة ويجعل صيانة عمليات التكامل أسهل.
DTO Pattern وService Layer
يتم استخدام DTOs بشكل شائع مع Service Layer Pattern. تتلقى وحدات التحكم requests وتقوم بإنشاء DTOs. تتلقى الخدمات DTOs وتنفذ منطق العمل.
يؤدي هذا إلى إبقاء الخدمات مستقلة عن كائنات HTTP request ويجعلها قابلة لإعادة الاستخدام من نقاط إدخال مختلفة مثل controllers ووحدة التحكم commands والوظائف والاختبارات.
على سبيل المثال، يمكن استخدام نفس CreateProductDto بواسطة لوحة الإدارة controller أو API controller أو استيراد command.
DTO Pattern وCommand Pattern
يمكن أن يبدو DTOs وcommands متشابهين. كلاهما قد يحمل البيانات. ومع ذلك، فإن نيتهم مختلفة.
يحمل DTO البيانات بين الطبقات. يمثل command الإجراء الذي يجب تنفيذه.
على سبيل المثال، يصف RegisterUserDto بيانات تسجيل المستخدم. قد يمثل RegisterUserCommand request لتسجيل مستخدم ويمكن معالجته بواسطة معالج command.
في بعض البنيات، تعمل كائنات command مثل DTOs في حالات الاستخدام. تعتمد التسمية على أسلوب التصميم.
DTO مقابل Model
DTO ليس هو نفسه model. عادةً ما يمثل model مفهوم المجال أو database entity. قد يحتوي على علاقات أو منطق استمراري أو سلوك المجال أو أدوات الوصول أو الطفرات أو ميزات ORM.
يستخدم DTO بشكل أساسي لنقل البيانات. يجب أن يكون أبسط وأكثر تحديدًا لحالة الاستخدام.
على سبيل المثال، قد يتضمن المستخدم model المعرف والاسم والبريد الإلكتروني وكلمة المرور والأدوار والأذونات والطوابع الزمنية والعلاقات والأساليب. قد يتضمن RegisterUserDto الاسم والبريد الإلكتروني وكلمة المرور فقط.
DTO مقابل Array
المصفوفات مرنة، لكنها لا تحدد البنية بوضوح. لا تُظهر الطريقة التي تستقبل مصفوفة المفاتيح المطلوبة أو الأنواع المتوقعة.
يوفر DTOs بنية واضحة ذات خصائص وأنواع محددة. تعمل على تحسين إمكانية القراءة ودعم IDE والتحليل الثابت وقابلية الصيانة.
بالنسبة للعمليات الصغيرة والبسيطة، قد تكون المصفوفات كافية. بالنسبة لسير العمل الأكبر ونقل البيانات المهمة، عادةً ما يكون DTOs أكثر وضوحًا.
DTO مقابل كائن القيمة
DTO وكائن القيمة مفهومان مختلفان. يقوم DTO بنقل البيانات بين الطبقات. يمثل كائن القيمة قيمة ذات معنى في المجال وغالبًا ما يتضمن validation أو السلوك.
على سبيل المثال، يمكن أن يكون EmailAddress كائن قيمة لأنه يمثل بريدًا إلكترونيًا صالحًا وقد يتحقق من صحة تنسيقه. قد يحتوي RegisterUserDto على سلسلة بريد إلكتروني أو كائن EmailAddress كجزء من بيانات تسجيل المستخدم.
كائنات القيمة تعبر عن معنى المجال. DTOs بنية نقل البيانات السريعة.
DTO مقابل الكيان
يمتلك entity هوية ويمثل عادةً كائنًا يتغير بمرور الوقت. على سبيل المثال، يمكن أن يكون المستخدم والأمر والمنتج والفاتورة entities.
لا يحتوي DTO عادةً على هوية أو سلوك دورة الحياة. إنه يحمل ببساطة بيانات لعملية معينة أو response.
يمكن أن يؤدي تمرير entities في كل مكان إلى الكشف عن الكثير من البنية الداخلية. يسمح DTOs للتطبيق بتمرير البيانات المطلوبة لغرض محدد فقط.
التحقق من صحة DTO
هناك آراء مختلفة حول ما إذا كان يجب على DTOs التحقق من صحة البيانات. في العديد من التطبيقات، تتم معالجة validation قبل إنشاء DTO، كما هو الحال في طلب نموذج Laravel أو أداة التحقق Symfony.
ومع ذلك، لا يزال بإمكان DTOs فرض أمان النوع الأساسي من خلال أنواع constructor. تضيف بعض المشاريع أيضًا أساليب validation البسيطة داخل المصنع DTO.
النقطة المهمة هي تجنب تحويل DTOs إلى منطق العمل classes كبير. عادةً ما تنتمي قواعد العمل validation المعقدة إلى أدوات التحقق من الصحة أو الخدمات أو كائنات المجال أو كائنات القيمة.
DTOs غير قابل للتغيير
غالبًا ما يتم تصميم DTOs ليكون غير قابل للتغيير. وهذا يعني أن قيمهم لا تتغير بعد الخلق.
DTOs غير القابل للتغيير يجعل تدفق البيانات أكثر قابلية للتنبؤ به. بمجرد إنشاء DTO من إدخال تم التحقق من صحته، لا يمكن للأجزاء الأخرى من التطبيق تعديله عن طريق الخطأ.
في PHP، يمكن أن تساعد خصائص classes للقراءة فقط وخصائص للقراءة فقط في إنشاء DTOs غير قابل للتغيير.
readonly class UpdateEmailDto
{
public function __construct(
public int $userId,
public string $newEmail
) {
}
}هذه بنية نظيفة وآمنة لنقل البيانات.
متداخل DTOs
تحتوي بعض بنيات البيانات على بيانات متداخلة. على سبيل المثال، قد يحتوي الطلب على بيانات العميل وعناصر الطلب. يمكن تداخل DTOs لتمثيل هذه البنية بوضوح.
readonly class OrderItemDto
{
public function __construct(
public int $productId,
public int $quantity,
public float $price
) {
}
}
readonly class CreateOrderDto
{
public function __construct(
public int $userId,
public array $items,
public string $paymentMethod
) {
}
}في التنفيذ الأكثر صرامة، يجب أن تحتوي مصفوفة العناصر على كائنات OrderItemDto فقط. هذا يبقي الهيكل واضحا.
مجموعات DTO
عند العمل مع قوائم DTOs، تقوم بعض المشاريع بإنشاء DTO collection classes. يضمن DTO collection أن القائمة تحتوي على كائنات DTO محددة فقط.
class OrderItemDtoCollection
{
private array $items = [];
public function add(OrderItemDto $item): void
{
$this->items[] = $item;
}
public function all(): array
{
return $this->items;
}
}يكون هذا مفيدًا عندما يكون لدى collection قواعد أو عندما تكون هناك حاجة إلى بنية أقوى. في الحالات البسيطة، قد يكون مصفوفة DTOs كافية.
رسم الخرائط DTO
تعيين DTO هو عملية تحويل البيانات من بنية واحدة إلى DTO. قد يتضمن ذلك التعيين من صفائف أو requests أو models أو entities أو API خارجي.
يمكن إجراء التعيين داخل أساليب المصنع الثابتة، أو مخطط classes، أو المحولات، أو الموارد الخاصة بإطار العمل.
بالنسبة إلى DTOs الصغيرة، عادةً ما تكون طريقة fromArray أو fromModel كافية. بالنسبة لرسم الخرائط المعقدة، قد يكون مصمم الخرائط المخصص class أكثر نظافة.
مثال على class مصمم الخرائط
class UserDtoMapper
{
public function fromUser(User $user): UserResponseDto
{
return new UserResponseDto(
id: $user->id,
name: $user->name,
email: $user->email
);
}
}يستطيع مصمم الخرائط إبقاء منطق التحويل منفصلاً عندما يصبح إنشاء DTO معقدًا.
موارد DTOs وAPI
في Laravel، غالبًا ما يتم استخدام موارد API لتحويل models إلى JSON responses. API قد تؤدي الموارد في بعض الأحيان إلى تقليل الحاجة إلى response DTOs.
ومع ذلك، لا تزال DTOs مفيدة لبيانات الإدخال، ونقل بيانات طبقة الخدمة، وبيانات command، وتكامل API الخارجي، والهندسة المعمارية المستقلة عن إطار العمل.
يعتمد الاختيار بين موارد DTOs وAPI على الغرض. تركز موارد API على العرض التقديمي، في حين أن DTOs عبارة عن كائنات عامة لنقل البيانات.
فوائد DTO Pattern
يوفر DTO Pattern العديد من الفوائد في تصميم البرامج الموجهة للكائنات.
تشمل الفوائد الرئيسية ما يلي:
يجعل نقل البيانات واضحًا ومنظمًا.
يقلل الاعتماد على المصفوفات الخام.
يحسن إمكانية القراءة وأمان الكتابة.
يحمي models الداخلي من التعرض في كل مكان.
يحسن دعم IDE والتحليل الثابت.
يجعل طرق الخدمة أكثر وضوحا.
يعمل بشكل جيد مع APIs والخدمات وcommands وعمليات التكامل الخارجية.
يدعم clean architecture وفصل الاهتمامات.
هذه الفوائد تجعل DTOs مفيدًا بشكل خاص في التطبيقات المتوسطة والكبيرة.
عيوب DTO Pattern
يمكن أن يكون لـ DTO Pattern أيضًا عيوب. فهو يضيف المزيد من classes إلى المشروع، وهو ما قد يكون غير ضروري لميزات بسيطة جدًا.
عيب آخر هو رسم الخرائط العامة. يجب على المطورين تحويل الصفائف models أو API responses إلى DTOs. إذا تم الإفراط في استخدامه، فقد يؤدي ذلك إلى إنشاء كود متكررة.
يمكن أيضًا أن يصبح DTOs مربكًا إذا لم يتم تسميتها بشكل واضح أو إذا تم استخدامها لغرض خاطئ. يجب أن يمثل DTO حاجة محددة لنقل البيانات، ولا يصبح كائنًا عامًا يستخدم في كل مكان.
متى يتم استخدام DTO Pattern
استخدم DTO Pattern عندما تحتاج البيانات إلى بنية واضحة أثناء تحركها بين الطبقات.
يكون DTO Pattern مفيدًا عندما:
تتلقى الخدمة العديد من قيم الإدخال.
أصبحت المصفوفات الأولية غير واضحة أو محفوفة بالمخاطر.
يتم تمرير نفس بنية البيانات عبر طبقات متعددة.
تريد حماية models من التعرض المباشر.
أنت تقوم بإنشاء APIs بتنسيقات request أو response الواضحة.
أنت تقوم بالتكامل مع APIs خارجية.
تريد أمانًا أفضل للكتابة ودعم IDE.
يستخدم المشروع clean architecture أو تصميم طبقة الخدمة.
في حالة وجود هذه الشروط، يمكن لـ DTOs أن يجعل التصميم أكثر نظافة وأمانًا.
متى لا تستخدم DTO Pattern
لا تستخدم DTOs لكل عملية صغيرة تلقائيًا. إذا كانت الميزة بسيطة وبنية البيانات واضحة، فقد يضيف DTO تعقيدًا غير ضروري.
تجنب DTOs عندما:
تحتوي العملية على قيمة واحدة أو قيمتين بسيطتين فقط.
لن يقوم DTO إلا بتكرار model بدون غرض.
المشروع صغير جدًا والبساطة أكثر أهمية.
يصبح رمز التعيين أكثر تعقيدًا من المشكلة نفسها.
يتم استخدام DTO ككائن عام بمسؤولية غير واضحة.
يجب أن يعمل DTOs على تحسين الوضوح. ولا ينبغي إضافتها فقط لجعل الكود تبدو أكثر تقدمًا.
الأخطاء الشائعة في DTO Pattern
أحد الأخطاء الشائعة هو إضافة منطق العمل إلى DTOs. يجب أن يحمل DTO البيانات، وليس تنفيذ مهام سير العمل المعقدة.
هناك خطأ آخر وهو إنشاء DTOs التي تكون عامة جدًا. على سبيل المثال، قد يصبح UserDto غير واضح إذا تم استخدامه للتسجيل وتحديثات الملف الشخصي وAPI responses وطرق عرض المسؤول والتكاملات الخارجية. غالبًا ما تكون DTOs المحددة مثل RegisterUserDto أو UserResponseDto أكثر وضوحًا.
الخطأ الثالث هو تكرار models دون تقليل التعرض أو تحسين البنية. يجب أن يكون لدى DTO سبب لوجوده.
الخطأ الرابع هو تمرير المصفوفات الأولية داخل DTOs دون كتابة واضحة عند الحاجة إلى بنية أقوى. قد يكون DTOs المتداخل أفضل للبيانات المعقدة.
أفضل الممارسات لـ DTO Pattern
لاستخدام DTO Pattern بشكل فعال، يجب على المطورين إبقاء DTOs بسيطًا ومركزًا وهادفًا.
تتضمن أفضل الممارسات المفيدة ما يلي:
قم بإنشاء DTOs لحالات استخدام محددة.
استخدم أسماء واضحة مثل CreateOrderDto أو UserResponseDto.
تفضل الخصائص المكتوبة.
استخدم DTOs للقراءة فقط عندما لا تتغير البيانات.
تجنب منطق العمل المعقد داخل DTOs.
استخدم أساليب المصنع مثل fromArray عندما يكون ذلك مفيدًا.
استخدم مخطط classes للتحويلات المعقدة.
لا تعرض حقول model الحساسة في response DTOs.
تجنب إنشاء DTOs بدون غرض واضح.
تساعد هذه الممارسات في إبقاء DTOs مفيدًا وقابلاً للصيانة.
قائمة مرجعية عملية قبل إنشاء DTO
قبل إنشاء DTO، يمكن للمطورين طرح هذه الأسئلة:
هل أصبحت بيانات المصفوفة الأولية غير واضحة؟
هل تحتاج هذه العملية إلى بنية واضحة للإدخال أو الإخراج؟
هل سيحمي DTO models الداخلي من التعرض؟
هل ستؤدي الخصائص المكتوبة إلى تحسين السلامة؟
هل DTO مخصص لحالة استخدام واحدة؟
هل سيجعل DTO طريقة الخدمة أسهل في الفهم؟
هل يستحق جهد رسم الخرائط الوضوح المكتسب؟
إذا كانت الإجابة بنعم على العديد من هذه الأسئلة، فقد يكون DTO اختيارًا جيدًا للتصميم.
لماذا DTO Pattern مهم للمبتدئين
بالنسبة للمبتدئين، قد يبدو DTOs وكأنه classes إضافي في البداية. ومع ذلك، فإنها تصبح مفيدة جدًا عندما تنمو التطبيقات وتبدأ البيانات في التحرك بين طبقات عديدة.
يساعد تعلم DTOs المبتدئين على فهم تدفق البيانات النظيف وتصميم طبقة الخدمة والتحكم API response ورسم خرائط التكامل الخارجي ورمز التطبيق الآمن من النوع.
يساعد DTOs المطورين أيضًا على الابتعاد عن الاعتماد بشكل كبير على المصفوفات الأولية وكائنات request الخاصة بإطار العمل داخل منطق العمل.
الاستنتاج
DTO Pattern هو نمط تصميم برمجي يستخدم لنقل البيانات المنظمة بين طبقات التطبيق. يحدد DTO حقول البيانات الواضحة ويساعد على تجنب المصفوفات الأولية غير الواضحة، والتعريض الزائد لـ models، ومعالجة request المقترنة بإحكام.
تعتبر DTOs مفيدة في PHP، وLaravel، وSymfony، وAPIs، وطبقات الخدمة، ومعالجات command، وعمليات التكامل الخارجية، وclean architecture. تعمل على تحسين إمكانية القراءة وأمان الكتابة وقابلية الصيانة وفصل الاهتمامات.
ومع ذلك، يجب استخدام DTOs بعناية. قد لا تحتاج العمليات البسيطة إلى DTOs، ولا ينبغي أن يصبح DTOs منطق العمل classes. عند تطبيقه بشكل صحيح، يعد DTO Pattern أداة عملية لإنشاء برامج موجهة للكائنات نظيفة ومنظمة وقابلة للتطوير.

