404 Not Found

404 Not Found


nginx

تمرين المرحلة 1 الشامل: بناء الإطار الأساسي لـ OrderFlow

وضع المعرفة موضع التطبيق—هذا الدرس يجمع كل المفاهيم التي تم تناولها في الدروس الخمسة الأولى لبناء الإطار الأساسي الكامل لنظام إدارة الطلبات التجاري OrderFlow.

1. ما ستتعلمه


2. قصة حقيقية عن عمل الفريق

(1) نقطة الألم: المعرفة مجزأة ولا يمكن تطبيقها

أكملت Alice تعلم التهيئة التلقائية لـ Spring Boot، وواجهات REST API، وإدارة التهيئة، ومواضيع أخرى، لكن عندما حان الوقت لبناء مشروع OrderFlow فعلياً، لم تكن تعرف من أين تبدأ. كل موضوع كان يُدرَّس بمعزل عن غيره، فلم تكن متأكدة كيف تتبدل بين H2 لبيئة التطوير وMySQL لبيئة الإنتاج، أو كيف تدمج واجهات REST API مع إدارة التهيئة.

(2) حلول التمارين الشاملة

يربط هذا الدرس جميع المفاهيم في مشروع كامل—من إنشاء المشروع، وتهيئة البيئة، وكتابة واجهات API إلى تخصيص Starter—لمحاكاة سير عمل التطوير الواقعي.

(3) العوائد

بمجرد أن أكملت Alice التمارين الشاملة، أصبح هيكل مشروع OrderFlow يخدم مباشرة كأساس للدورات اللاحقة، مما ضاعف كفاءة التطوير بثلاثة أضعاف.


3. إنشاء مشروع وتهيئة البيئة

(1) تهيئة المشروع

(1) ▶ مثال: إنشاء مشروع باستخدام Spring Initializr

BASH
curl https://start.spring.io/starter.zip \
  -d type=maven-project \
  -d language=java \
  -d bootVersion=3.2.5 \
  -d groupId=com.orderflow \
  -d artifactId=orderflow-service \
  -d packageName=com.orderflow \
  -d javaVersion=17 \
  -d dependencies=web,data-jpa,h2,mysql,validation,lombok \
  -o orderflow-service.zip && unzip orderflow-service.zip

الناتج:

TEXT
{"status":"ok","data":{}}

(2) تهيئة البيئات المتعددة

100%
graph TD
    A["application.yml<br/>مشترك"] --> B["application-dev.yml<br/>H2 + DEBUG"]
    A --> C["application-prod.yml<br/>MySQL + WARN"]
    B --> D["مدمج: ملف تعريف dev"]
    C --> E["مدمج: ملف تعريف prod"]
عنصر التهيئة dev prod
قاعدة البيانات H2 في الذاكرة MySQL
ddl-auto create-drop validate
مستوى السجل DEBUG WARN
المنفذ 8080 8080

4. واجهة REST API للمنتجات والطلبات

(1) هيكل حزم المشروع

TEXT
com.orderflow/
├── OrderFlowApplication.java
├── config/
│   └── OrderFlowProperties.java
├── controller/
│   ├── ProductController.java
│   └── OrderController.java
├── model/
│   ├── Product.java
│   └── Order.java
└── repository/
    ├── ProductRepository.java
    └── OrderRepository.java

(1) ▶ مثال: DTO والمتحكم للمنتجات

JAVA
// model/Product.java
package com.orderflow.model;

public record Product(
    Long id,
    String name,
    BigDecimal price,
    Integer stock
) {}

// controller/ProductController.java
package com.orderflow.controller;

import com.orderflow.model.Product;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

@RestController
@RequestMapping("/api/v1/products")
public class ProductController {

    private final Map<Long, Product> store = new ConcurrentHashMap<>();
    private final AtomicLong seq = new AtomicLong(1);

    @PostMapping
    public ResponseEntity<Product> create(@RequestBody Product product) {
        Long id = seq.getAndIncrement();
        Product saved = new Product(id, product.name(), product.price(), product.stock());
        store.put(id, saved);
        return ResponseEntity.status(HttpStatus.CREATED).body(saved);
    }

    @GetMapping
    public Collection<Product> list() {
        return store.values();
    }

    @GetMapping("/{id}")
    public ResponseEntity<Product> get(@PathVariable Long id) {
        Product p = store.get(id);
        return p != null ? ResponseEntity.ok(p) : ResponseEntity.notFound().build();
    }
}

