Friday, March 10, 2017

ما أهمية تقسيم المشروع لطبقات layers and tiers؟

من فترة كان فيه سؤال على المجموعة Egpytian Geeks على الفيسبوك عن أهمية إني أقسم المشروع بتاعي لlayers و tiers, و هل ده هايأثر على سرعة و أداء التطبيق و لا إيه اللي هايحصل؟
ساعتها كنت كتبت كلام كتير و افتكرته دلوقتي، فقلت أظبطته شوية و أعيد نشره على صفحتي على الفيسبوك لتعم الفائدة، ثم نصحني بعض الأصدقاء بإعادة نشره المدونة دي, و عشان كده مكتوب بالعامية...
و أصل النقاش كان هنا لمن أراد الاطلاع عليه.

الفرق بين الlayers و الtiers

خلينا الأول نبين الفرق بين الtier و الlayer. الاتنين طبقات, بس لو الطبقات بيشتغلوا على نفس الكمبيوتر يبقى اسمهم layers, لو انفصلوا يبقى اسمهم tiers. بعبارة أخرى, الlayers هي logical separation أما ال tiers فهي physical separation.

هل الطبقات دي هاتأثر على طريقة أداء المشروع؟

آه طبعا...
- هاتبطأه (و لو قليلا في حالة الlayers (فيمكن إهماله) و كثيرا في حالة الtiers)
- هاتخليه أصعب في الفهم و الdebugging: تخيل إنت عمال تتنط بين الlayers عشان توصل للbug أسهل و لا لو هي ف نفس المكان معاك أسهل؟
- هاتكبر المشروع: هاتزود tier يعني هاتعمل project منفصل لكل tier و هاتحتاج تزود  web services...إلخ, و ده طبعا هايصعب الدنيا أكتر
- فيه ناس هاتقول لك هاتقدر تعيد استخدام الطبقات دي في مشاريع تانية، و الفكرة دي تسمى  reusability ، و دي انساها خالص مش هاتحصل!!! -- هاتيجي بالتفصيل تحت - إن شاء الله -
أمال ليه وجع القلب ده؟!!!
 خلينا ناخد شوية تفاصيل عشان نعرف الفايدة الحقيقية...

ليه بقسم المشروع لطبقات؟ 

 - فيه نظرية عامة في هندسة البرمجيات بتقول: إن أي مشكلة في تصميم البرامج ممكن أحلها بإضافة طبقة غير مباشرة - و الاستثناء الوحيد هو مشكلة كثرة الطبقات غير المباشرة -
 We can solve any problem by introducing an extra level of indirection....except for the problem of too many levels of indirection
ممكن تقرأ المزيد عن النظرية دي هنا

يعني إيه الكلام ده؟

 يعني أنا عندي فورمة عليها شوية كونترولز, و عندي قاعدة بيانات, ليه ما بندهش لقاعدة البيانات على طول و أريح دماغي؟
 عشان أنا عندي مشكلة (أو عدة مشاكل) عاوز أحلها, و بتطبيق النظرية السابقة, رحت عامل طبقة حطيت فيها شغل الداتابيز, و طبقة حطيت فيها البزنس بتاعي, و خليت الطبقة اللي فيها الUI بسيطة لا بتهش و لا بتنش!

طب إيه هي المشاكل اللي عاوز أحلها دي؟

تخيل الآتي:
إني لو جبت الكود بتاع البزنس + الكود بتاع الداتبيز + الكود بتاع الuii و حطيتهم في حتة واحدة, فكر معايا إيه اللي هايحصل في السيناريوهات دي:
1- كنا شغالين ال UI بasp.net web forms بس مايكروسوفت عملت حاجة جديدة اسمها asp.net mvc بس لما جيت أطبقها ما عرفتش عشان الكود ملخفن!
2- البرنامج كان صغير, و كنا شغالبن أكسس, بس لما كبر البرنامج أكسس ما استحملتش, فقلنا لازم نستخدم sql server بس ما عرفناش عشان الكود بتاع الداتابيز معتمد على أكسس, و ملخفن جوة الكود بتاع الui و محتاج تعديلات في حتت كتيرة!
3- البرنامج كان شغال desktop بس برامج الdesktop بقت ميتة, و كل الناس متجهة نحو الويب و الموبايل, و أنا كاتب كود بقالي سنين كتيرة و حرام أرميه, و أبدأه من أول و جديد, و مش عارف أسلكه من الكود الملخفن!
 .. الأمثلة من دي كتيرة, الشاهد: إن فكرة تقسيم البرنامج لطبقات فوق بعض بتديني ميزة تسهيل استبدال أي طبقة (نوعا ما) , و ده اللي بنسميه design for replacement not reusability!
