Skip to content

هندسة الواجهة الخلفية متعددة الطبقات

السؤال الأساسي: كيف تنظم الكود عندما يصبح فوضوياً بشكل متزايد؟

عندما يتوسع المشروع من بضع عشرات من الأسطر إلى عشرات الآلاف، ومن مطور واحد إلى فريق كامل، ومن عمليات CRUD بسيطة إلى منطق أعمال معقد، فإن طريقة تنظيم الكود تحدد بشكل مباشر مصير المشروع. الهندسة متعددة الطبقات ليست للتباهي أو اتباع العقائد، بل لحل تناقض جوهري في هندسة البرمجيات: النمو الطبيعي لتعقيد الأعمال مقابل محدودية القدرة الإدراكية البشرية.


1. لماذا نحتاج إلى الطبقات؟

1.1 جذور المشكلة

النسخة الأولية (100 سطر من الكود):

java
@PostMapping("/register")
public Result register(@RequestBody User user) {
    // 1. التحقق من عدم تكرار اسم المستخدم
    if (userRepository.findByUsername(user.getUsername()) != null) {
        return Result.error("اسم المستخدم موجود مسبقاً");
    }
    // 2. تشفير كلمة المرور
    user.setPassword(encrypt(user.getPassword()));
    // 3. حفظ المستخدم
    userRepository.save(user);
    // 4. إرسال بريد الترحيب
    emailService.sendWelcome(user.getEmail());
    // 5. تسجيل السجل
    log.info("User registered: {}", user.getUsername());
    return Result.success();
}

بعد 6 أشهر (500 سطر من الكود):

  • تمت إضافة التحقق من رقم الهاتف
  • تمت إضافة التحقق من الهوية الحقيقية
  • تمت إضافة مكافآت الدعوة
  • تمت إضافة فحص المخاطر
  • ...

الآن هذا الميثود يحتوي على 500 سطر، وكل تعديل يثير القلق، لأن:

  • المنطق مختلط، وتعديل جزء قد يؤثر على وظائف أخرى
  • صعوبة في الاختبار، حيث يتطلب كل اختبار محاكاة طلب HTTP كامل
  • المطورون الجدد لا يفهمونه، لأن كل المنطق مكدس معاً

جوهر المشكلة: الكود يفتقر إلى "الحدود"، وجميع المسؤوليات مختلطة معاً.

التأثير التراكمي للديون التقنية:

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

1.2 الفكرة الأساسية للطبقات

هندسة الطبقات ترسم حدوداً واضحة للكود:

┌─────────────────────────────────────┐
│  استقبال الطلب ← Controller          │   مسؤول فقط عن "استلام الطلب"
├─────────────────────────────────────┤
│  تنسيق الأعمال ← Service             │   مسؤول فقط عن "الطهي"
├─────────────────────────────────────┤
│  الوصول إلى البيانات ← Repository    │   مسؤول فقط عن "جلب المكونات"
├─────────────────────────────────────┤
│  تعريف الأعمال ← Domain              │   مسؤول فقط عن "معايير الوصفة"
└─────────────────────────────────────┘

المبادئ الأساسية:

  • كل طبقة تقوم بعملها فقط
  • التواصل بين الطبقات عبر واجهات واضحة
  • منطق الأعمال يتركز في Service و Domain
  • منطق الوصول إلى البيانات يتركز في Repository

القيمة الهندسية لهندسة الطبقات:

  1. تقليل الحمل الإدراكي: يمكن للمطور التركيز على مسؤوليات الطبقة الحالية دون فهم التفاصيل العامة
  2. تحسين قابلية الاختبار: يمكن اختبار كل طبقة بشكل مستقل عبر اختبارات الوحدة باستخدام Mock للاعتماديات
  3. تعزيز قابلية الصيانة: عند تغيير المتطلبات، يكون نطاق التعديل واضحاً، مما يقلل المخاطر
  4. تعزيز إعادة استخدام الكود: منطق الأعمال لا يعتمد على HTTP، ويمكن إعادة استخدامه في المهام المجدولة وطوابير الرسائل
  5. دعم التعاون الجماعي: يمكن لمطورين مختلفين العمل على طبقات مختلفة بالتوازي، مما يقلل التعارضات
  6. إطالة عمر الكود: الحدود الواضحة تجعل إعادة الهيكلة والتطور أسهل

2. شرح مفصل للهندسة رباعية الطبقات

2.1 الهيكل العام

جوهر هندسة الطبقات هو فصل الاهتمامات (Separation of Concerns) والتحكم في اتجاه الاعتماديات:

┌─────────────────────────────────────────────────────┐
│  طلب الواجهة الأمامية                                 │
└────────────────────┬────────────────────────────────┘
                     │ HTTP Request

┌─────────────────────────────────────────────────────┐
│  Controller (طبقة المتحكم)                            │
│  - استقبال الطلب، التحقق من المعاملات                  │
│  - تحويل DTO                                          │
│  - استدعاء Service                                    │
│  - إرجاع الاستجابة                                    │
└────────────────────┬────────────────────────────────┘
                     │ استدعاء الأعمال

┌─────────────────────────────────────────────────────┐
│  Service (طبقة منطق الأعمال)                          │
│  - تنسيق منطق الأعمال                                 │
│  - إدارة المعاملات                                    │
│  - تنسيق عدة Repository                               │
│  - التنسيق عبر الوحدات                                │
└────────────────────┬────────────────────────────────┘
                     │ الوصول إلى البيانات

