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

TP3.1 - الهندسة الطبقية وAPI CRUD كاملة

تطبيق إدارة الطلاب​

الأهداف​

سيمكنك هذا العمل العملي من:

  • فهم الهندسة الطبقية (Controller → Service → Repository)
  • إنشاء API REST CRUD كاملة
  • تنفيذ عمليات Create, Read, Update, Delete
  • استخدام Spring Data JPA مع H2 (قاعدة بيانات في الذاكرة)
  • اختبار API REST باستخدام Postman أو متصفح
  • إدارة الأخطاء والتحقق من الصحة

جدول المحتويات​

  1. الهندسة الطبقية
  2. إنشاء المشروع
  3. إنشاء الكيان Student
  4. إنشاء الـ Repository
  5. إنشاء الـ Service
  6. إنشاء الـ Controller
  7. إعداد H2
  8. اختبارات الـ Endpoints
  9. تمارين إضافية

الموارد​

التوثيق مع لقطات شاشة مفصلة:

📸 مراجع TP3.1 - Google Drive


1. الهندسة الطبقية​

نظرة عامة​

تفصل الهندسة الطبقية المسؤوليات إلى 4 مستويات مختلفة:

+----------------+       +----------------+       +----------------+       +----------------+
| | | | | | | |
| Controller +------>+ Service +------>+ Repository +------>+ Database |
| | | | | | | |
+----------------+ +----------------+ +----------------+ +----------------+
(HTTP) (منطق الأعمال) (الوصول إلى البيانات) (التخزين)

مخطط Mermaid​

مسؤوليات كل طبقة​

1. Controller (طبقة العرض)​

الدور:

  • نقطة الدخول للتطبيق
  • استقبال الطلبات HTTP (GET, POST, PUT, DELETE)
  • تحليل معلمات الطلب
  • استدعاء الـ Service المناسب
  • إرجاع الرد إلى العميل (JSON)

التعليقات التوضيحية:

  • @RestController
  • @RequestMapping
  • @GetMapping, @PostMapping, @PutMapping, @DeleteMapping

مثال:

@RestController
@RequestMapping("/api/students")
public class StudentController {

@Autowired
private StudentService studentService;

@GetMapping
public List<Student> getAllStudents() {
return studentService.getAllStudents();
}
}

2. Service (طبقة الأعمال)​

الدور:

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

التعليقات التوضيحية:

  • @Service
  • @Transactional (للمعاملات)

مثال:

@Service
public class StudentService {

@Autowired
private StudentRepository studentRepository;

public List<Student> getAllStudents() {
return studentRepository.findAll();
}
}

3. Repository (طبقة الوصول إلى البيانات)​

الدور:

  • التفاعل مباشرة مع قاعدة البيانات
  • إجراء عمليات CRUD
  • استخدام Spring Data JPA
  • توليد تلقائي لطلبات SQL

التعليقات التوضيحية:

  • @Repository
  • واجهة تمتد JpaRepository

مثال:

@Repository
public interface StudentRepository extends JpaRepository<Student, Long> {
// طرق يتم توليدها تلقائيًا بواسطة Spring Data JPA
}

4. Database (طبقة الاستمرارية)​

الدور:

  • التخزين الفعلي للبيانات
  • إدارة معاملات ACID
  • الفهرسة والتحسين

التقنيات:

  • H2 (قاعدة بيانات في الذاكرة للتطوير/الاختبارات)
  • PostgreSQL, MySQL (للإنتاج)

تدفق الطلب​


2. إنشاء المشروع​

2.1 Spring Initializr​

اذهب إلى https://start.spring.io/

الإعدادات:

المعلمةالقيمة
ProjectMaven
LanguageJava
Spring Boot3.2.0 (أو أحدث إصدار مستقر)
Groupcom.example
Artifactstudent-management
Namestudent-management
Package namecom.example.student
PackagingJar
Java17

