انتقل إلى المحتوى الرئيسي

الفصل 1: مقدمة في الخدمات المصغرة

🎯 أهداف الفصل​

  • فهم الفرق بين النظام الأحادي والخدمات المصغرة
  • تحديد مزايا وعيوب كل نهج
  • اكتشاف الأنماط الأساسية للخدمات المصغرة
  • إتقان مبادئ تطبيق 12-Factor
  • معرفة متى تختار أي بنية

1. البنية الأحادية​

1.1 التعريف​

النظام الأحادي هو تطبيق حيث يتم نشر جميع الوظائف معًا في عملية واحدة.

1.2 مثال: التجارة الإلكترونية الأحادية​

// هيكل نموذجي لنظام أحادي
src/main/java/com/ecommerce/
├── EcommerceApplication.java
├── config/
│ └── SecurityConfig.java
├── controller/
│ ├── UserController.java
│ ├── ProductController.java
│ ├── OrderController.java
│ ├── PaymentController.java
│ └── InventoryController.java
├── service/
│ ├── UserService.java
│ ├── ProductService.java
│ ├── OrderService.java
│ ├── PaymentService.java
│ └── InventoryService.java
├── repository/
│ ├── UserRepository.java
│ ├── ProductRepository.java
│ ├── OrderRepository.java
│ └── ...
└── model/
├── User.java
├── Product.java
├── Order.java
└── ...

1.3 مزايا النظام الأحادي​

الميزةالتفسير
سهولة التطويرمشروع واحد، بيئة تطوير واحدة
نشر بسيطملف JAR/WAR واحد للنشر
اختبارات سهلةاختبارات تكامل بسيطة
المعاملات ACIDقاعدة بيانات واحدة = معاملات سهلة
تصحيح أخطاء بسيطتتبع كامل للأخطاء
أداء محليلا يوجد تأخير شبكي بين الوحدات
إعادة هيكلة سهلةبيئة التطوير يمكنها إعادة التسمية في كل مكان

1.4 عيوب النظام الأحادي​

العيبالتفسير
توسيع الكل أو لا شيءمن المستحيل توسيع وحدة واحدة فقط
نشر محفوف بالمخاطرخطأ واحد = تعطل التطبيق بالكامل
تكديس فريدكل شيء في Java (أو غيرها)
وقت بناء طويلإعادة تجميع كل شيء في كل مرة
صراعات الفريقدمج صعب في مشروع كبير
ديون تقنيةصعوبة في التحديث
تدريب طويليجب على المطور الجديد فهم كل شيء

1.5 متى يصبح النظام الأحادي مشكلة​

السنة 1: شركة ناشئة
- 3 مطورين
- 50 ألف سطر من الكود
- نشر: 5 دقائق
→ نظام أحادي مثالي ✅

السنة 3: نمو
- 30 مطور
- 500 ألف سطر من الكود
- نشر: 45 دقيقة
- صراعات Git يومية
→ النظام الأحادي يصبح عائقًا ⚠️

السنة 5: توسع
- 100 مطور
- 2 مليون سطر من الكود
- نشر: ساعتان
- الجمعة السوداء: كل شيء يتعطل
→ النظام الأحادي = "كرة طين كبيرة" ❌

2. بنية الخدمات المصغرة​

2.1 التعريف​

الخدمات المصغرة هي نهج حيث يتم تقسيم التطبيق إلى خدمات صغيرة مستقلة، كل منها:

  • قابلة للنشر بشكل مستقل
  • لها قاعدة بيانات خاصة بها
  • تتواصل عبر API (HTTP/REST أو الرسائل)
  • تُدار بواسطة فريق صغير

2.2 الخصائص الرئيسية​

1. مسؤولية واحدة​

كل خدمة تقوم بشيء واحد جيدًا:
- user-service: إدارة المستخدمين فقط
- order-service: إدارة الطلبات فقط
- inventory-service: إدارة المخزون فقط