┌─────────────────────────────────────────────────────┐
│  Repository (طبقة الوصول إلى البيانات)                │
│  - عمليات CRUD على قاعدة البيانات                     │
│  - تغليف الاستعلامات                                  │
│  - تعيين ORM                                          │
└────────────────────┬────────────────────────────────┘
                     │ كائنات المجال

┌─────────────────────────────────────────────────────┐
│  Domain (طبقة نموذج المجال)                           │
│  - الكيانات (Entity)                                  │
│  - كائنات القيمة (Value Object)                       │
│  - قواعد الأعمال                                      │
└─────────────────────────────────────────────────────┘

اتجاه الاعتماديات: يجب أن تشير اعتماديات الكود نحو الاتجاه الأكثر استقراراً وتجريداً

  • Controller يعتمد على واجهة Service (تجريد)
  • Service يعتمد على واجهة Repository (تجريد)
  • جميع الطبقات تعتمد على Domain (نواة الأعمال، الأكثر استقراراً)
  • لا يُسمح بالاعتماديات العكسية (مثل اعتماد Repository على Service)
Backend Four-Layer Architecture Overview
Click a layer to see details.
Client (Web / App)
↓ HTTP
ControllerEntry
Receive requests, validate parameters, call Service
ServiceBusiness core
Orchestrate business logic, manage transactions, coordinate modules
RepositoryData access
Persistence, query encapsulation, ORM mapping
DomainDomain model
Entities, business rules, value objects
↓ SQL
Database (MySQL / PostgreSQL)

2.2 طبقة Controller

المسؤولية: "موظف الاستقبال" للطلبات

  • استقبال طلبات HTTP، تحليل المعاملات
  • التحقق من المعاملات (التنسيق، الحقول المطلوبة، إلخ)
  • تحويل DTO (Request → Param)
  • استدعاء Service لتنفيذ الأعمال
  • تحويل DTO (Result → Response)
  • إرجاع استجابة HTTP

ما لا يجب فعله:

  • كتابة منطق الأعمال مباشرة
  • التعامل مع قاعدة البيانات مباشرة
  • إدارة المعاملات

فلسفة التصميم: Controller هو "واجهة" النظام، ويؤدي دور المحول — تكييف بروتوكول HTTP الخارجي إلى استدعاءات الأعمال الداخلية. يجب ألا يحتوي على أي قرارات أعمال، لأن قرارات الأعمال هي تجسيد للمعرفة المجالية ويجب فصلها عن بروتوكول النقل.

مثال:

java
@RestController
@RequestMapping("/api/users")
public class UserController {

    private final UserService userService;

    @PostMapping
    public UserResponse createUser(
            @RequestBody @Valid UserRequest request) {

        // 1. Request DTO → Param DTO
        UserParam param = UserParam.builder()
                .username(request.getUsername())
                .password(encrypt(request.getPassword()))
                .email(request.getEmail())
                .build();

        // 2. استدعاء Service
        User user = userService.createUser(param);

        // 3. Entity → Response DTO
        return UserResponse.from(user);
    }
}

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

  • استخدام @Valid للتحقق التلقائي من المعاملات
  • استخدام DTO لعزل هياكل بيانات الواجهة الأمامية والخلفية
  • القيام فقط بـ"الترجمة" و"الجدولة"، دون احتواء منطق الأعمال
Controller Layer: Request Reception
Click flow nodes to see details.
Client sends request
POST /api/users/register
Content-Type: application/json
{ "username": "Alice", "email": "alice@example.com", "password": "123456" }
↓ Request arrives
Controller receives and parses request
@RestController
@RequestMapping("/api/users")
public class UserController {
    @PostMapping("/register")
    public ResponseEntity<UserDTO> register(
        @RequestBody @Valid UserRegisterRequest request) {
        UserDTO user = userService.register(request);
        return ResponseEntity.ok(user);
    }
}
↓ Validate parameters + call service
Parameter validation, one Controller responsibility
public class UserRegisterRequest {
    @NotBlank(message = "Username is required")
    @Size(min = 2, max = 20) private String username;
    @Email(message = "Invalid email format") private String email;
    @Size(min = 6, message = "Password needs at least 6 characters") private String password;
}
↓ Return result
Controller wraps response
HTTP/1.1 200 OK
{ "code": 200, "message": "Registered",
  "data": { "id": 10001, "username": "Alice", "email": "alice@example.com" } }
Core Controller Responsibilities
Receive request
Map HTTP request to method
Validate params
Basic format and required checks
Call Service
Forward request to business layer
Wrap response
Return unified response format

2.3 طبقة Service

المسؤولية: "طاهي" الأعمال

  • تنفيذ منطق الأعمال الأساسي
  • تنسيق عمليات عدة Repository
  • إدارة حدود المعاملات
  • معالجة التنسيق عبر الوحدات

ما لا يجب فعله:

  • كتابة SQL مباشرة (اتركها لـ Repository)
  • معالجة الأمور المتعلقة بـ HTTP
  • إرجاع كيانات قاعدة البيانات إلى Controller

فلسفة التصميم: طبقة Service هي حامل منطق الأعمال، ويجب أن تبقى نقية. لا تعتمد على أي إطار عمل أو بروتوكول نقل، وهذا يمكنها من:

  • الاختبار المستقل عن طبقة الويب عبر اختبارات الوحدة
  • إعادة الاستخدام في المهام المجدولة ومستهلكي طوابير الرسائل
  • تجنب تأثير تغييرات المكدس التقني على منطق الأعمال

