انتقال للمقال

مبدأ الـ Consistency

السلام عليكم ورحمة الله وبركاته

وقت القراءة: ≈ 5 دقائق

المقدمة


مبدأ الـ Consistency هو المبدأ الذي يضمن لك أن الـ Transaction بعد تنفيذها
ستجعل البيانات في الـ Database متناسقة
أي البيانات ستحفظ بشكل صحيح وسليم داخل الـ Database ولن يقوم الـ Transaction
بافسادها أو التلاعب بها

في الحقيقة مبدأ الـ Consistency يعني الكثير من الأشياء منها أن الـ
Transaction يجب أن تحترم القواعد والقيود التي تم وضعها على الـ Database
بمعنى أن أي Constraint سواء كان Primary Key أو Foreign Key أو Unique
قيمة Default أو أي قيد آخر يجب أن تحترمه الـ Transaction ولا تخالفه

أنواع التناسق

مبدأ الـ Consistency أيضًا يعني أنه لا يجب أن يكون هناك أي تعارض بين البيانات
فمثلًا لنتخيل أن هناك شخص يدعى Ahmed ولديه ID رقم 10 وقام هذا الشخص بشراء
منتج يدعى Laptop ولديه ID رقم 20
لذا قمنا بتسجيل هذه العملية في جدول يسمى Orders وقلنا أنه تم شراء المنتج
Laptop الـ ID صاحب الـ 20 من قبل Ahmed صاحب الـ ID رقم 10

Users Table
+----+-------+
| id | name  |
+----+-------+
| 10 | Ahmed |
+----+-------+

Products Table
+----+--------+
| id | name   |
+----+--------+
| 20 | Laptop |
+----+--------+

Orders Table
+----+---------+------------+------------+------------+
| id | user_id | product_id | sold_at    | arrived_at |
+----+---------+------------+------------+------------+
| 30 | 10      | 20         | 2024-11-01 | 2024-11-05 |
+----+---------+------------+------------+------------+

هكذا البيانات تبدو متناسقة وسليمة ولا توجد بها أي تعارض
وما ليس بالمتناسق هو أن يتم تسجيل البيانات بشكل خاطئ فمثلًا
نسجل البيانات بشكل خاطئ ونقول أن الـ product_id هو 2 بينما في الحقيقة هو
1
أو ننسبه للـ user_id مختلف أو حتى نكتب تاريخ العملية بشكل خاطئ فنقول أنه تم
شراء المنتج في 2024-10-01 بينما في الحقيقة هو في 2024-11-01
أو نقول أنه تم تسليم المنتج في الماضي أي قبل تاريخ الشراء حتى وهذا يعتبر تعارض
وخطأ


مثال أخر قد يكون لديك منتجات تم حذفها من قبل المستخدمين ولكن لا تزال موجودة في
قاعدة البيانات كـ foreign key
ويتم استخدامها في العمليات والحسابات الأخرى على الرغم من أنها تم حذفها

أو قد يكون لديك بيانات متكررة سواء تكرار في الـ Primary Key أو في الـ Unique

وهكذا من الأمور التي تجعل البيانات غير متناسقة وغير صحيحة

وهذا ما يحاول مبدأ الـ Consistency أن يحميك منه
هو ضمان أن البيانات والعمليات التي بداخل الـ Transaction سيتم تنفيذها وتخزينها
بشكل صحيح
عن طريق الـ Constraints والقيود التي تم وضعها على الـ Database وعن طريق
التحقق من البيانات والعمليات قبل تنفيذها

مثال على عدم التناسق

المبدأ شامل أي شيء قد يؤدي إلى تعارض البيانات أو تخزينها بشكل خاطئ
أذكر مثال أنني كنت أحدث صورتي الشخصية في أحد المواقع
وبعد تحديث الصورة قامت الصفحة بعرض الصورة الجديدة
لكن الغريب أن تطبيق الهاتف الخاص بالموقع ظل يعرض الصورة القديمة لعدة أيام
وصورتي القديمة كانت عند بعض الأشخاص والصورة الجديدة تظهر عند البعض الآخر