2. الاستقلالية​

كل خدمة:
- لها مستودع Git خاص بها
- لها قاعدة بيانات خاصة بها (قاعدة بيانات لكل خدمة)
- لها خط أنابيب CI/CD خاص بها
- يمكن نشرها بمفردها
- يمكنها استخدام تقنية مختلفة

3. الاتصال​

متزامن (HTTP/REST):
- العميل ينتظر الرد
- بسيط ولكنه يربط

غير متزامن (الرسائل):
- أرسل وانسى
- غير مرتبط ولكنه معقد

2.3 مزايا الخدمات المصغرة​

الميزةالتفسير
توسيع مستهدفتوسيع الخدمة التي تحتاج إلى ذلك فقط
عزل الأعطالتعطل خدمة واحدة ≠ تعطل الكل
نشر مستقلنشر 10 مرات في اليوم لكل خدمة
تعدد اللغاتJava، Node، Python، Go... حسب الحاجة
فرق مستقلة"أنت تبنيها، أنت تديرها"
قابلية التوسعإضافة خدمة دون التأثير على الأخرى
التحديثإعادة كتابة خدمة واحدة في كل مرة

2.4 عيوب الخدمات المصغرة​

العيبالتفسير
تعقيد موزعتصحيح الأخطاء صعب
تأخير الشبكةمكالمات HTTP بين الخدمات
المعاملات الموزعةACID مستحيل، تحتاج إلى Sagas
اتساق محتملالبيانات ليست متزامنة فورًا
عمليات معقدةمراقبة، تسجيل، تتبع
تكلفة البنية التحتيةالمزيد من الخوادم، المزيد من قواعد البيانات
تكلفة إضافيةكل خدمة = تكلفة ذاكرة إضافية

3. مقارنة مفصلة​

3.1 جدول مقارنة​

المعيارالنظام الأحاديالخدمات المصغرة
التعقيد الأوليمنخفضمرتفع
التعقيد على المدى الطويلمرتفعمُدار
النشرالكل أو لا شيءمستقل
التوسيععموديأفقي
التكنولوجيافريدةمتعددة اللغات
الفريقفريق واحدفرق لكل خدمة
قاعدة البياناتمشتركةلكل خدمة
المعاملاتACID بسيطةSagas (معقدة)
تصحيح الأخطاءبسيطموزع (معقد)
التأخيرمنخفضأعلى
التكلفة الأوليةمنخفضةمرتفعة
الوقت للسوقسريع في البدايةأبطأ في البداية

3.2 معايير القرار​


4. أنماط الخدمات المصغرة الأساسية​

4.1 نمط بوابة API​

المسؤوليات:

  • توجيه الطلبات
  • المصادقة/التفويض
  • تحديد المعدل
  • موازنة التحميل
  • إنهاء SSL
  • التخزين المؤقت
  • تحويل الطلب/الرد

4.2 نمط اكتشاف الخدمة​

كيف يعمل:

  1. تبدأ الخدمة → تسجل نفسها في Eureka
  2. يريد العميل الاتصال بخدمة A
  3. يطلب العميل من Eureka: "أين خدمة A؟"
  4. يعيد Eureka قائمة الأمثلة
  5. يختار العميل مثالًا (موازنة التحميل من جانب العميل)
  6. يتصل العميل مباشرة بالمثال

4.3 نمط قاطع الدائرة​

الحالات:

  • مغلق: عادي، تمر المكالمات
  • مفتوح: الكثير من الفشل، المكالمات محظورة → تراجع
  • نصف مفتوح: اختبار إذا عادت الخدمة
// مثال مع Resilience4j
@CircuitBreaker(name = "userService", fallbackMethod = "getUserFallback")
public User getUser(Long id) {
return userServiceClient.getUser(id); // اتصال بعيد
}

public User getUserFallback(Long id, Exception ex) {
return new User(id, "غير معروف", "الخدمة غير متاحة مؤقتًا");
}

