| وجه المقارنة المعمارية | وضع المنطق داخل المكونات مباشرة (Component-Based Logic) | فصل المنطق داخل خدمات مستقلة (Dedicated Services) |
|---|---|---|
| إعادة الاستخدام (Reusability) | مستحيلة أو صعبة جداً، حيث يرتبط الكود بصرياً وبشكل وثيق بمكون واحد فقط | عالية جداً، حيث يمكن حقن الخدمة واستخدامها عبر عدة مكونات مختلفة في جميع أنحاء التطبيق |
| فصل المسؤوليات (Separation of Concerns) | تتداخل أكواد واجهة المستخدم مع عمليات جلب البيانات ومعالجة الحسابات، مما يخلق ملفات ضخمة ومعقدة (`God Components`) | تتحمل المكونات عرض البيانات فقط، بينما تتولى الخدمات معالجة البيانات والاتصال بالخارج بشكل نظيف |
| قابلية الصيانة والاختبار (Maintainability & Testing) | صعبة ومرهقة، حيث يتطلب اختبار المنطق إنشاء المكون بكامل تفاصيله | سهلة وفعالة للغاية، حيث يمكن اختبار خدمات الأكواد منعزلة (`Unit Testing`) دون الحاجة لواجهة المستخدم |
يُعد فصل منطق الأعمال عن المكونات البصرية (Components) ركيزة أساسية لبناء تطبيقات قابلة للصيانة والتوسع. تُستخدم الخدمات (Services) لحفظ البيانات، التواصل مع الخوادم (APIs)، ومعالجة العمليات الحسابية أو المنطقية المعقدة، مما يترك المكونات تركز فقط على عرض واجهة المستخدم والتفاعل معها.
يجب أن تتخصص كل خدمة في مهمة معينة (مثل خدمة إدارة المستخدمين، خدمة المنتجات، أو خدمة المصادقة)، مما يجعل الكود منظماً وسهل التتبع عند التعديل أو إضافة ميزات جديدة.
بفضل طبيعة الخدمات كـ Singletons (عند توفيرها في الجذر)، يمكنها الاحتفاظ بالبيانات ومشاركتها بسلاسة بين مكونات مختلفة لا تربطها علاقة مباشرة (أب وابن).
⚠️ تحذير
تجنب وضع حالة قابلة للتغيير بشكل عشوائي في الخدمات العامة دون حماية! مشاركة البيانات عبر الخدمات دون تنظيمها باستخدام أنماط حديثة (مثل Signals) قد يؤدي إلى صعوبة تتبع مصدر التغييرات في التطبيقات الكبيرة ويسبب أخطاء غير متوقعة في واجهة المستخدم.
| 1 | import { Injectable } from '@angular/core'; |
| 2 | |
| 3 | @Injectable({ |
| 4 | providedIn: 'root', // توفير الخدمة على مستوى التطبيق بالكامل |
| 5 | }) |
| 6 | export class UserDataService { |
| 7 | private users = [ |
| 8 | { id: 1, name: 'أحمد محمد', role: 'مهندس برمجيات' }, |
| 9 | { id: 2, name: 'سارة علي', role: 'مطور واجهات أمامية' } |
| 10 | ]; |
| 11 | |
| 12 | getUsers() { |
| 13 | return this.users; |
| 14 | } |
| 15 | |
| 16 | addUser(name: string, role: string) { |
| 17 | const newUser = { id: this.users.length + 1, name, role }; |
| 18 | this.users.push(newUser); |
| 19 | return newUser; |
| 20 | } |
| 21 | } |
| 1 | import { Component, inject } from '@angular/core'; |
| 2 | import { CommonModule } from '@angular/common'; |
| 3 | import { UserDataService } from './user-data.service'; |
| 4 | |
| 5 | @Component({ |
| 6 | selector: 'app-user-list', |
| 7 | standalone: true, |
| 8 | imports: [CommonModule], |
| 9 | template: ` |
| 10 | <div class="service-container"> |
| 11 | <h3>قائمة المستخدمين (مدارة عبر الخدمة المستقلة)</h3> |
| 12 | <ul class="user-list"> |
| 13 | @for (user of users; track user.id) { |
| 14 | <li class="user-item"> |
| 15 | <span>{{ user.name }}</span> - <small>{{ user.role }}</small> |
| 16 | </li> |
| 17 | } |
| 18 | </ul> |
| 19 | <button (click) = "addNewUser()" class="add-btn">إضافة مستخدم جديد</button> |
| 20 | </div> |
| 21 | `, |
| 22 | styles: [` |
| 23 | .service-container { background: #090d16; color: #fff; padding: 24px; border-radius: 12px; border: 1px solid #1e293b; } |
| 24 | .user-list { list-style: none; padding: 0; margin-bottom: 16px; } |
| 25 | .user-item { background: #1e293b; padding: 10px 14px; border-radius: 6px; margin-bottom: 8px; border: 1px solid #334155; } |
| 26 | .add-btn { background: #38bdf8; color: #090d16; border: none; padding: 8px 16px; border-radius: 6px; font-weight: bold; cursor: pointer; } |
| 27 | `] |
| 28 | }) |
| 29 | export class UserListComponent { |
| 30 | // حقن الخدمة باستخدام دالة inject الحديثة |
| 31 | private readonly userService = inject(UserDataService); |
| 32 | |
| 33 | get users() { |
| 34 | return this.userService.getUsers(); |
| 35 | } |
| 36 | |
| 37 | addNewUser() { |
| 38 | this.userService.addUser('محمود خالد', 'خبير أنگولر'); |
| 39 | } |
| 40 | } |