الإضافات التي يجب إضافتها:

  1. Spring Web - لإنشاء API REST
  2. Spring Data JPA - للاستمرارية
  3. H2 Database - قاعدة بيانات في الذاكرة
  4. Validation - للتحقق من صحة البيانات
  5. Spring Boot DevTools - لإعادة التحميل التلقائي

2.2 الاستيراد في VS Code​

  1. قم بتنزيل المشروع (ZIP)
  2. فك الضغط في مساحة العمل الخاصة بك
  3. افتح VS Code
  4. File → Open Folder → اختر student-management
  5. انتظر حتى يقوم Maven بتنزيل الإضافات

2.3 هيكل المشروع​

قم بإنشاء هيكل الحزم التالي:

src/main/java/com/example/student/
├── StudentManagementApplication.java
├── controller/
│ └── StudentController.java
├── service/
│ └── StudentService.java
├── repository/
│ └── StudentRepository.java
├── model/
│ └── Student.java
└── exception/
└── ResourceNotFoundException.java

3. إنشاء الكيان Student​

3.1 فئة Student​

قم بإنشاء src/main/java/com/example/student/model/Student.java:

package com.example.student.model;

import jakarta.persistence.*;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

@Entity
@Table(name = "students")
public class Student {

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

@NotBlank(message = "الاسم الأول مطلوب")
@Size(min = 2, max = 50, message = "يجب أن يحتوي الاسم الأول على 2 إلى 50 حرفًا")
@Column(name = "first_name", nullable = false)
private String firstName;

@NotBlank(message = "الاسم مطلوب")
@Size(min = 2, max = 50, message = "يجب أن يحتوي الاسم على 2 إلى 50 حرفًا")
@Column(name = "last_name", nullable = false)
private String lastName;

@NotBlank(message = "البريد الإلكتروني مطلوب")
@Email(message = "تنسيق البريد الإلكتروني غير صالح")
@Column(name = "email", nullable = false, unique = true)
private String email;

@Column(name = "age")
private Integer age;

// منشئ افتراضي (مطلوب بواسطة JPA)
public Student() {
}

// منشئ مع المعلمات
public Student(String firstName, String lastName, String email, Integer age) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.age = age;
}

// Getters وSetters
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

@Override
public String toString() {
return "Student{" +
"id=" + id +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", age=" + age +
'}';
}
}

3.2 شرح التعليقات التوضيحية​

التعليق التوضيحيالدور
@Entityتحديد الفئة ككيان JPA
@Table(name = "students")اسم الجدول SQL
@Idالمفتاح الأساسي
@GeneratedValue(strategy = GenerationType.IDENTITY)الترقيم التلقائي
@Columnإعداد العمود
@NotBlankالتحقق: الحقل غير فارغ
@Sizeالتحقق: الحجم الأدنى/الأقصى
@Emailالتحقق: تنسيق البريد الإلكتروني

4. إنشاء الـ Repository​

4.1 واجهة StudentRepository​

قم بإنشاء src/main/java/com/example/student/repository/StudentRepository.java:

package com.example.student.repository;

import com.example.student.model.Student;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;

@Repository
public interface StudentRepository extends JpaRepository<Student, Long> {

// Spring Data JPA تولد التنفيذ تلقائيًا

// طرق مخصصة (اختيارية)
Optional<Student> findByEmail(String email);

boolean existsByEmail(String email);
}

4.2 الطرق المتاحة​

الطرق الموروثة من JpaRepository:

الطريقةالوصف
save(Student s)حفظ أو تحديث
findAll()استرجاع جميع الطلاب
findById(Long id)استرجاع بواسطة ID
deleteById(Long id)حذف بواسطة ID
count()عد السجلات
existsById(Long id)التحقق من الوجود

الطرق المخصصة:

  • findByEmail(String email) - البحث بواسطة البريد الإلكتروني
  • existsByEmail(String email) - التحقق مما إذا كان البريد الإلكتروني موجودًا