4.4 نمط Saga (المعاملات الموزعة)​

في نظام أحادي:

@Transactional
public void createOrder(Order order) {
orderRepository.save(order); // ✅
inventoryService.reserve(order); // ✅
paymentService.charge(order); // ❌ فشل → إلغاء الكل
}

مع الخدمات المصغرة (Saga Choreography):

4.5 Event Sourcing & CQRS​

الأحداث بدلاً من الحالات:

// بدلاً من: UPDATE orders SET status = 'SHIPPED'
// نخزن الأحداث:
OrderCreated { orderId: 1, items: [...], timestamp: ... }
PaymentReceived { orderId: 1, amount: 100, timestamp: ... }
OrderShipped { orderId: 1, trackingNumber: "ABC123", timestamp: ... }

5. تطبيق 12-Factor​

مبادئ 12 عامل هي مبادئ لبناء تطبيقات سحابية:

العوامل الـ12​

#العاملالوصفتطبيق Spring Boot
1قاعدة الكودمستودع واحد = تطبيق واحدمستودع Git لكل خدمة
2التبعياتالتصريح بوضوحpom.xml / build.gradle
3التكوينالتكوين في البيئةapplication.yml + متغيرات البيئة
4الخدمات الداعمةالتعامل معها كموارد مرفقةمصدر بيانات قابل للتكوين
5البناء، الإصدار، التشغيلفصل المراحلخط أنابيب CI/CD
6العملياتبدون حالةالجلسة في Redis
7ربط المنافذالتصدير عبر المنفذTomcat مدمج
8التزامنالتوسع عبر العملياتأمثلة متعددة
9التخلصبدء/إيقاف سريعإيقاف سلس
10التكافؤ بين التطوير والإنتاجبيئات متشابهةDocker
11السجلاتالتعامل معها كتيارstdout → جامع
12العمليات الإداريةمهام إدارية لمرة واحدةSpring Batch

مثال: العامل 3 - التكوين​

# ❌ سيء: تكوين مدمج
spring:
datasource:
url: jdbc:postgresql://localhost:5432/mydb
username: admin
password: secret123

# ✅ جيد: تكوين خارجي
spring:
datasource:
url: ${DATABASE_URL}
username: ${DATABASE_USERNAME}
password: ${DATABASE_PASSWORD}

مثال: العامل 6 - العمليات بدون حالة​

// ❌ سيء: حالة في الذاكرة
@RestController
public class CartController {
private Map<Long, Cart> carts = new HashMap<>(); // حالة محلية!

@PostMapping("/cart/{userId}/add")
public void addItem(@PathVariable Long userId, @RequestBody Item item) {
carts.computeIfAbsent(userId, Cart::new).add(item);
}
}

// ✅ جيد: حالة خارجية (Redis)
@RestController
public class CartController {
private final RedisTemplate<String, Cart> redis;

@PostMapping("/cart/{userId}/add")
public void addItem(@PathVariable Long userId, @RequestBody Item item) {
String key = "cart:" + userId;
Cart cart = redis.opsForValue().get(key);
if (cart == null) cart = new Cart();
cart.add(item);
redis.opsForValue().set(key, cart);
}
}

6. التصميم الموجه بالمجال (DDD) والخدمات المصغرة​

6.1 السياق المحدود​

يحدد السياق المحدود حدود الخدمة المصغرة:

6.2 نفس المفهوم، سياقات مختلفة​

// في سياق المبيعات
public class Customer {
private Long id;
private String name;
private String email;
private List<Order> orders;
private LoyaltyLevel loyaltyLevel;
}

// في سياق الشحن
public class Customer {
private Long id;
private String name;
private Address shippingAddress;
private String phoneNumber;
}

// في سياق الفوترة
public class Customer {
private Long id;
private String name;
private PaymentMethod defaultPaymentMethod;
private BillingAddress billingAddress;
}

7. الأنماط المضادة لتجنبها​