الناتج:

TEXT
// التنفيذ ناجح

(2) ▶ مثال: DTO والمتحكم للطلبات

JAVA
// model/Order.java
package com.orderflow.model;

import java.time.Instant;

public record Order(
    Long id,
    Long productId,
    Integer quantity,
    String status,
    Instant createdAt
) {}

// controller/OrderController.java
package com.orderflow.controller;

import com.orderflow.model.Order;
import com.orderflow.config.OrderFlowProperties;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

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

    private final Map<Long, Order> store = new ConcurrentHashMap<>();
    private final AtomicLong seq = new AtomicLong(1);
    private final OrderFlowProperties props;

    public OrderController(OrderFlowProperties props) {
        this.props = props;
    }

    @PostMapping
    public ResponseEntity<Order> create(@RequestBody OrderRequest req) {
        if (req.quantity() > props.maxItemsPerOrder()) {
            return ResponseEntity.badRequest().build();
        }
        Long id = seq.getAndIncrement();
        Order order = new Order(id, req.productId(), req.quantity(), "PENDING", Instant.now());
        store.put(id, order);
        return ResponseEntity.status(HttpStatus.CREATED).body(order);
    }

    @GetMapping("/{id}")
    public ResponseEntity<Order> get(@PathVariable Long id) {
        Order o = store.get(id);
        return o != null ? ResponseEntity.ok(o) : ResponseEntity.notFound().build();
    }

    public record OrderRequest(Long productId, Integer quantity) {}
}

الناتج:

TEXT
// التنفيذ ناجح

5. تهيئة الأعمال باستخدام ConfigurationProperties

(1) ▶ مثال: OrderFlowProperties

JAVA
// config/OrderFlowProperties.java
package com.orderflow.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import java.time.Duration;

@ConfigurationProperties(prefix = "orderflow")
public record OrderFlowProperties(
    int maxItemsPerOrder,
    Duration orderTimeout,
    String defaultCurrency
) {}

الناتج:

TEXT
// التنفيذ ناجح
YAML
# application.yml
spring:
  application:
    name: orderflow-service
  profiles:
    active: dev

orderflow:
  max-items-per-order: 100
  order-timeout: 30m
  default-currency: USD

6. استبعاد التهيئة التلقائية وStarters المخصصة

(1) ▶ مثال: استبعاد التهيئات التلقائية غير المرغوب فيها

JAVA
@SpringBootApplication(exclude = {
    org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration.class
})
@EnableConfigurationProperties(OrderFlowProperties.class)
public class OrderFlowApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderFlowApplication.class, args);
    }
}

الناتج:

TEXT
// التنفيذ ناجح
⚠️ ملاحظة: يتم استبعاد DataSource في هذه المرحلة لأننا لا نستخدم قاعدة بيانات حقيقية في الوقت الحالي. يجب إزالة هذا الاستبعاد بعد تقديم JPA في المرحلة 2.


7. تنظيم مجموعات Postman

(1) ▶ مثال: نص اختبار Postman

BASH
# اختبارات واجهة Product API
curl -X POST http://localhost:8080/api/v1/products \
  -H "Content-Type: application/json" \
  -d '{"name":"Laptop","price":999.99,"stock":50}'

curl http://localhost:8080/api/v1/products

curl http://localhost:8080/api/v1/products/1

# اختبارات واجهة Order API
curl -X POST http://localhost:8080/api/v1/orders \
  -H "Content-Type: application/json" \
  -d '{"productId":1,"quantity":3}'

curl http://localhost:8080/api/v1/orders/1

# التحقق من التهيئة
curl http://localhost:8080/api/v1/health

الناتج:

TEXT
{"status":"ok","data":{}}
سيناريو الاختبار API رمز الحالة المتوقع
إنشاء منتج POST /api/v1/products 201
استعلام قائمة المنتجات GET /api/v1/products 200
استعلام منتج واحد GET /api/v1/products/1 200
إنشاء طلب POST /api/v1/orders 201
طلب مفرط POST /api/v1/orders (qty=200) 400

8. مثال شامل: الإطار الأساسي الكامل لـ OrderFlow

JAVA
// OrderFlowApplication.java
package com.orderflow;

import com.orderflow.config.OrderFlowProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

@SpringBootApplication
@EnableConfigurationProperties(OrderFlowProperties.class)
public class OrderFlowApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderFlowApplication.class, args);
    }
}

