تمرين المرحلة 1 الشامل: بناء الإطار الأساسي لـ OrderFlow
وضع المعرفة موضع التطبيق—هذا الدرس يجمع كل المفاهيم التي تم تناولها في الدروس الخمسة الأولى لبناء الإطار الأساسي الكامل لنظام إدارة الطلبات التجاري OrderFlow.
1. ما ستتعلمه
- إنشاء مشروع OrderFlow وتهيئة بيئتي dev وprod
- تنفيذ واجهة REST API الأساسية للمنتجات والطلبات
- استخدام
@ConfigurationPropertiesلإدارة معاملات قاعدة البيانات والأعمال - استبعاد التهيئة التلقائية غير الضرورية وتخصيص Starter بسيط
- تنظيم جميع حالات اختبار API باستخدام Postman Collection
2. قصة حقيقية عن عمل الفريق
(1) نقطة الألم: المعرفة مجزأة ولا يمكن تطبيقها
أكملت Alice تعلم التهيئة التلقائية لـ Spring Boot، وواجهات REST API، وإدارة التهيئة، ومواضيع أخرى، لكن عندما حان الوقت لبناء مشروع OrderFlow فعلياً، لم تكن تعرف من أين تبدأ. كل موضوع كان يُدرَّس بمعزل عن غيره، فلم تكن متأكدة كيف تتبدل بين H2 لبيئة التطوير وMySQL لبيئة الإنتاج، أو كيف تدمج واجهات REST API مع إدارة التهيئة.
(2) حلول التمارين الشاملة
يربط هذا الدرس جميع المفاهيم في مشروع كامل—من إنشاء المشروع، وتهيئة البيئة، وكتابة واجهات API إلى تخصيص Starter—لمحاكاة سير عمل التطوير الواقعي.
(3) العوائد
بمجرد أن أكملت Alice التمارين الشاملة، أصبح هيكل مشروع OrderFlow يخدم مباشرة كأساس للدورات اللاحقة، مما ضاعف كفاءة التطوير بثلاثة أضعاف.
3. إنشاء مشروع وتهيئة البيئة
(1) تهيئة المشروع
(1) ▶ مثال: إنشاء مشروع باستخدام Spring Initializr
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
الناتج:
{"status":"ok","data":{}}
(2) تهيئة البيئات المتعددة
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) هيكل حزم المشروع
com.orderflow/
├── OrderFlowApplication.java
├── config/
│ └── OrderFlowProperties.java
├── controller/
│ ├── ProductController.java
│ └── OrderController.java
├── model/
│ ├── Product.java
│ └── Order.java
└── repository/
├── ProductRepository.java
└── OrderRepository.java
(1) ▶ مثال: DTO والمتحكم للمنتجات
// 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();
}
}
الناتج:
// التنفيذ ناجح
(2) ▶ مثال: DTO والمتحكم للطلبات
// 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) {}
}
الناتج:
// التنفيذ ناجح
5. تهيئة الأعمال باستخدام ConfigurationProperties
(1) ▶ مثال: OrderFlowProperties
// 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
) {}
الناتج:
// التنفيذ ناجح
# 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) ▶ مثال: استبعاد التهيئات التلقائية غير المرغوب فيها
@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);
}
}
الناتج:
// التنفيذ ناجح
7. تنظيم مجموعات Postman
(1) ▶ مثال: نص اختبار Postman
# اختبارات واجهة 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
الناتج:
{"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
// 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
) {}
# 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
# تشغيل وضع التطوير
$ 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
❓ أسئلة شائعة
application.yml والتهيئات الخاصة بكل بيئة في application-{profile}.yml. احقن دائماً المعلومات الحساسة (كلمات المرور، المفاتيح) باستخدام متغيرات البيئة.The following 1 profile is active: "dev" في سجل التشغيل. يمكنك أيضاً حقن @Value("${spring.profiles.active}") في المتحكم وإعادته.record كـ DTO؟record غير قابل للتغيير وغير مناسب لكيانات JPA (التي تتطلب مُنشئاً بدون معاملات وحقولاً قابلة للتغيير). ومع ذلك، فهو مناسب جداً للاستخدام كـ DTOs للطلبات/الاستجابات في طبقة المتحكم.📖 ملخص
- استخدم Spring Initializr وProfile و@ConfigurationProperties معاً لإعداد هيكل المشروع
- واجهات REST API لـ
ProductوOrderتستخدم حالياً خريطة في الذاكرة للتخزين؛ سيتم استبدالها بـ JPA لاحقاً - تهيئات الأعمال مركزية في
OrderFlowProperties، مع دعم البيئات المتعددة عبرProfile - استبعد التهيئة التلقائية غير الضرورية لمنع أخطاء التشغيل
- مجموعات Postman مجمعة حسب المورد، مما يسهل إجراء الاختبارات المستمرة
📝 تمارين
-
تمرين أساسي (الصعوبة: ⭐): أكتب جميع الكود الخاص بهذا الدرس، تأكد من تشغيل بيئة التطوير بنجاح، وتحقق من نجاح جميع أوامر اختبار curl.
-
تمرين متقدم (الصعوبة ⭐⭐): أضف نقاط النهاية
PUT /api/v1/products/{id}وDELETE /api/v1/products/{id}، أضف نقطة نهاية قائمةGET /api/v1/ordersإلى OrderController، ونفذ التصفية باستخدام معامل الاستعلامstatus. -
تحدٍ (الصعوبة: ⭐⭐⭐): أنشئ
orderflow-spring-boot-starterيغلف نقطة نهاية فحص صحة OrderFlow (التي تُرجع اسم التطبيق، الإصدار، Profile، ومعلومات JVM)، ادمجه في مشروع OrderFlow، وتحقق من سريان التهيئة التلقائية.