7.1 النظام الأحادي الموزع​

❌ نمط مضاد: النظام الأحادي الموزع
- 10 خدمات يجب نشرها معًا
- مخطط قاعدة بيانات مشترك
- ارتباط قوي بين الخدمات
- أسوأ من العالمين!

7.2 الخدمات النانوية​

❌ نمط مضاد: خدمات صغيرة جدًا
- خدمة لكل كيان
- UserService، UserAddressService، UserPreferenceService
- تكلفة إضافية هائلة
- معاملات مستحيلة

7.3 قاعدة بيانات مشتركة​

❌ نمط مضاد: قاعدة بيانات مشتركة
خدمة A ──┬──> PostgreSQL <──┬── خدمة B
│ │
└──> جدول الطلبات <┘

المشكلة:
- ارتباط قوي عبر المخطط
- من المستحيل تعديل جدول
- معاملات متقاطعة مغرية

7.4 كل شيء متزامن​

❌ نمط مضاد: كل شيء متزامن

خدمة الطلب
↓ HTTP متزامن
خدمة المخزون
↓ HTTP متزامن
خدمة الدفع
↓ HTTP متزامن
خدمة الإشعار

المشكلة:
- تأخير متراكم
- خدمة بطيئة = كل شيء بطيء
- فشل متسلسل

8. الانتقال من النظام الأحادي إلى الخدمات المصغرة​

8.1 استراتيجية: نمط شجرة التين الخانقة​

8.2 خطوات الانتقال​

  1. تحديد السياقات المحدودة في النظام الأحادي
  2. ابدأ بالأقل خطورة (مثل: الإشعارات)
  3. استخراج إلى خدمة جديدة
  4. التوجيه عبر بوابة API
  5. إزالة الكود القديم من النظام الأحادي
  6. التكرار للسياق التالي

9. ملخص​

النقاط الرئيسية​

✅ النظام الأحادي = بسيط ولكنه لا يتوسع
✅ الخدمات المصغرة = معقدة ولكنها مرنة
✅ ابدأ بالنظام الأحادي إذا كان ممكنًا
✅ انتقل عندما يصبح النظام الأحادي عائقًا
✅ تطبيق 12-Factor = ممارسات جيدة للسحابة
✅ DDD = يساعد في تحديد الحدود
✅ الأنماط = قاطع الدائرة، Saga، Event Sourcing

قائمة التحقق قبل الخدمات المصغرة​

  • فريق ذو خبرة في DevOps؟
  • بنية تحتية Kubernetes/Docker؟
  • مراقبة وتسجيل مركزي؟
  • CI/CD ناضج؟
  • سياقات محدودة جيدًا؟
  • مشاكل حقيقية في التوسع؟

10. اختبار التحقق​

السؤال 1​

أي نمط يسمح بتجنب سلسلة من الأعطال بين الخدمات؟

عرض الإجابة

قاطع الدائرة - يكتشف الأعطال و"يفتح" الدائرة لإرجاع تراجع فورًا بدلاً من انتظار انتهاء المهلة.

السؤال 2​

لماذا "قاعدة بيانات لكل خدمة" مهم؟

عرض الإجابة
  • فك الارتباط: يمكن لكل خدمة تطوير مخططها بشكل مستقل
  • الاستقلالية: لا توجد تبعية على مسؤول قاعدة بيانات مركزي
  • التكنولوجيا: PostgreSQL لخدمة، MongoDB لأخرى
  • تجنب الارتباط المخفي عبر المخطط المشترك

السؤال 3​

ما هو "النظام الأحادي الموزع"؟

عرض الإجابة

نمط مضاد حيث لدينا عدة خدمات ولكن:

  • يجب نشرها معًا
  • تشارك قاعدة بيانات
  • مرتبطة بقوة

لدينا تعقيد الخدمات المصغرة بدون الفوائد.


الفصل التالي: اكتشاف الخدمة مع Eureka →

العودة إلى الوحدة 13