لاحظ هنا إني حصلت على ميزة سهولة الاستبدال replacement مش سهولة إعادة الاستخدام reusability، طب إيه الفرق بينهم؟
الأولى تعني *إعادة الاستخدام* يعني عندي layer هاستخدمها ف حتة تانية
أما الثانية فتعني *الاستبدال*, يعني عندي layer هارميها و أجيب واحدة بدالها
اسأل أي واحد عمل كام مشروع وييب, كام مرة أخد Layer ك dlll و أعاد استخدامها في مشروع تاني؟ عمر ده ما حصل معايا أنا - على الأقل -! أقصى ما فعلته إني أخدت شوية كود copy/paste و استخدمتهم في مشروع تاني. و ده ما اسمهوش reusability ده اسمه code duplication و ده ربما يكون مصدر كل الشرور في البرمجة - كما قال روبرت مارتن!
لكن كام مرة احتجت إني استبدل layer فيها مشاكل بlayer تانية لسبب أو لآخر؟ ده حصل معايا كتيييير. عشان كده بقول لك design for replacement not reusability و ده بيفرق على فكرة في طريقة التصميم نفسها, بس ده مش مجاله دلوقتي.

طب إزاي حصلت على ميزة القابلية للاستبدال دي؟

بإني خليت كل طبقة تعمل حاجة واحدة بس, و بالتالي بقى فيه سبب واحد للعب في الطبقة دي.
مثلا: طبقة الpresentation بقت مش هالعب فيها إلا لما عاوز أعدل حاجة في طريقة العرض أو استبدلها كليا, و ده من غير ما قاعدة البيانات تتأثر. و نفس الكلام بينطبق على الطبيقات التانية.
و ده اللي بنسميه separation of concerns و فيه رواية أخرى  single responsibility principle

مشكلة كمان...

 دلوقتي أنا حاطط كل الطبقات بتاعتي على كمبيوتر واحد, بس أنا خايف إن حد يقدر يخترق الكمبيوتر ده و يحصل على البيانات اللي متسجلة في قاعدة البيانات, إيه الحل؟
Add layers of indirection!
بس الطبقات اللي هاضيفها هنا هاتكون في الحقيقة tiers مش layers بحيث لو أي tier فيهم اتضربت compromised هايكون لسه عندي خطوط دفاع تانية (الtiers اللي ورا)!
و بمجرد ما أقسم البرنامج بتاعي لtiers هاتتفتح لي آفاق تانية مثيرة جدا!!!

نرجع لمشكلة البطء...

زي ما قلنا، إن أول حاجة هاتحصل لما أقسم البرنامج على أكتر من كمبيوتر tiers إن البرنامج هايبطأ شوية!
أيوة... عشان فيه وقت هايستهلك في إن الدااتا تتنقل من كمبيوتر للتاني عبر الشبكة.
 لو الكمبيوترين دول بعاد عن بعض, و عملية نقل البيانات هاتتم عبر الإنترنت فالدنيا هاتكون أبطأ مما لو كانوا على شبكة داخلية.
 بس عشان أتغلب على مشكلات البطء دي محتاج أظبط حتت كتير في البرنامج, يعني مثلا أعتمد كتير على client = javascript و الajax calls و في السيرفر أعتمد كتير على الasync calls و هاحتاج اعمل optimization للdatabase queries و أعمل caching للبيانات...إلخ و في الآخر الدنيا هاتظبط بصورة كبيرة.
بالرغم من إن سرعة البرنامج قلت (نوعا ما) إلا إني لما عملت tiers هاقدر دلوقتي أحسن اللscalability بتاعت البرنامج, وده هايحسن السرعة جدا!
 طب إزاي؟