مثال:

java
@Service
@RequiredArgsConstructor
public class UserService {

    private final UserRepository userRepository;
    private final EmailService emailService;

    @Transactional
    public User createUser(UserParam param) {
        // 1. قاعدة الأعمال: التحقق من عدم تكرار اسم المستخدم
        if (userRepository.existsByUsername(param.getUsername())) {
            throw new UserAlreadyExistsException();
        }

        // 2. إنشاء كيان المستخدم
        User user = new User();
        user.setUsername(param.getUsername());
        user.setPassword(param.getPassword());
        user.setEmail(param.getEmail());

        // 3. الحفظ في قاعدة البيانات
        userRepository.save(user);

        // 4. إرسال بريد الترحيب (تنسيق عبر الوحدات)
        emailService.sendWelcomeEmail(user);

        return user;
    }
}

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

  • استخدام @Transactional لضمان اتساق المعاملات
  • رمي استثناءات الأعمال، ليقوم Controller بمعالجتها بشكل موحد
  • عدم الاعتماد على مفاهيم HTTP، مما يسمح بإعادة الاستخدام
Service Layer: Business Orchestration
Choose a business scenario to see how Service coordinates logic.
E-commerce Order Flow
Placing an order involves inventory deduction, order creation, and payment records, all requiring transactional consistency.
1
Parameter validation and DTO conversion
Controller
@PostMapping("/orders")
public ResponseEntity<OrderDTO> createOrder(
    @RequestBody @Valid CreateOrderRequest request) {
    OrderDTO order = orderService.createOrder(request);
    return ResponseEntity.ok(order);
}
2
Business orchestration with transaction
Service
@Transactional
public OrderDTO createOrder(CreateOrderRequest request) {
    inventoryService.checkAndDeduct(request.getSkuId(), request.getQuantity());
    Order order = new Order();
    order.setUserId(request.getUserId());
    order.setTotalAmount(calculateTotal(request));
    orderRepository.save(order);
    Payment payment = createPayment(order);
    paymentRepository.save(payment);
    return convertToDTO(order);
}
3
Persist data
Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
    // Basic CRUD is built in
}
Service Layer Design Principles
Single responsibility
One Service owns one business area
UserService handles users, OrderService handles orders
Transaction boundary
Manage transactions declaratively in Service
Put @Transactional on Service methods
Avoid cycles
Services should not call each other in loops
A→B→A creates a cycle
DTO conversion
Convert to DTO before returning; do not expose entities
return new UserDTO(user)

2.4 طبقة Repository

المسؤولية: "أمين المستودع" للبيانات

  • تغليف كل منطق الوصول إلى البيانات
  • تنفيذ عمليات CRUD
  • معالجة تعيين ORM
  • تغليف شروط الاستعلام

ما لا يجب فعله:

  • كتابة منطق الأعمال
  • معالجة المعاملات (تديرها طبقة Service)
  • الاعتماد على الوحدات العليا

فلسفة التصميم: Repository هو طبقة تجريد للوصول إلى البيانات، تخفي تفاصيل قاعدة البيانات الأساسية. قيمة هذا التجريد تكمن في:

  • عند تغيير قاعدة البيانات، يكفي تعديل تنفيذ Repository فقط، دون تغيير منطق الأعمال
  • سهولة استخدام Mock لاختبارات الوحدة
  • إدارة مركزية لمنطق الاستعلام، لتجنب تكرار الكود

مثال:

java
@Repository
public interface UserRepository extends JpaRepository<User, Long> {

    // Spring Data JPA تنفذ تلقائياً
    Optional<User> findByUsername(String username);
    boolean existsByUsername(String username);

    // استعلام معقد مخصص
    @Query("SELECT u FROM User u WHERE u.email = :email AND u.deleted = false")
    Optional<User> findActiveByEmail(@Param("email") String email);
}

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

  • Repository هو واجهة، لا تحتوي على منطق الأعمال
  • استخدام أسماء الميثود للتعبير عن نية الاستعلام
  • يمكن استخدام @Query للاستعلامات المعقدة المخصصة
Repository Layer: Data Access Boundary
Repository encapsulates data access so upper layers do not need database details.
Use Repository to encapsulate data accessClearly decoupled
// Repository interface definition
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
    // ✅ Query generated from method name
    List<Order> findByUserIdAndDeletedFalse(Long userId);

    // ✅ Custom JPQL
    @Query("SELECT o FROM Order o WHERE o.createdAt BETWEEN :start AND :end")
    List<Order> findByDateRange(@Param("start") LocalDateTime start,
                                @Param("end") LocalDateTime end);
}

// Service layer with pure business logic
@Service
public class OrderService {
    @Autowired private OrderRepository orderRepository; // ✅ Depend on interface

    public List<OrderDTO> getUserOrders(Long userId) {
        List<Order> orders = orderRepository.findByUserIdAndDeletedFalse(userId);
        return orders.stream().map(OrderDTO::from).collect(Collectors.toList());
    }
}
Benefits of this approach
  • Separation of concerns: Service handles business, Repository handles data
  • High testability: mocks can replace the real database
  • Code reuse: common queries are defined once and reused
  • Low switching cost: changing database mostly affects Repository implementation
Repository Implementation Options
ImplementationProsConsBest for
Spring Data JPA
Mainstream
Method-name query derivation, built-in paginationComplex queries may be less efficientFast development, standard CRUD
MyBatis / MyBatis-Plus
SQL control
Full SQL control and strong dynamic SQLRequires handwritten SQLComplex queries, performance-sensitive paths
Spring Data JDBC
Lightweight
Simple, lightweight, fast startupNo complex mappingMicroservices, simple aggregate roots