وهكذا ... هذا أيضًا يدخل ضمن مبدأ الـ Consistency
وفي هذه الحالة يوجد عدم تناسق بين بيانات الشخص عبر المنصات المختلفة والأجهزة
المختلفة
لعل السبب قد يكون Caching أو CDN أو أن بياناتي تحدثت في Database في أماكن
ومناطق مختلفة ولم تتم مزامنتها بشكل صحيح
أو تأخرت عملية التحديث في بعض الأماكن ولم تتم بشكل سريع وهكذا

لكن هذا مبدأ الـ Consistency بشكل عام، نحن هنا نتكلم عن الـ Consistency في
الـ Transaction الواحدة
وهو أن الـ Transaction يجب أن تحترم القيود والقواعد التي تم وضعها على الـ
Database ولا تخالفها
وأن تحدث البيانات بشكل صحيح ومتناسق ولا توجد بها أي تعارضات أو تناقضات

تطبيق بسيط لفائدة الـ Consistency

لنتخيل أن لدينا جدول للـ Products وجدول آخر للـ Categories وجدول آخر للـ
ProductCategories

بحيث أن هناك أكثر من Product لديه أو ينتمي إلى أكثر من Category وهذا العلاقة
تسمى Many to Many لذا سيتم تخزينها في جدول الـ ProductCategories

ونتخيل أن الـ Database تحتوي على البيانات التالية

Products Table
+----+--------+
| id | name   |
+----+--------+
| 10 | Laptop |
| 20 | Mouse  |
| 30 | Chair  |
+----+--------+

Categories Table
+-----+--------+
| id  | name   |
+-----+--------+
| 100 | Tech   |
| 200 | Home   |
+-----+--------+

ProductCategories Table
+----+------------+-------------+
| id | product_id | category_id |
+----+------------+-------------+
| 1  | 10         | 100         |
| 3  | 20         | 100         |
| 2  | 30         | 100         |
| 4  | 30         | 200         |
+----+------------+-------------+

الآن لنفترض أننا نريد إضافة منتج جديد ونريد أن ينتمي إلى الفئة Tech
ونريد أن نتأكد من أن الـ Product سيتم تخزينه بشكل صحيح وأنه سينتمي إلى الفئة
Tech
وأيضا سيتم إضافتهما في جدول الـ ProductCategories

public function addProduct(Request $request)
{
    $request->validate([
        'name' => 'required|string',
        'category_id' => 'required|integer|exists:categories,id',
    ]);

    Product::create([
        'name' => $request->name,
    ]);

    return response()->json([
        'message' => 'The product has been added successfully',
    ], 201);
}

حسنًا هذه الدالة تقوم بإضافة منتج جديد وتقوم بتخزينه في جدول الـ Products
أين المشكلة هنا ؟ .. هل الدالة تنفذ المطلوب ؟

الإجابة هي لا

لأن الدالة لا تقوم بتخزين الـ Product في جدول الـ ProductCategories
بالتالي لن يتم تخزين العلاقة بين الـ Product والـ Category وهذا لا يعد
Consistency

حدود الـ Consistency

انتظر هذا خطأ بشري ؟

حسنًا ومن قال أن الـ Consistency سيحميك من الأخطاء البشرية ؟
الـ Consistency مبدأ يهتم بالبيانات والعمليات التي تخزن في الـ Database
لكن هناك أمور لن يستطيع مبدأ الـ Consistency أو حتى الـ ACID حمايتك منها
وهي هل أنت كتبت الدالة بشكل صحيح من الأساس ؟ الـ Consistency لن يقول لك
لقد نسيت كتابة الجزء الخاص بالـ ProductCategories

مبدأ الـ Consistency ينظر إلى تطبيقك بشكل عام ويقول لك هل البيانات التي تم
تخزينها في الـ Database متناسقة وصحيحة أم لا
ففي بعض الحالات يكون الـ Consistency مجرد كلام نظري وليس عملي مثل الحالة
السابقة يجب على الدالة أن تنفذ المطلوب بشكل صحيح
لكن ماذا لو نسيت شيء هل سيحميك الـ Consistency ؟ بالطبع لا فهو لن يكتب الدالة
بدلًا عنك

هناك حدود لكل شيء والـ Consistency لن يحميك من الأخطاء البشرية
الـ Consistency قد يحميك وينبهك من الأخطاء التي قد تحدث بسبب الـ Database أو
الـ Transaction ولكن ليس من الأخطاء البشرية