ملاحظة: Spring Data JPA تولد تلقائيًا طلبات SQL بناءً على اسم الطريقة!


5. إنشاء الـ Service​

5.1 فئة StudentService​

قم بإنشاء src/main/java/com/example/student/service/StudentService.java:

package com.example.student.service;

import com.example.student.model.Student;
import com.example.student.repository.StudentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;

@Service
public class StudentService {

@Autowired
private StudentRepository studentRepository;

// CREATE - إنشاء طالب
public Student createStudent(Student student) {
// التحقق مما إذا كان البريد الإلكتروني موجودًا بالفعل
if (studentRepository.existsByEmail(student.getEmail())) {
throw new RuntimeException("طالب بهذا البريد الإلكتروني موجود بالفعل");
}
return studentRepository.save(student);
}

// READ - استرجاع جميع الطلاب
public List<Student> getAllStudents() {
return studentRepository.findAll();
}

// READ - استرجاع طالب بواسطة ID
public Optional<Student> getStudentById(Long id) {
return studentRepository.findById(id);
}

// UPDATE - تحديث طالب
public Student updateStudent(Long id, Student studentDetails) {
Student student = studentRepository.findById(id)
.orElseThrow(() -> new RuntimeException("طالب غير موجود بالمعرف: " + id));

student.setFirstName(studentDetails.getFirstName());
student.setLastName(studentDetails.getLastName());
student.setEmail(studentDetails.getEmail());
student.setAge(studentDetails.getAge());

return studentRepository.save(student);
}

// DELETE - حذف طالب
public void deleteStudent(Long id) {
Student student = studentRepository.findById(id)
.orElseThrow(() -> new RuntimeException("طالب غير موجود بالمعرف: " + id));

studentRepository.delete(student);
}

// طريقة مساعدة - البحث بواسطة البريد الإلكتروني
public Optional<Student> getStudentByEmail(String email) {
return studentRepository.findByEmail(email);
}
}

5.2 منطق الأعمال​

يحتوي الـ Service على:

  1. التحقق: التحقق مما إذا كان البريد الإلكتروني موجودًا قبل الإنشاء
  2. إدارة الأخطاء: إطلاق استثناءات إذا لم يتم العثور على الطالب
  3. التنسيق: تنسيق الاستدعاءات إلى الـ Repository
  4. التحويل: تحويل البيانات إذا لزم الأمر

6. إنشاء الـ Controller​

6.1 فئة StudentController​

قم بإنشاء src/main/java/com/example/student/controller/StudentController.java:

package com.example.student.controller;