2.5 طبقة Domain

المسؤولية: "معايير الوصفة" للأعمال

  • تعريف كيانات الأعمال (Entity)
  • تعريف كائنات القيمة (Value Object)
  • تغليف قواعد الأعمال
  • العمل كاعتمادية مشتركة لجميع الطبقات

خصائص مهمة:

  • طبقة Domain لا تعتمد على أي طبقة أخرى
  • جميع الطبقات تعتمد على طبقة Domain
  • هي أساس هندسة الطبقات

فلسفة التصميم: طبقة Domain هي نواة الأعمال للنظام بأكمله، وتعبر عن المعرفة المجالية وقواعد الأعمال. نقاؤها أمر بالغ الأهمية:

  • عدم الاعتماد على الإطار يعني أن منطق الأعمال ليس مقيداً بالمكدس التقني
  • اعتماد جميع الطبقات عليها يضمن توحيد قواعد الأعمال
  • سهولة التطور على المدى الطويل، حيث يمكن استبدال المكدس التقني بينما تبقى قواعد الأعمال مستقرة نسبياً

مثال:

java
@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, nullable = false)
    private String username;

    @Column(nullable = false)
    private String password;

    // ✅ ميثود الأعمال: تغليف قواعد الأعمال
    public boolean isPasswordCorrect(String rawPassword) {
        return BCrypt.checkpw(rawPassword, this.password);
    }

    public void changePassword(String oldPassword, String newPassword) {
        if (!isPasswordCorrect(oldPassword)) {
            throw new IncorrectPasswordException();
        }
        this.password = BCrypt.hashpw(newPassword);
    }
}

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

  • Entity لها معرف فريد
  • قواعد الأعمال مغلفة في كائنات Domain
  • طبقة Domain هي منطق أعمال خالص، لا تعتمد على الإطار
Domain Layer: Domain Model Design
Domain carries business concepts and forms the dependency base for all layers.
Anemic ModelTraditional approach
@Entity
public class Order {
    @Id private Long id;
    private BigDecimal totalAmount;
    private OrderStatus status;
    // Only getters/setters, no business logic
    public Long getId() { return id; }
    public void setStatus(OrderStatus s) { this.status = s; }
}
@Service
public class OrderService {
    public void cancelOrder(Long orderId) {
        Order order = orderRepository.findById(orderId).orElseThrow();
        // Anemic model: business logic is scattered in Service
        if (order.getStatus() == OrderStatus.SHIPPED)
            throw new IllegalStateException("Shipped order cannot be cancelled");
        order.setStatus(OrderStatus.CANCELLED);
        orderRepository.save(order);
    }
}
Problems with anemic model
  • Violates object orientation: objects have data but no behavior
  • Scattered logic: same rule may repeat in multiple Services
  • Hard to maintain: changing a rule requires finding all usages
Rich Domain ModelRecommended approach
@Entity
public class Order {
    @Id private Long id;
    private BigDecimal totalAmount;
    private OrderStatus status;

    // Business behavior is encapsulated in the entity
    public void cancel() {
        if (this.status == OrderStatus.SHIPPED)
            throw new IllegalStateException("Shipped order cannot be cancelled");
        this.status = OrderStatus.CANCELLED;
        registerEvent(new OrderCancelledEvent(this.id));
    }

    public void pay(Payment payment) {
        if (this.status != OrderStatus.PENDING_PAYMENT)
            throw new IllegalStateException("Invalid order state");
        this.status = OrderStatus.PAID;
    }
}
@Service
public class OrderService {
    @Transactional
    public void cancelOrder(Long orderId) {
        Order order = orderRepository.findById(orderId).orElseThrow();
        order.cancel(); // Call domain behavior
        orderRepository.save(order);
    }
}
Benefits of rich domain model
  • Object-oriented: data and behavior are encapsulated together
  • Business cohesion: rules stay with objects and change in one place
  • Testable: domain objects are in-memory and do not require database
  • Expressive: order.cancel() is more natural than orderService.cancel(order)

3. DTO: "المترجم" بين الطبقات

3.1 لماذا نحتاج إلى DTO؟

المشكلة: إذا قمنا بإرجاع كيان قاعدة البيانات مباشرة إلى الواجهة الأمامية:

java
// ❌ خطأ: إرجاع Entity مباشرة
@Entity
public class User {
    private Long id;
    private String username;
    private String password;        // معلومات حساسة!
    private Boolean isDeleted;      // حقل داخلي!
}

الواجهة الأمامية ستتلقى حقولاً لا يجب كشفها، مما يشكل خطراً أمنياً.

الحل: استخدام DTO "للترجمة"

كيان قاعدة البيانات → Service Param/Result → Controller Request/Response → الواجهة الأمامية

3.2 أنواع DTO

النوعالغرضمثال
Request DTOاستقبال المعاملات في ControllerUserCreateRequest
Response DTOإرجاع البيانات من ControllerUserResponse
Param DTOمعاملات ميثود ServiceUserParam
Result DTOنتيجة الإرجاع من ServiceUserResult
Entityتعيين قاعدة البياناتUser

المبادئ الأساسية: كل طبقة تستخدم DTO الخاص بها، ولا تمرر Entity مباشرة، ويحتوي DTO فقط على الحقول الضرورية، مما يتجنب كشف تفاصيل التنفيذ الداخلية ويضمن استقلالية كل طبقة.

