من البنية متعددة الطبقات إلى البنية النظيفة
افهم سبب توجّه الاعتماديات إلى الداخل.
من البنية متعددة الطبقات إلى البنية النظيفة درس مجاني في PHP Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في PHP Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة PHP Academy 4 دروس في المجموع.
لماذا Clean Architecture
أنتم تعرفون بالفعل حزمة PHP التقليدية ذات الطبقات الثلاث: Controller → Service → Repository → Database. إنها تعمل، لكن منطق الأعمال ينتهي مقترنًا بـ Eloquent وDoctrine وطلب HTTP ودورة حياة الإطار. تعكس Clean Architecture اتجاه التبعيات، بحيث لا يعرف المجال شيئًا عن البنية التحتية. والنتيجة: حالات استخدام قابلة للاختبار، ومحوّلات قابلة للاستبدال، وقاعدة شيفرة تصمد أمام ترقيات الإطار.
قاعدة التبعية
القاعدة الوحيدة في Clean Architecture هي: تشير تبعيات الشيفرة المصدرية إلى الداخل فقط. يجب ألا تشير الدوائر الداخلية (الكيانات وحالات الاستخدام) مطلقًا إلى الدوائر الخارجية (المتحكمات وORMs والأطر). يتجه تدفق التحكم وقت التشغيل إلى الخارج عبر الواجهات، لكن وقت الترجمة/الاستيراد لا تستورد أي طبقة داخلية شيئًا من طبقة خارجية.
- الكيانات: قواعد المؤسسة
- حالات الاستخدام: قواعد التطبيق
- المحوّلات: المتحكمات وطبقات العرض والبوابات
- الأطر وأدوات التشغيل: قاعدة البيانات وHTTP والويب
مثال على طبقات مقترنة
إليكم نوع الخدمة الذي توفره معظم تطبيقات PHP. لاحظوا أن منطق المجال متشابك مع Eloquent واستجابة HTTP. ولا يمكنكم اختبار قاعدة الخصم باختبار وحدة دون قاعدة بيانات وإطار عمل.
<?php
class OrderService
{
public function place(Request $request)
{
$user = User::find($request->user_id); // Eloquent
$total = 0;
foreach ($request->items as $i) {
$total += Product::find($i['id'])->price * $i['qty'];
}
if ($user->is_vip) {
$total *= 0.9; // business rule trapped in infra code
}
Order::create(['user_id' => $user->id, 'total' => $total]);
return response()->json(['total' => $total]);
}
}الكيانات: مجال مستقل عن الإطار
يشفّر الكيان القواعد الشاملة للمؤسسة ولا يعتمد على أي شيء. إنه PHP عادي، بلا تعليقات توضيحية وبلا صنف أساسي من ORM. ويمكن إنشاؤه بالكامل داخل اختبار.
<?php
final class Money
{
public function __construct(public readonly int $cents) {
if ($cents < 0) throw new InvalidArgumentException('negative money');
}
public function multiply(float $factor): self {
return new self((int) round($this->cents * $factor));
}
}
final class Order
{
/** @param array<int,int> $lineCents */
public function __construct(private array $lineCents, private bool $vip) {}
public function total(): Money {
$sum = array_sum($this->lineCents);
$money = new Money($sum);
return $this->vip ? $money->multiply(0.9) : $money;
}
}
echo (new Order([1000, 2000], true))->total()->cents, PHP_EOL; // 2700تملك حالات الاستخدام سير العمل
تنسّق حالة الاستخدام (interactor) الكيانات وتتحدث مع العالم الخارجي عبر الواجهات (المنافذ) فقط. فهي تستقبل DTO للطلب وتعيد DTO للاستجابة — وليس كائن HTTP مطلقًا.
<?php
interface OrderRepository {
public function save(Order $order): void;
}
final class PlaceOrder
{
public function __construct(private OrderRepository $orders) {}
public function execute(array $lineCents, bool $vip): int {
$order = new Order($lineCents, $vip);
$this->orders->save($order);
return $order->total()->cents;
}
}الحد هو واجهة
تعلن حالة الاستخدام عن واجهة OrderRepository التي تحتاج إليها. وتوجد الواجهة في الدائرة الداخلية؛ بينما يوجد تطبيق Eloquent/Doctrine الفعلي في الخارج ويعتمد على الداخل. وهذا تطبيق لمبدأ عكس التبعية عند حد معماري.
اتجاه تبعية الشيفرة المصدرية: EloquentOrderRepository → OrderRepository (الواجهة)، وليس العكس مطلقًا.
<?php
// Lives in infrastructure layer, points INWARD to the domain interface
final class EloquentOrderRepository implements OrderRepository
{
public function save(Order $order): void {
OrderModel::create(['total' => $order->total()->cents]);
}
}الاختبار دون بنية تحتية
بما أن حالة الاستخدام تعتمد على واجهة، تحقن الاختبارات بديلًا وهميًا. فلا توجد قاعدة بيانات ولا حاجة إلى تشغيل الإطار — بل اختبارات وحدة سريعة جدًا تستغرق ميكروثواني وتتحقق من سلوك الأعمال الخالص.
<?php
final class InMemoryOrders implements OrderRepository {
public array $saved = [];
public function save(Order $o): void { $this->saved[] = $o; }
}
$repo = new InMemoryOrders();
$useCase = new PlaceOrder($repo);
$total = $useCase->execute([1000, 2000], true);
assert($total === 2700);
assert(count($repo->saved) === 1);
echo "PASS total=$total saved=" . count($repo->saved) . PHP_EOL;تتحول المتحكمات إلى محوّلات رقيقة
أصبح المتحكم الآن محوّلًا: فهو يترجم HTTP إلى استدعاء لحالة الاستخدام، ثم يترجم النتيجة إلى HTTP. ولا يحتوي على أي قواعد أعمال. ويمكنكم استبدال REST بواجهة CLI أو عامل لمعالجة الطوابير دون المساس بحالة الاستخدام.
<?php
final class OrderController
{
public function __construct(private PlaceOrder $placeOrder) {}
public function store(Request $request): JsonResponse {
$total = $this->placeOrder->execute(
lineCents: $request->input('lineCents'),
vip: (bool) $request->input('vip'),
);
return new JsonResponse(['total' => $total], 201);
}
}البنية التي تصرخ بالمجال
ينبغي أن تُظهر بنية المجلدات المجال بوضوح، لا الإطار. تجنبوا وضع Controllers/ وModels/ في المستوى الأعلى. ونظّموا الشيفرة حسب القدرة حتى يرى المنضم الجديد ما يفعله التطبيق.
src/Ordering/Domain/— الكيانات وكائنات القيمةsrc/Ordering/Application/— حالات الاستخدام وواجهات المنافذsrc/Ordering/Infrastructure/— مستودعات Eloquent ومتحكمات HTTP
كل سياق محدود مجلد من المستوى الأعلى؛ ويعيش الإطار عند الأطراف.
فرض قاعدة التبعية
تتآكل الانضباطية دون أدوات. استخدموا deptrac أو phparkitect في CI لإفشال عملية البناء عندما يستورد Domain من Infrastructure. وهكذا تصبح القاعدة ضمانًا وقت الترجمة بدل أن تكون أملًا يعتمد على مراجعة الشيفرة.
# deptrac.yaml
deptrac:
layers:
- name: Domain
collectors: [{ type: directory, value: src/.*/Domain/.* }]
- name: Application
collectors: [{ type: directory, value: src/.*/Application/.* }]
- name: Infrastructure
collectors: [{ type: directory, value: src/.*/Infrastructure/.* }]
ruleset:
Domain: [] # Domain may depend on nothing
Application: [Domain]
Infrastructure: [Application, Domain]عبور الحدود باستخدام DTOs
لمنع تسرّب الكيانات إلى الخارج، تعبر البيانات الحدود في صورة DTO بسيط، لا في صورة كيان أو نموذج ORM. وتعيد حالة الاستخدام بنية مسطحة يستطيع المحوّل إجراء تسلسل لها، وبذلك لا يغادر كائن المجال النواة ولا تحصل الطبقة الخارجية على مرجع إلى الحالة الداخلية.
<?php
final class OrderSummary // boundary DTO, no behavior, no domain types
{
public function __construct(
public readonly string $orderId,
public readonly int $totalCents,
) {}
}
final class PlaceOrderV2 {
public function __construct(private OrderRepository $orders) {}
public function execute(array $lineCents, bool $vip): OrderSummary {
$order = new Order($lineCents, $vip);
$this->orders->save($order);
return new OrderSummary('ord_1', $order->total()->cents);
}
}اختبار سريع
ما اتجاه التبعية المسموح به بموجب قاعدة التبعية؟
مراجعة
لقد انتقلتم من حزمة طبقية مقترنة إلى Clean Architecture:
- قاعدة التبعية: تشير تبعيات الشيفرة المصدرية إلى الداخل فقط.
- تحتفظ الكيانات بقواعد المؤسسة في PHP مستقل عن الإطار.
- تنظّم حالات الاستخدام العمل عبر واجهات المنافذ، وتعيد DTOs لا HTTP.
- المتحكمات ومستودعات ORM هي محوّلات خارجية تعتمد على الداخل (DIP).
- ينبغي أن تُظهر البنية المجال بوضوح، وتفرض أدوات مثل deptrac القاعدة في CI.
تعلم PHP مع معلم ذكاء اصطناعي — مجانًا
اكتب وقم بتشغيل أكوادك الفعلية في المتصفح، واحصل على مساعدة فورية من معلم ذكاء اصطناعي متاح 24/7، واستمر من حيث توقفت على الويب أو في التطبيق.
- الدورات
- 49
- الدروس
- 195
الأسئلة الشائعة
هل درس «من البنية متعددة الطبقات إلى البنية النظيفة» مجاني؟
نعم — نص درس «من البنية متعددة الطبقات إلى البنية النظيفة» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة PHP Academy، انتقل إلى CoddyKit PRO. تتضمن دورة PHP Academy 4 دروس في المجموع.
ماذا ستتعلم في «من البنية متعددة الطبقات إلى البنية النظيفة»؟
افهم سبب توجّه الاعتماديات إلى الداخل. تتمرن على PHP Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ PHP Academy؟
لا تُشترط خبرة سابقة. PHP Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.
كم من الوقت يستغرق درس «من البنية متعددة الطبقات إلى البنية النظيفة»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس PHP Academy هذا؟
نعم. كل درس في PHP Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- من البنية متعددة الطبقات إلى البنية النظيفة
- شرح المنافذ والمحوّلات
- حالات الاستخدام وخدمات التطبيق
- عكس الاعتماديات في التطبيق