الحل الصحيح

الآن لنقم بتحسين الدالة السابقة ونضع الجزء الخاص بالـ ProductCategories

public function addProduct(Request $request)
{
    $request->validate([
        'name' => 'required|string',
        'category_id' => 'required|integer|exists:categories,id',
    ]);

    $product = Product::create([
        'name' => $request->name,
    ]);

    ProductCategory::create([
        'product_id' => $product->id,
        'category_id' => $request->category_id,
    ]);


    return response()->json([
        'message' => 'The product has been added successfully',
    ], 201);
}

حسنًا الآن الدالة تقوم بإضافة المنتج وتقوم بإضافته إلى الفئة المحددة وتخزينه في
جدول الـ ProductCategories
هل قد يحدث خطأ هنا ؟

الإجابة هي نعم لكن أين وكيف ؟

ماذا لو كان الـ Product لكن عندما حاول تخزينه في الـ ProductCategories حدث
خطأ ما ؟ حصل Database Exception أو Server Error أو أي شيء آخر
في هذه الحالة الـ Product قد تم تخزينه في الـ Products ولكن لم يتم تخزينه في
الـ ProductCategories

هنا أيضًا الـ Database لن تكون متناسقة أو تتبع مبدأ الـ Consistency لأنها بها
خلل بحيث الـ Product لديه Category ولكن لم يتم تخزينه في الـ
ProductCategories

الحل باستخدام Transaction

ماذا نفعل الآن ؟ كيف نتجنب هذا الخطأ ؟
نحتاج شيء يضمن لنا أنه إذا حدث خطأ في أي جزء من الـ Transaction فسيتم التراجع
عن كل شيء تم تنفيذه ولن يتم تطبيق أي شيء على الـ Database
... نعم نحتاج إلى مبدأ الـ Atomicity .. وكيف نحقق ذلك ؟ .. بالطبع بوضع الدالة
داخل Transaction

public function addProduct(Request $request)
{
    $request->validate([
        'name' => 'required|string',
        'category_id' => 'required|integer|exists:categories,id',
    ]);

    DB::transaction(function () use ($request) {
        $product = Product::create([
            'name' => $request->name,
        ]);

        ProductCategory::create([
            'product_id' => $product->id,
            'category_id' => $request->category_id,
        ]);
    });

    return response()->json([
        'message' => 'The product has been added successfully',
    ], 201);
}

الآن الدالة تقوم بإضافة المنتج وتقوم بإضافته إلى الفئة المحددة وتخزينه في جدول
الـ ProductCategories
الدالة من الناحية العملية تبدو جيدة وتقوم بكل ما نريد

وأيضًا لاحظ أننا وضعنا الدالة داخل Transaction هكذا سيقوم مبدأ الـ
Consistency بجزءه العملي وهو ضمان أن البيانات تتبع الـ Constraints والقيود
الموجودة على الـ Database
وأيضًا ضمننا معنا مبدأ الـ Atomicity وهو ساعدنا في حال حدوث Exception ونحتاج
للتراجع عن كل شيء تم تنفيذه
بالتالي مبدأ الـ Atomicity ساعدنا في تحقيق مبدأ الـ Consistency

ملخص

مبدأ الـ Consistency مبدأ عام وأحيانًا قد يكون مجرد كلام نظري وليس عملي كثيرًا
لكن ما يهمنا أن الـ Transaction تم بناءها على بعض المبادئ ومن ضمنها مبدأ الـ
Consistency ليضمن عدم تلف البيانات أو تخزينها بشكل خاطئ
عن طريق احترام الـ Constraints والقيود الموجودة على الـ Database والتحقق من
البيانات قبل تنفيذها
مثل التحقق من الـ Foreign Key والـ Unique والـ Primary Key وغيرها
لكن يوجد حدود عملية خارجة عن نطاق الـ Consistency وهي الأخطاء البشرية والتي لا
يمكن للـ Consistency حمايتك منها
لكنها تخالف مبدأ الـ Consistency لأنك كمبرمج نفذت الدالة بشكل خاطئ من الأساس
وليس بسبب الـ Database