DTO Flow: Data Conversion Between Layers
DTO means Data Transfer Object, the carrier for data passed between layers.
Controller Layer
// Receive Request DTO
public ResponseEntity<UserDTO> createUser(
    @RequestBody @Valid UserCreateRequest request) { ... }
↓ Convert to parameters required by Service
Service Layer
public UserDTO createUser(UserCreateParam param) {
    User user = param.toEntity();   // Convert to Entity
    userRepository.save(user);
    return UserDTO.from(user);      // Entity → DTO
}
↓ Convert to Entity required by Repository
Repository Layer
public interface UserRepository
    extends JpaRepository<User, Long> { }
↑ Return Entity and convert to DTO
Return to client
{ "id": 10001, "username": "Alice",
  "email": "alice@example.com", "createdAt": "2024-01-15T10:30:00Z" }
DTO Responsibility by Layer
LayerDTO typeResponsibilityExample
ControllerRequest / Response DTODefine API contract and validationUserCreateRequest
ServiceParam / Result DTOWrap business method parameters and decouple layersUserCreateParam
RepositoryEntity / DOMap database table structureUserEntity

4. اتجاه الاعتماديات: القانون الحديدي لهندسة الطبقات

4.1 مبدأ عكس الاعتماديات

الطريقة الخاطئة:

Controller → UserServiceImpl → UserDaoImpl → UserEntity

الطريقة الصحيحة:

Controller → UserService(واجهة) → UserRepository(واجهة) → UserEntity

اتجاه الاعتماديات:

اتجاه الاعتماديات الصحيح هو أن جميع الطبقات تعتمد على الطبقات الأكثر تجريداً واستقراراً. بشكل ملموس، Controller يعتمد على واجهة Service، و Service يعتمد على واجهة Repository، وجميع الطبقات تعتمد على طبقة Domain، بينما طبقة Domain لا تعتمد على أي طبقة أخرى. يضمن اتجاه الاعتماديات هذا استقلالية منطق الأعمال وقابليته للاختبار.

تشمل الممارسات الخاطئة اعتماد Service مباشرة على فئة تنفيذ Repository، أو تعامل Controller مباشرة مع قاعدة البيانات، أو اعتماد طبقة Domain على طبقات أخرى، وكلها تؤدي إلى زيادة الاقتران وتقليل قابلية الصيانة.

4.2 مثال على الكود

java
// ✅ صحيح: الاعتماد على الواجهة
@Service
public class OrderService {
    private final OrderRepository orderRepository;  // واجهة
    private final PaymentService paymentService;    // واجهة
}

// ✅ فئة التنفيذ تُحقن تلقائياً عبر Spring
@Repository
public class OrderRepositoryImpl implements OrderRepository {
    // تفاصيل التنفيذ
}
Dependency Direction: Core Rule of Layered Architecture
Understanding dependency direction is essential to layered architecture.
Outer layer (UI / external systems)
Controller
↓ depends on
Middle layer (application layer)
Service
↓ depends on
Inner layer (domain layer)
Domain / Repository
Core principle: Dependency Inversion Principle

High-level modules should not depend on low-level implementation details. They should depend on abstractions.

Controller → Service interface
Controller depends on the Service interface, not implementation class
Service → Repository interface
Service depends on Repository interface and does not care how data is stored
All layers depend on Domain
Domain is the core and is depended on by upper layers, but Domain depends on no layer

5. حالة عملية: نظام طلبات التجارة الإلكترونية

5.1 المتطلبات

إنشاء طلب:

  1. يختار المستخدم المنتج
  2. التحقق من المخزون
  3. حساب المبلغ
  4. إنشاء الطلب
  5. خصم المخزون

5.2 تنفيذ الكود

طبقة Domain:

java
@Entity
public class Order {
    @Id
    private Long id;
    private Long userId;
    private List<OrderItem> items;
    private Money totalAmount;
    private OrderStatus status;

    public void calculateTotal() {
        Money total = Money.zero();
        for (OrderItem item : items) {
            total = total.add(item.getSubTotal());
        }
        this.totalAmount = total;
    }

    public void cancel() {
        if (this.status != OrderStatus.PENDING_PAYMENT) {
            throw new IllegalStateException("يمكن فقط إلغاء الطلبات المعلقة للدفع");
        }
        this.status = OrderStatus.CANCELLED;
    }
}

طبقة Repository:

java
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
    List<Order> findByUserIdOrderByCreatedAtDesc(Long userId);
}

طبقة Service:

java
@Service
@RequiredArgsConstructor
public class OrderService {

    private final OrderRepository orderRepository;
    private final InventoryService inventoryService;

    @Transactional
    public OrderDTO createOrder(OrderParam param) {
        // 1. التحقق من المنتج وخصم المخزون
        for (OrderItemParam item : param.getItems()) {
            inventoryService.reserveStock(item.getProductId(), item.getQuantity());
        }

        // 2. إنشاء الطلب
        Order order = new Order();
        order.setUserId(param.getUserId());
        order.calculateTotal();

        // 3. حفظ الطلب
        orderRepository.save(order);

        return OrderDTO.from(order);
    }
}

طبقة Controller:

java
@RestController
@RequestMapping("/api/orders")
public class OrderController {

    private final OrderService orderService;