الscalability قصة طويلة مش ده وقتها, بس خلينا نقول إن معناها قد إيه البرنامج بتاعي هايستحمل زيادة عدد المستخدمين؟
في حالة الكمبيوتر الواحد one tier قدرة البرنامج مرتبط بقدرة الكمبيوتر اللي هو شغال عليه. البرنامج هايفضل شغال معايا كويس و زي الفل لغاية لما عدد المستخدمين يوصل لحد معين بعده البرنامج سرعته هاتقل بصورة دراماتيكية (حلوة دراماتيكية دي :)
و الحل إما تزويد إمكانات الكمبيوتر أو تزويد الكمبيوترات.
فيه كلام هنا كتير web farms - web gardens ...إلخ, بس اختصارا, توزيع البرنامج هايسمح لي إني أعمل optimization لأجزاء معينة في البرنامج تحسن الscalability بتاعته.
طب السرعة هاتزيد و لا هاتقل, آخر كلام؟ :)
لو البرنامج بتاعك صغير و عدد المستخدمين صغير, هاتحس إنها بطيئة.
لو البرنامج بتاعك كبر, و عدد المستخدمين زاد هاتحس إنها أسرع.
 ---
و ده يقودنا إن للهدف الرئيسي من كل الحوارات دي: المرونة!
لما بقسم الدنيا لأجزاء أو طبقات بيبقى عندي المرونة اللي تخيني أعمل optimizations على كل جزء على حدة.
----
و كما يظهر من كل الهري ده, إني زودت الcomplexity بتاعت البرنامج بس في المقابل حصلت على مرونة: مرونة إني أستبدل بأجزاء تعبانة في البرنامج أجزاء كويسة , مرونة إني أظبط أجزاء في البرنامج من غير ما أؤثر على أجزاء تانية..إلخ.
 ---

طيب إمتى أعقد الدنيا كده؟

لازم أختم الكلام بإن إضافة الcomplexity دي على المشروع لازم تكون مبررة, يعني المشروع طول ما هو صغير (يتعمل في أيام) مفيش داعي إني أعمل الفصلة في الكود, أي عجن في الcontroller هايكون كويس
لو المشروع كبر شوية (يتعمل في أسابيع) هنا الlayers هاتكون مناسبة
لو كبر أكتر (يتعمل في شهور) هنا الtiers هاتكون مناسبة أكتر
... و هكذا
 أو بعبارة أخرى: الفائدة من الحوارات دي بتبان لما البرنامج يكبر, لكن طول ما هو صغير فهاتفضل تحس إنه صعبة و رخمة و مالهاش فايدة كبيرة!

ما هي فائدة الORMs؟

مقدمة

كان فيه نقاش على مجموعة Egyptian geeks حول فائدة الORMs. و لقيت إني كتبت كلام كتير، فهاعيد نشره هنا لتعم الفائدة...
و ده رابط النقاش الأصلي للي عاوز يطلع عليه.
طبعا أنا كنت قائل إني هاكتب على البلوج دي بالإنجليزي، بس لقيت نفسي مش بكتب بقالي كتير، و لما نشرت الكلام ده على الفيسبوك بعض الأصدقاء نصحوني إني أنشرها على مدونتي، و عشان كده مكتوبة باللغة العامية. و يارب تكون مفيدة.

أولا: إيه لازمة الORM؟ أو إيه هي المشاكل اللي بيحلها؟


* مشكلة رقم 1: impedance mismatch و دي لو أنت اشتغلت بالتقنيات القديمة (عصر ما قبل الorm) هاتلاقي إنك كنت دايما بتعمل class و تعبي فيها النتائج بتاعت الكويريز بتاعتك، و ده أمر مرهق جدا، الorm عملهولك ببساطة. ميزة إضافية هنا إنك ممكن تربط الclass بتاعتك بكذا جدول، أو جدول واحد يتفك ف كذا class