import com.example.student.model.Student;
import com.example.student.service.StudentService;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/students")
public class StudentController {

@Autowired
private StudentService studentService;

// CREATE - POST /api/students
@PostMapping
public ResponseEntity<Student> createStudent(@Valid @RequestBody Student student) {
try {
Student createdStudent = studentService.createStudent(student);
return new ResponseEntity<>(createdStudent, HttpStatus.CREATED);
} catch (RuntimeException e) {
return new ResponseEntity<>(null, HttpStatus.CONFLICT);
}
}

// READ ALL - GET /api/students
@GetMapping
public ResponseEntity<List<Student>> getAllStudents() {
List<Student> students = studentService.getAllStudents();
return new ResponseEntity<>(students, HttpStatus.OK);
}

// READ ONE - GET /api/students/{id}
@GetMapping("/{id}")
public ResponseEntity<Student> getStudentById(@PathVariable Long id) {
return studentService.getStudentById(id)
.map(student -> new ResponseEntity<>(student, HttpStatus.OK))
.orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}

// UPDATE - PUT /api/students/{id}
@PutMapping("/{id}")
public ResponseEntity<Student> updateStudent(
@PathVariable Long id,
@Valid @RequestBody Student studentDetails) {
try {
Student updatedStudent = studentService.updateStudent(id, studentDetails);
return new ResponseEntity<>(updatedStudent, HttpStatus.OK);
} catch (RuntimeException e) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}

// DELETE - DELETE /api/students/{id}
@DeleteMapping("/{id}")
public ResponseEntity<HttpStatus> deleteStudent(@PathVariable Long id) {
try {
studentService.deleteStudent(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (RuntimeException e) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}

// SEARCH BY EMAIL - GET /api/students/email/{email}
@GetMapping("/email/{email}")
public ResponseEntity<Student> getStudentByEmail(@PathVariable String email) {
return studentService.getStudentByEmail(email)
.map(student -> new ResponseEntity<>(student, HttpStatus.OK))
.orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
}

6.2 Endpoints REST​

طريقة HTTPEndpointالإجراءرمز النجاح
POST/api/studentsإنشاء طالب201 CREATED
GET/api/studentsقائمة جميع الطلاب200 OK
GET/api/students/\{id\}استرجاع طالب200 OK
PUT/api/students/\{id\}تحديث طالب200 OK
DELETE/api/students/\{id\}حذف طالب204 NO CONTENT
GET/api/students/email/\{email\}البحث بواسطة البريد الإلكتروني200 OK

6.3 رموز الحالة HTTP​


7. إعداد H2​

7.1 ملف application.properties​

قم بإنشاء/تعديل src/main/resources/application.properties:

# منفذ الخادم
server.port=8080

# إعداد H2 Database
spring.datasource.url=jdbc:h2:mem:studentdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

# إعداد JPA
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

# وحدة التحكم H2 (متاحة عبر المتصفح)
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

7.2 الوصول إلى وحدة التحكم H2​

بمجرد بدء التطبيق، قم بالوصول إلى:

http://localhost:8080/h2-console

إعدادات الاتصال:

الحقلالقيمة
JDBC URLjdbc:h2:mem:studentdb
User Namesa
Password(اتركه فارغًا)

8. اختبارات الـ Endpoints​

8.1 بدء التطبيق​

في VS Code:

mvn clean install -DskipTests spring-boot:run

السجلات المتوقعة:

Hibernate: create table students (...)
Started StudentManagementApplication in X.XXX seconds

8.2 اختبارات باستخدام المتصفح​

اختبار 1: إنشاء طالب (POST)​

ملاحظة: المتصفح لا يمكنه القيام بـ POST بسهولة. استخدم Postman أو curl.

باستخدام curl (PowerShell/CMD):

curl -X POST http://localhost:8080/api/students ^
-H "Content-Type: application/json" ^
-d "{\"firstName\":\"Jean\",\"lastName\":\"Dupont\",\"email\":\"jean.dupont@example.com\",\"age\":20}"

اختبار 2: قائمة جميع الطلاب (GET)​

في المتصفح:

http://localhost:8080/api/students

الاستجابة المتوقعة:

[
{
"id": 1,
"firstName": "Jean",
"lastName": "Dupont",
"email": "jean.dupont@example.com",
"age": 20
}
]

اختبار 3: استرجاع طالب بواسطة ID (GET)​

http://localhost:8080/api/students/1

الاستجابة المتوقعة:

{
"id": 1,
"firstName": "Jean",
"lastName": "Dupont",
"email": "jean.dupont@example.com",
"age": 20
}

8.3 اختبارات باستخدام Postman​

تثبيت Postman​

  1. قم بتنزيل Postman: https://www.postman.com/downloads/
  2. قم بتثبيت وتشغيل Postman

مجموعة الاختبارات​

اختبار 1: CREATE (POST)

  • الطريقة: POST
  • URL: http://localhost:8080/api/students
  • Headers: Content-Type: application/json
  • Body (raw JSON):
{
"firstName": "Alice",
"lastName": "Martin",
"email": "alice.martin@example.com",
"age": 22
}

النتيجة المتوقعة: حالة 201 CREATED

اختبار 2: READ ALL (GET)

  • الطريقة: GET
  • URL: http://localhost:8080/api/students

النتيجة المتوقعة: حالة 200 OK + قائمة الطلاب

اختبار 3: READ ONE (GET)

  • الطريقة: GET
  • URL: http://localhost:8080/api/students/1

النتيجة المتوقعة: حالة 200 OK + تفاصيل الطالب

اختبار 4: UPDATE (PUT)

  • الطريقة: PUT
  • URL: http://localhost:8080/api/students/1
  • Headers: Content-Type: application/json
  • Body (raw JSON):
{
"firstName": "Alice",
"lastName": "Martin-Modifié",
"email": "alice.martin.updated@example.com",
"age": 23
}

النتيجة المتوقعة: حالة 200 OK + طالب محدث

اختبار 5: DELETE (DELETE)

  • الطريقة: DELETE
  • URL: http://localhost:8080/api/students/1

النتيجة المتوقعة: حالة 204 NO CONTENT

اختبار 6: SEARCH BY EMAIL (GET)

  • الطريقة: GET
  • URL: http://localhost:8080/api/students/email/alice.martin@example.com

النتيجة المتوقعة: حالة 200 OK + طالب موجود

8.4 التحقق في وحدة التحكم H2​

  1. قم بالوصول إلى http://localhost:8080/h2-console
  2. قم بتسجيل الدخول
  3. نفذ الطلب:
SELECT * FROM students;

يجب أن ترى جميع الطلاب الذين تم إنشاؤهم.


9. تمارين إضافية​

تمرين 1: إضافة حقل "برنامج"​

الهدف: إضافة حقل program (String) إلى الكيان Student

التعليمات:

  1. قم بتعديل الفئة Student.java
  2. أضف الحقل program
  3. قم بإنشاء getters/setters
  4. اختبر إنشاء طالب مع برنامج

تمرين 2: البحث بواسطة الاسم​

الهدف: تنفيذ البحث بواسطة اسم العائلة

التعليمات:

  1. أضف طريقة في StudentRepository:
    List<Student> findByLastName(String lastName);
  2. أضف طريقة في StudentService
  3. أضف endpoint في StudentController:
    @GetMapping("/lastname/\{lastName\}")
  4. اختبر باستخدام Postman

تمرين 3: الترقيم​

الهدف: إضافة الترقيم إلى قائمة الطلاب

التعليمات:

  1. قم بتعديل الطريقة getAllStudents() في الـ Service:
    public Page<Student> getAllStudents(int page, int size) {
    Pageable pageable = PageRequest.of(page, size);
    return studentRepository.findAll(pageable);
    }
  2. قم بتعديل الـ Controller لقبول المعلمات page وsize
  3. اختبر: http://localhost:8080/api/students?page=0&size=5

تمرين 4: إدارة الأخطاء المخصصة​

الهدف: إنشاء إدارة أخطاء شاملة

التعليمات:

  1. قم بإنشاء ResourceNotFoundException.java:
package com.example.student.exception;

public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
  1. قم بإنشاء GlobalExceptionHandler.java:
package com.example.student.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<String> handleResourceNotFound(ResourceNotFoundException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
}
}
  1. استخدم ResourceNotFoundException في الـ Service
  2. اختبر بمحاولة الوصول إلى طالب غير موجود

تمرين 5: التحقق المتقدم​

الهدف: إضافة التحقق المخصص

التعليمات:

  1. أضف قيود في Student.java:
    @Min(value = 18, message = "الحد الأدنى للعمر هو 18 عامًا")
    @Max(value = 100, message = "الحد الأقصى للعمر هو 100 عامًا")
    private Integer age;
  2. اختبر بإنشاء طالب بعمر غير صالح
  3. لاحظ الرمز 400 BAD REQUEST

تمرين 6: الانتقال إلى PostgreSQL​

الهدف: استبدال H2 بـ PostgreSQL

التعليمات:

  1. أضف اعتماد PostgreSQL في pom.xml
  2. قم بتعديل application.properties:
    spring.datasource.url=jdbc:postgresql://localhost:5432/studentdb
    spring.datasource.username=postgres
    spring.datasource.password=postgres
    spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
    spring.jpa.hibernate.ddl-auto=update
  3. قم بإنشاء قاعدة البيانات studentdb في PostgreSQL
  4. أعد تشغيل التطبيق
  5. تحقق من أن البيانات تستمر بعد إعادة التشغيل

حل المشاكل​

مشكلة 1: خطأ 404 على جميع الـ endpoints​

السبب: الـ Controller غير مفحوص بواسطة Spring

الحل:

تحقق من أن StudentController في نفس الحزمة (أو حزمة فرعية) لـ StudentManagementApplication.

مشكلة 2: خطأ في التحقق​

الأعراض:

Validation failed for object='student'

الحل:

تحقق من أن البيانات المرسلة تلتزم بالقيود @NotBlank, @Email, @Size, إلخ.

مشكلة 3: وحدة التحكم H2 غير متاحة​

الأعراض:

404 on /h2-console

الحل:

تحقق في application.properties:

spring.h2.console.enabled=true

مشكلة 4: تعارض البريد الإلكتروني​

الأعراض:

Status 409 CONFLICT

الحل:

طبيعي! طالب بهذا البريد الإلكتروني موجود بالفعل. هذا هو التحقق الذي يعمل.


أسئلة المراجعة​

  1. ما هي مسؤولية كل طبقة (Controller, Service, Repository)؟

  2. لماذا نفصل الكود إلى طبقات؟

  3. ما الفرق بين @RestController و@Service؟

  4. ماذا تفعل التعليمة @Valid في الـ Controller؟

  5. كيف يقوم Spring Data JPA بتوليد طلبات SQL؟

  6. ما الفرق بين save() للإنشاء والتحديث؟

  7. لماذا نستخدم Optional<Student> في الـ Service؟

  8. ما هو رمز HTTP الذي يجب إرجاعه بعد حذف ناجح؟


التسليمات​

يجب تسليم:

  1. الكود المصدري الكامل للمشروع
  2. لقطات شاشة Postman تظهر 6 اختبارات ناجحة
  3. لقطة شاشة وحدة التحكم H2 مع البيانات
  4. على الأقل 2 من التمارين المكتملة
  5. وثيقة إجابات لأسئلة المراجعة

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

المعيارالنقاط
الهندسة الطبقية مطبقة بشكل صحيح20%
كيان Student مع التحقق15%
Repository وظيفي10%
Service مع منطق الأعمال20%
Controller مع جميع الـ endpoints CRUD20%
اختبارات ناجحة (Postman/Browser)10%
تمارين إضافية5%
المجموع100%

الملخص​

ما تعلمته​

  • الهندسة الطبقية (Controller → Service → Repository → Database)
  • إنشاء API REST CRUD كاملة
  • عمليات CRUD: Create, Read, Update, Delete
  • التحقق من البيانات باستخدام Bean Validation
  • إدارة رموز الحالة HTTP
  • استخدام H2 للتطوير
  • اختبارات API باستخدام Postman

الممارسات الجيدة​

  • فصل المسؤوليات في طبقات مختلفة
  • التحقق من البيانات عند الإدخال (Controller)
  • إدارة الأخطاء بشكل صحيح (استثناءات مخصصة)
  • استخدام رموز HTTP المناسبة
  • اختبار جميع الـ endpoints
  • توثيق الـ API

الخطوات التالية​

في الأعمال العملية القادمة، سنرى:

  • TP4: الأمان باستخدام Spring Security
  • TP5: التوثيق باستخدام Swagger/OpenAPI
  • TP6: النشر في الإنتاج

موارد إضافية​

التوثيق​

الدروس​


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