    @PostMapping
    public OrderResponse createOrder(@RequestBody @Valid OrderRequest request) {
        OrderParam param = OrderParam.builder()
                .userId(request.getUserId())
                .items(request.getItems())
                .build();

        OrderDTO order = orderService.createOrder(param);

        return OrderResponse.from(order);
    }
}

6. الأسئلة الشائعة

6.1 هل يمكن لـ Controller كتابة منطق الأعمال؟

يجب ألا يكتب Controller منطق الأعمال، فهو مسؤول فقط عن استقبال الطلبات وإرجاع الاستجابات. يجب تغليف منطق الأعمال في طبقة Service، والفائدة من ذلك هي إمكانية إعادة استخدام الكود، على سبيل المثال يمكن للمهام المجدولة أو مستهلكي طوابير الرسائل استدعاء Service مباشرة دون المرور عبر طلب HTTP. في الوقت نفسه، تركيز منطق الأعمال في مكان واحد يجعله أسهل في الاختبار والصيانة، ويتجنب مشاكل عدم الاتساق الناتجة عن تشتت المنطق.

6.2 ما هو النموذج الفقير والنموذج الغني؟

النموذج الفقير (Anemic Model) يعني أن فئة الكيان تحتوي فقط على الخصائص وطرق getter/setter المقابلة، دون أي منطق أعمال، وتوضع جميع قواعد الأعمال في طبقة Service. هذا النموذج بسيط الهيكل وسهل الفهم، وهو النهج المعتمد في معظم المشاريع.

النموذج الغني (Rich Model) يعني أن فئة الكيان لا تحتوي فقط على الخصائص، بل أيضاً على ميثودات الأعمال المتعلقة بالكيان، مما يغلف قواعد الأعمال داخل الكيان نفسه. هذا النهج أكثر توافقاً مع فكر التصميم الموجه للكائنات، حيث يجمع البيانات والسلوك معاً، مما يزيد من تماسك الكود.

يُنصح باختيار النموذج المناسب بناءً على الخلفية التقنية للفريق وتعقيد المشروع، ولكن أياً كان الاختيار، يجب الحفاظ على الاتساق، ويجب أن تحتوي طبقة Domain على الأقل على ميثودات سلوك الأعمال الأساسية، بدلاً من أن تكون مجرد قشرة فارغة تماماً.

6.3 كيفية التعامل مع المعاملات عبر عدة Service؟

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


7. ملخص

الطبقةالمسؤوليةالكلمة المفتاحية
Controllerاستقبال الطلب، التحقق من المعاملات، استدعاء Service، إرجاع الاستجابةموظف الاستقبال
Serviceتنسيق منطق الأعمال، إدارة المعاملات، تنسيق Repositoryالطاهي
Repositoryالوصول إلى البيانات، تعيين ORM، تغليف الاستعلاماتأمين المستودع
Domainتعريف الكيانات، قواعد الأعمال، كائنات القيمةمعايير الوصفة

المبادئ الأساسية:

  1. كل طبقة تقوم بعملها فقط
  2. التواصل بين الطبقات عبر واجهات
  3. منطق الأعمال يتركز في Service و Domain
  4. منطق الوصول إلى البيانات يتركز في Repository
  5. استخدام DTO لعزل هياكل البيانات بين الطبقات

8. المزيد من أنماط الهندسة

تقدم هذه المقالة الهندسة متعددة الطبقات (Layered Architecture)، وهي النمط الأكثر شيوعاً وسهولة في البداية لهندسة الواجهة الخلفية. لكن هندسة الواجهة الخلفية لا تقتصر على هذا النمط، فهناك أنماط أخرى تستحق المعرفة حسب سيناريوهات الأعمال المختلفة:

8.1 أنماط الهندسة الشائعة الأخرى

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

فيما يلي شرح مفصل لكل منها:

الهندسة الأحادية (Monolithic)

جميع الوظائف مجمعة في تطبيق واحد، تشترك في نفس قاعدة البيانات والعملية.

┌──────────────────────────────┐
│         تطبيق أحادي           │
│  ┌────┐ ┌────┐ ┌────┐       │
│  │مستخدم│ │طلب │ │دفع │ ...  │
│  └──┬─┘ └──┬─┘ └──┬─┘       │
│     └──────┼──────┘          │
│        قاعدة بيانات مشتركة     │
└──────────────────────────────┘
  • المزايا: تطوير بسيط، نشر سهل، تصحيح محلي مريح
  • العيوب: اقتران عالٍ للكود، صعوبة في التوسع، مشكلة في وحدة واحدة قد تؤثر على النظام بأكمله
  • مناسب لـ: مشاريع الشركات الناشئة المبكرة، تطوير فريق واحد، التحقق السريع من النموذج الأولي

هندسة الخدمات المصغرة (Microservices)

تقسيم النظام إلى خدمات مستقلة متعددة، لكل خدمة بياناتها ومنطق أعمالها الخاص، ويمكن نشرها وتوسيعها بشكل مستقل.

┌────────┐  ┌────────┐  ┌────────┐
│خدمة     │  │خدمة     │  │خدمة     │
│المستخدم│  │الطلبات  │  │الدفع    │
│  DB-1  │  │  DB-2  │  │  DB-3  │
└───┬────┘  └───┬────┘  └───┬────┘
    └───────────┼───────────┘
          API Gateway
  • المزايا: نشر وتوسيع مستقل، مرونة في المكدس التقني، عزل الأعطال
  • العيوب: تعقيد التواصل بين الخدمات، صعوبة اتساق البيانات الموزعة، الحاجة إلى قدرات DevOps ناضجة
  • مناسب لـ: الأنظمة الكبيرة المعقدة، التعاون متعدد الفرق، السيناريوهات التي تحتاج توسعاً مستقلاً