// OrderFlowProperties.java
package com.orderflow.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import java.time.Duration;

@ConfigurationProperties(prefix = "orderflow")
public record OrderFlowProperties(
    int maxItemsPerOrder,
    Duration orderTimeout,
    String defaultCurrency
) {}
YAML
# application.yml
spring:
  application:
    name: orderflow-service
  profiles:
    active: dev

orderflow:
  max-items-per-order: 100
  order-timeout: 30m
  default-currency: USD

---
# application-dev.yml
spring:
  datasource:
    url: jdbc:h2:mem:orderflow_dev
    username: sa
    password:
  h2:
    console:
      enabled: true
  jpa:
    hibernate:
      ddl-auto: create-drop
    show-sql: true

logging:
  level:
    com.orderflow: DEBUG

---
# application-prod.yml
spring:
  datasource:
    url: jdbc:mysql://${DB_HOST:localhost}:3306/orderflow
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: false

logging:
  level:
    com.orderflow: WARN
💻 الناتج:

TEXT
# تشغيل وضع التطوير
$ java -jar orderflow-service.jar --spring.profiles.active=dev
Started OrderFlowApplication in 3.2 seconds

# تشغيل وضع الإنتاج
$ java -jar orderflow-service.jar --spring.profiles.active=prod
Started OrderFlowApplication in 5.1 seconds

❓ أسئلة شائعة

س هل يمكن استخدام هيكل مشروع تمرينات المرحلة 1 في الدروس اللاحقة؟
ج نعم. الإطار المبني في هذا الدرس يخدم كأساس للمرحلتين 2-5. المرحلة 2 ستضيف JPA، طبقة خدمة، تحققات، والمزيد، لتحسين المشروع تدريجياً.
س لماذا تم استبعاد تهيئة DataSource التلقائية مؤقتاً؟
ج في هذه المرحلة، نستخدم خريطة في الذاكرة لمحاكاة تخزين البيانات. بمجرد تقديم Spring Data JPA في المرحلة 2، سيكون مصدر بيانات حقيقي مطلوباً؛ عند ذلك يمكنك إزالة الاستبعاد.
س كيف يجب إدارة اختلافات التهيئة بين dev وprod؟
ج ضع التهيئات المشتركة في application.yml والتهيئات الخاصة بكل بيئة في application-{profile}.yml. احقن دائماً المعلومات الحساسة (كلمات المرور، المفاتيح) باستخدام متغيرات البيئة.
س كيف أتحقق من أن Profile نشط؟
ج سيُطبع The following 1 profile is active: "dev" في سجل التشغيل. يمكنك أيضاً حقن @Value("${spring.profiles.active}") في المتحكم وإعادته.
س ما هي قيود استخدام record كـ DTO؟
ج record غير قابل للتغيير وغير مناسب لكيانات JPA (التي تتطلب مُنشئاً بدون معاملات وحقولاً قابلة للتغيير). ومع ذلك، فهو مناسب جداً للاستخدام كـ DTOs للطلبات/الاستجابات في طبقة المتحكم.
س كيف يجب تنظيم Postman Collection؟
ج جمّع الموارد حسب الفئة (المنتجات، الطلبات)، أضف تأكيدات (رموز الحالة، حقول الاستجابة) لكل طلب، واستخدم متغيرات البيئة لإدارة URL الأساسي.

📖 ملخص


📝 تمارين

  1. تمرين أساسي (الصعوبة: ⭐): أكتب جميع الكود الخاص بهذا الدرس، تأكد من تشغيل بيئة التطوير بنجاح، وتحقق من نجاح جميع أوامر اختبار curl.

  2. تمرين متقدم (الصعوبة ⭐⭐): أضف نقاط النهاية PUT /api/v1/products/{id} وDELETE /api/v1/products/{id}، أضف نقطة نهاية قائمة GET /api/v1/orders إلى OrderController، ونفذ التصفية باستخدام معامل الاستعلام status.

  3. تحدٍ (الصعوبة: ⭐⭐⭐): أنشئ orderflow-spring-boot-starter يغلف نقطة نهاية فحص صحة OrderFlow (التي تُرجع اسم التطبيق، الإصدار، Profile، ومعلومات JVM)، ادمجه في مشروع OrderFlow، وتحقق من سريان التهيئة التلقائية.

Web-Tutorial.com

فريق Web-Tutorial التقني

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

100%