* مشكلة رقم 2: persistence ignorance و دي تعني إنك فعليا بتتعامل مع الobjects ككobjects بعض النظر هي جاية من داتابيز و لا من ملف، و بغض النظر نوع الداتابيز دي إيه أو الملف ده إيه. دي مفيدة في بعض الحالات زي مثلا إني أغير قاعدة البيانات بتاعتي، و دي طوال سنين خبرتي لم تطلب مني إلا مرة واحدة بس!
* مشكلة رقم 3: كتابة و صيانة أكواد الsqll عملية مش لطيفة خالص، خاصة إن البزنس بتاعك بيبقى موزع على التطبيق و قاعدة البيانات، فالorm بيساعدك في تنفيذ الseparation of concerns و بيديك لغة زي ال sql بس غالبا بتكون statically typed و هذا يعني إنك ممكن تكتشف مشاكل الsyntax من غير من تشغل البرنامج، و فيه pattern مستخدم هنا اسمه object query pattern، و من أجمد التطبيقات للباترن ده: LINQ
* مشكلة رقم 4: الcaching: لما بتجيب حاجة من قاعدة البيانات، ليه تروح تجيبها تاني لما ممكن تحتفظ بيها طول الفترة اللي ممكن تحتاجها فيها؟ و عشان يعملوا الcaching ده فيه pattern بيستخدموه اسمه identity map. فيه مستويين من الcaching: المستوى الأول في الsession الواحدة، و دي موجودة في أغلب الorms. و المستوى التاني فيما بين الsessions المختلفة، و دي موجودة في بعض الorms. المستوى الأول بيسموه 1st level cache و المستوى التاني بيسموه 2nd level cache
* مشكلة رقم 5: أحيانا بيكون بيانات مرتبطة ببعضيها بس موزعة على كذا جدول، في الأول بتجيب الmaster record و بعدين بتكتب كويري منفصل عشان تجيب البيانات المرتبطة بيه من الجداول التانية. الموضوع ده بيبقى رخم لما بيبقى كتير. الorms جواها lazy loading implementation عشان يسهل عليك الحوار ده: البيانات المرتبطة هي عبارة عن reference to object و أول ما بتستعمله الorm بتروح تجيبهولك من غير ما أنت تبذل أي مجهود.
ده لذيذ جدا لغاية لما تخبط في مشكلى اسمها n+1: و دي تعني إنك مثلا عندك 1000 بيان في قاعدة البيانات و كلهم ليهم بيانات مرتبطة بالmaster records. فأنت لما تعوز تجيب البيانات المرتبطة دي بتعمل for loop كل مرة فيها الorm هايروح قاعدة البيانات من غير ما أنت تبقى واخد بالك، و كده أنت فشخت الperformance لما رحت 100 مرة + المرة الأولى اللي جبت فيها قائمة البيانات الأساسية.
و دي برضو الormm عامل لك ليها حل إنك تقدر تجيب البيانات الأساسية بالبيانات المرتبطة بيهم مرة واحدة، و ده بيسمى: eager loading

ثانيا: إيه هي المشاكل اللي بتحصل لما تستخدم الorms؟


* مشكلة رقم 1: سوء الاستخدام!!
- المبرمجين بيتستهلوا فبيعكوا في كتابة أكواد الاستعلام فبيطلع sql query زبالة، فبيأثر على الperformance!
و بسبب سوء الاستخدام ممكن تخبط في مشكلة n+1 اللي ذكرتها في التعليق السابق.
و بسبب سوء الاستخدام ممكن ما تنتبهش إن الorm بيعمل caching للobjects و تحتفظ باللsession لفترة طويلة أنت مش محتاجها (صدق أو لا تصدق: شفت برنامج بيحتاج 170 GB RAM عشان المبرمج كان بيحط الdbcontext في ال session بتاعت كل مستخدم!!!)
و ممكن نقول هنا بصفة عامة الكويريز اللي بتطلع من الOrms ما بتبقاش لطيفة!


* مشكلة رقم 2: التأثير السلبي على performance.عشان كده لازم تبقى فاهم الorm كويس عشان ما تعكش منك.
* مشكلة رقم 3: استخدامه في حالات هناك حلولا أفضل منها: مثال, إنك تستخدم الorm في التقارير، هنا مثلا أنت مش محتاج إن الorm يعمل caching للobjects و عاوزه يبقى سريع جدا، و الorm هايبطأ لك الدنيا

ثالثا: استدراكات لابد منها

1- كل نظام و له ظروفه، و الOrms لما طلعت - فيما أعلم - كانت عشان تحل مشاكل في الenterprise applications
و عشان كده لما موقع زي stackoverflow قابلته مشكلة في الperformance بقى إن الorm بوضعه الحالي مش مناسب، فعمل micro orm بيحل أول مشكلة ذكرتها فوق بس، فالنتيجة: لازال يستخدم صورة من صور الorm عشان توفر عليه شوية مجهود قد يكون كبير (حسب حجم النظام)
2- - لو أنت محتاج *بعض* وظائف الorms ما تعملهاش بنفسك، حتى لو بfunction صغيرة، عشان أنت كده بتعيد اختراع العجلة، خاصة لو فيه عجل كويس و هايسد معاك زي الmicro-orms!