الهندسة المدفوعة بالأحداث (Event-Driven)

التواصل عبر الأحداث غير المتزامنة، المنتج يصدر الأحداث، والمستهلك يستجيب لها، والمكونات عالية الفصل.

منتج ──→ [ناقل الأحداث/طابور الرسائل] ──→ مستهلك أ
                                        ──→ مستهلك ب
                                        ──→ مستهلك ج
  • المزايا: فصل عالٍ، دعم طبيعي للتوسع، مناسب للمعالجة الفورية
  • العيوب: صعوبة في التصحيح، ترتيب الأحداث والعامل المثالي يحتاجان معالجة إضافية
  • مناسب لـ: تحليل البيانات الفوري، أنظمة IoT، التواصل غير المتزامن بين الخدمات المصغرة

الهندسة النظيفة (Clean Architecture)

قدمها Robert C. Martin، تقسم النظام إلى أربع طبقات دائرية متحدة المركز، الاعتماديات تتجه فقط من الخارج إلى الداخل:

┌─────────────────────────────────────┐
│  Frameworks & Drivers (الأطر والمشغلات) │
│  ┌─────────────────────────────┐    │
│  │  Interface Adapters (المحولات) │    │
│  │  ┌─────────────────────┐    │    │
│  │  │  Use Cases (حالات الاستخدام)│    │    │
│  │  │  ┌─────────────┐    │    │    │
│  │  │  │  Entities    │    │    │    │
│  │  │  │  (الكيانات/المجال)│    │    │    │
│  │  │  └─────────────┘    │    │    │
│  │  └─────────────────────┘    │    │
│  └─────────────────────────────┘    │
└─────────────────────────────────────┘
         اتجاه الاعتماديات: الخارج → الداخل
  • القاعدة الأساسية: الطبقات الداخلية لا تعرف وجود الطبقات الخارجية، منطق الأعمال مستقل تماماً عن الإطار وقاعدة البيانات
  • المزايا: قابلية اختبار عالية، المكدس التقني قابل للاستبدال، منطق الأعمال واضح
  • العيوب: تكلفة تطوير أولية عالية، كود تعيين كثير بين الطبقات، قد تكون مبالغة في التصميم للمشاريع الصغيرة
  • مناسب لـ: أنظمة الأعمال المعقدة، المشاريع التي تحتاج صيانة طويلة الأمد
Clean Architecture vs Layered Architecture
Layered architecture is a foundation for clean architecture. Understanding the relationship helps build more flexible systems.
Controller Layer Receive requests, validate parameters
Service Layer Business logic, transaction management
Repository Layer Data access, ORM mapping
Domain Layer Entities, business rules
Traditional Layered Architecture Traits
  • Vertical dependency: upper layers directly depend on lower layers
  • Simple and intuitive: clear structure, easy to understand
  • Good for small and medium projects: quick development
  • Potential issue: lower-layer changes may affect upper layers

الهندسة السداسية (Hexagonal / Ports & Adapters)

تعريف واجهات الإدخال والإخراج للأعمال الأساسية عبر "المنافذ"، والاتصال بالأنظمة الخارجية عبر "المحولات":

        ┌─────────────┐
  HTTP ──→ Port      │
  CLI  ──→ (منفذ إدخال)│  منطق الأعمال الأساسي │  (منفذ إخراج) ──→ قاعدة البيانات
  MQ   ──→           │                      │  Port    ──→ API خارجي
        └─────────────┘
  • الفكرة الأساسية: منطق الأعمال لا يعتمد على أي تقنية خارجية، الأنظمة الخارجية تتصل عبر المحولات
  • المزايا: الأنظمة الخارجية قابلة للاستبدال بحرية، يمكن استخدام محولات Mock للاختبار
  • مناسب لـ: السيناريوهات التي تحتاج الاتصال بأنظمة خارجية متعددة

هندسة البصل (Onion Architecture)

مشابهة للهندسة النظيفة، تؤكد على أن نموذج المجال في الطبقة الأعمق، والبنية التحتية في الطبقة الخارجية، والاعتماديات تتجه للداخل فقط:

┌──────────────────────────────┐
│  Infrastructure (البنية التحتية) │
│  ┌────────────────────────┐  │
│  │  Application Services  │  │
│  │  ┌──────────────────┐  │  │
│  │  │  Domain Services  │  │  │
│  │  │  ┌────────────┐   │  │  │
│  │  │  │Domain Model│   │  │  │
│  │  │  └────────────┘   │  │  │
│  │  └──────────────────┘  │  │
│  └────────────────────────┘  │
└──────────────────────────────┘
  • الفكرة الأساسية: نموذج المجال هو نواة النظام، جميع الاعتماديات تشير إليه
  • الفرق عن الهندسة النظيفة: هندسة البصل تؤكد أكثر على طبقة خدمات المجال، بينما الهندسة النظيفة تؤكد أكثر على طبقة حالات الاستخدام
  • مناسب لـ: المشاريع التي تتبنى التصميم الموجه بالمجال (DDD)

8.2 مسار تطور الهندسة

هذه الهندسات ليست بدائل عن بعضها البعض، بل هي تطور تدريجي:

text
الهندسة التقليدية متعددة الطبقات (N-Layered)
  │  المشكلة: اقتران بين الطبقات، صعوبة استبدال الاعتماديات الخارجية

الهندسة السداسية (Ports & Adapters)
  │  التحسين: عزل الأنظمة الخارجية بالمنافذ والمحولات

هندسة البصل (Onion)
  │  التحسين: طبقات دائرية واضحة، نموذج المجال في المركز

الهندسة النظيفة (Clean Architecture)
  │  التحسين: توحيد قواعد الاعتماديات، توضيح مسؤوليات الطبقات الأربع

اختيار الهندسة المناسبة حسب احتياجات الأعمال

8.3 دليل اختيار نمط الهندسة

text
عدد المستخدمين < 1k، حجم الكود < 5000 سطر

الهندسة الأحادية + طبقات بسيطة

عدد المستخدمين 1k-100k، الحاجة إلى تعاون متعدد الفرق

الهندسة متعددة الطبقات (المقدمة في هذه المقالة)

عدد المستخدمين > 100k، تعقيد أعمال مرتفع

هندسة الخدمات المصغرة / الهندسة المدفوعة بالأحداث

أبعاد اختيار أكثر تفصيلاً:

عامل الاعتبارطبقات بسيطةهندسة نظيفة/سداسيةخدمات مصغرة
حجم الفريق1-5 أشخاص5-20 شخص20+ شخص
تعقيد الأعمالمنخفضمتوسط-مرتفعمرتفع
تكرار النشرمنخفضمتوسطمرتفع (نشر مستقل)
تنوع المكدس التقنيواحدواحديمكن أن يكون متنوعاً
تكلفة التشغيلمنخفضةمتوسطةمرتفعة

8.4 قراءات موصى بها

  • الهندسة الأحادية: راجع المقالة الشقيقة backend-project-architecture.md لفهم التطور من السكربت إلى الأحادي
  • هندسة الخدمات المصغرة: راجع التطور من الأحادي إلى الخدمات المصغرة
  • الهندسة النظيفة: كتاب "Clean Architecture" لـ Robert C. Martin — العمل الكلاسيكي الذي يقدم قواعد الاعتماديات ونموذج الطبقات الأربع متحدة المركز
  • أنماط هندسة المؤسسات: كتاب "Patterns of Enterprise Application Architecture" لـ Martin Fowler — المرجع الموثوق لهندسة الطبقات وتنظيم منطق المجال

8.5 كيف تختار؟

تذكر هذا المبدأ: الهندسة تخدم الأعمال، وليس الهندسة من أجل الهندسة.

  • المشاريع الصغيرة تستخدم هندسة بسيطة، للإطلاق السريع والتحقق
  • المشاريع الكبيرة تفكر في الهندسة المعقدة لاحقاً، لتجنب المبالغة في التصميم
  • ألفة الفريق مهمة جداً أيضاً، اختر حلاً يمكن للجميع فهمه

9. ملخص

الطبقةالمسؤوليةالكلمة المفتاحية
Controllerاستقبال الطلب، التحقق من المعاملات، استدعاء Service، إرجاع الاستجابةموظف الاستقبال
Serviceتنسيق منطق الأعمال، إدارة المعاملات، تنسيق Repositoryالطاهي
Repositoryالوصول إلى البيانات، تعيين ORM، تغليف الاستعلاماتأمين المستودع
Domainتعريف الكيانات، قواعد الأعمال، كائنات القيمةمعايير الوصفة

المبادئ الأساسية:

جوهر هندسة الطبقات يكمن في التقسيم الواضح للمسؤوليات والتحكم في اتجاه الاعتماديات. كل طبقة تركز فقط على مسؤولياتها، وتتواصل مع الطبقات المجاورة عبر الواجهات، ويتركز منطق الأعمال في طبقتي Service و Domain، ويتركز منطق الوصول إلى البيانات في طبقة Repository، وتعزل الطبقات هياكل البيانات فيما بينها عبر DTO، لتجنب الكشف المباشر عن تفاصيل التنفيذ الداخلي. هذا التصميم يجعل النظام أسهل في الفهم والاختبار والصيانة، وقادراً على مواجهة التطور المستمر للأعمال.


المراجع

  1. Catalog of Patterns of Enterprise Application Architecture - Martin Fowler — دليل Martin Fowler لأنماط هندسة تطبيقات المؤسسات، مرجع كلاسيكي لهندسة الطبقات
  2. Backend Side Architecture Evolution (N-layered, DDD, Hexagon, Onion, Clean Architecture) — رحلة التطور من هندسة N من الطبقات إلى الهندسة النظيفة، لفهم أسباب نشأة كل هندسة
  3. Complete Guide to Clean Architecture - GeeksforGeeks — دليل شامل للهندسة النظيفة، شرح مفصل للطبقات وقواعد الاعتماديات وفصل الاهتمامات
  4. Understanding Hexagonal, Clean, Onion, and Traditional Layered Architectures: A Deep Dive — مقارنة معمقة بين الهندسة السداسية والنظيفة والبصل والطبقات التقليدية
  5. Building Clean Architectures in Modern Backend Frameworks — دليل عملي لتطبيق الهندسة النظيفة في أطر الواجهة الخلفية الحديثة
  6. Backend Architecture Patterns: From Monoliths to Microservices — نظرة شاملة على أنماط هندسة الواجهة الخلفية من الأحادي إلى الخدمات المصغرة
  7. MVC شرح مفصل لحالة الهندسة ثلاثية الطبقات — العلاقة بين MVC والهندسة ثلاثية الطبقات مع حالة عملية، مناسبة للمبتدئين الناطقين بالصينية