Kotlin: شرح تصميم مشاريع كوتلن

آخر تحديث: 2026-08-26

جميع الدروس الأربعة والعشرون تتلاقى هنا — يبدأ Charlie من تحليل المتطلبات ويصمم نموذج نطاق OrderProcessor، والبنية المعمارية الطبقية، واختيار مجموعة التقنيات، وعقود واجهة برمجة التطبيقات. هذه هي الخطوة الأولى للمشروع الحقيقي.

1. ما ستتعلمه


2. قصة مهندس معمارية حقيقي

(1) المشكلة: التصميم بدون خطة يضمن إعادة العمل

فريق Charlie تخطى التصميم وبدأ البرمجة مباشرة مرة واحدة. بعد ثلاثة أشهر، نموذج النطاق لم يتطابق مع متطلبات العمل — أُعيد كتابة 60% من الكود. الخسارة: 3 أشهر تطوير + شهران إعادة عمل.

(2) نهج التصميم أولاً

TEXT 📖 للعرض فقط
الأسبوع 1: المتطلبات ← نموذج النطاق ← آلة الحالة
الأسبوع 2: البنية المعمارية ← مجموعة التقنيات ← عقد API
الأسبوع 3: مخطط قاعدة البيانات ← حدود الوحدات ← خطة CI/CD
الأسبوع 4+: التطوير (بمخطط واضح)

استثمر 3 أسابيع في التصميم، قلل إعادة العمل بنسبة 60%. "التطوير السريع" بدون تصميم هو أبطأ طريق.


3. تحليل المتطلبات ونمذجة النطاق

(1) المتطلبات الأساسية

المتطلب الوصف الأولوية
إنشاء الطلب العميل يضع طلبًا، يُولّد طلبًا معلقًا P0
دفع الطلب تأكيد الطلب بعد دفع ناجح P0
شحن الطلب المستودع يشحن، يُولّد رقم تتبع P0
إلغاء الطلب العميل/النظام يلغي الطلب، يُطلق استرداد P0
توجيه الطلبات عالية القيمة الطلبات > 10,000 دولار تمر عبر خط VIP P1
المعالجة الدفعية دعم إنتاجية 5,000 طلب/ثانية P1
مصدر الأحداث تغييرات حالة الطلب تُنتج أحداث نطاق P2

(2) نموذج النطاق

KOTLIN
// كيانات النطاق الأساسية
data class Order(
    val id: String,
    val total: Double,
    var status: OrderStatus,
    val customerId: String,
    val items: List<OrderItem>,
    val createdAt: String,
    var updatedAt: String?
)

data class OrderItem(val sku: String, val quantity: Int, val unitPrice: Double)

sealed class OrderStatus {
    object Pending : OrderStatus()
    data class Processing(val step: Int) : OrderStatus()
    object Paid : OrderStatus()
    object Shipped : OrderStatus()
    object Delivered : OrderStatus()
    data class Cancelled(val reason: String) : OrderStatus()
}

sealed class OrderEvent {
    data class Created(val orderId: String, val total: Double) : OrderEvent()
    data class PaymentReceived(val orderId: String, val amount: Double) : OrderEvent()
    data class Shipped(val orderId: String, val trackingCode: String) : OrderEvent()
    data class Cancelled(val orderId: String, val reason: String) : OrderEvent()
}

(3) آلة حالة الطلب

100%
stateDiagram-v2
    [*] --> Pending: إنشاء طلب
    Pending --> Processing: بدء المعالجة
    Pending --> Cancelled: إلغاء العميل
    Processing --> Paid: نجاح الدفع
    Processing --> Cancelled: فشل الدفع
    Paid --> Shipped: شحن الطلب
    Shipped --> Delivered: تأكيد التسليم
    Delivered --> [*]
    Cancelled --> [*]

4. البنية المعمارية الطبقية

(1) بنية من أربع طبقات

100%
flowchart TD
    A[طبقة وحدة التحكم<br/>REST API / التحقق من الطلبات] --> B[طبقة الخدمة<br/>منطق الأعمال / آلة الحالة]
    B --> C[طبقة المستودع<br/>الوصول للبيانات / الاستمرارية]
    B --> D[طبقة التكامل<br/>استدعاءات API خارجية]
    C --> E[(قاعدة البيانات)]
    D --> F[خدمة الدفع]
    D --> G[خدمة المخزون]

(2) مسؤوليات الطبقات

الطبقة المسؤولية التقنية الأساسية
وحدة التحكم معالجة طلبات HTTP، التحقق، الاستجابة Spring WebFlux، suspend
الخدمة منطق الأعمال، آلة الحالة، نشر الأحداث الكوروتينات، sealed class
المستودع استمرارية البيانات، الاستعلامات R2DBC، JPA
التكامل استدعاءات الخدمات الخارجية عميل Ktor، Resilience4j

(3) هيكل وحدات المشروع

TEXT 📖 للعرض فقط
order-processor/
├── build.gradle.kts
├── src/main/kotlin/com/order/
│   ├── Application.kt                    # نقطة دخول Spring Boot
│   ├── controller/
│   │   └── OrderController.kt            # نقاط نهاية REST
│   ├── service/
│   │   ├── OrderService.kt               # منطق الأعمال
│   │   └── OrderStateMachine.kt          # انتقالات الحالة
│   ├── repository/
│   │   ├── OrderRepository.kt            # الوصول للبيانات
│   │   └── EventRepository.kt            # مخزن الأحداث
│   ├── integration/
│   │   ├── PaymentClient.kt              # API الدفع
│   │   └── InventoryClient.kt            # API المخزون
│   ├── domain/
│   │   ├── Order.kt                      # الكيان
│   │   ├── OrderStatus.kt                # تعداد الحالة
│   │   └── OrderEvent.kt                 # أحداث النطاق
│   ├── config/
│   │   └── AppConfig.kt                  # التهيئة
│   └── exception/
│       └── OrderExceptions.kt            # استثناءات مخصصة
└── src/test/kotlin/com/order/
    └── ...                                # الاختبارات

5. اختيار مجموعة التقنيات

النطاق الاختيار التقني السبب
إطار العمل Spring Boot 3.2 + WebFlux نظام بيئي ناضج، دعم الكوروتينات
اللغة كوتلن 1.9 أمان null، الكوروتينات، فئة data
غير متزامن kotlinx.coroutines تزامن مهيكل، suspend
التسلسل kotlinx.serialization أمان وقت الترجمة، تنسيقات متعددة
قاعدة البيانات PostgreSQL + R2DBC مشغل تفاعلي، صديق للكوروتينات
عميل HTTP عميل Ktor أصلي لكوتلن، دعم الكوروتينات
التخزين المؤقت Redis + kotlinx.coroutines تخزين مؤقت غير متزامن
الاختبار JUnit 5 + MockK محاكاة أصيلة لكوتلن
المراقبة Micrometer + Prometheus جمع المقاييس

6. تصميم API

(1) نقاط نهاية RESTful

KOTLIN
// نقاط نهاية API
// POST   /api/v1/orders              - إنشاء طلب
// GET    /api/v1/orders              - قائمة الطلبات (مع ترقيم الصفحات)
// GET    /api/v1/orders/{id}         - الحصول على طلب بالمعرف
// PATCH  /api/v1/orders/{id}/status  - تحديث حالة الطلب
// DELETE /api/v1/orders/{id}         - إلغاء طلب
// GET    /api/v1/orders/{id}/events  - الحصول على سجل أحداث الطلب

(2) DTOs الطلب/الاستجابة

KOTLIN
// طلب
data class CreateOrderRequest(
    val customerId: String,
    val items: List<CreateOrderItemRequest>,
    val shippingAddress: AddressRequest?
)

data class CreateOrderItemRequest(
    val sku: String,
    val quantity: Int,
    val unitPrice: Double
)

// استجابة
data class OrderResponse(
    val id: String,
    val total: Double,
    val status: String,
    val customerId: String,
    val items: List<OrderItemResponse>,
    val createdAt: String,
    val updatedAt: String?
)

// خطأ
data class ErrorResponse(
    val code: String,
    val message: String,
    val details: Map<String, String>? = null
)

(3) مبادئ تصميم API

المبدأ الممارسة
RESTful أسماء الموارد + دلالات طرق HTTP
الإصدارات بادئة /api/v1/
ترقيم الصفحات ?page=0&size=20
التصفية ?status=CONFIRMED&customer=C001
HATEOAS تضمين روابط ذات صلة في الاستجابة (اختياري)
تنسيق الخطأ ErrorResponse موحد

7. مخطط قاعدة البيانات

SQL
CREATE TABLE orders (
    id          VARCHAR(20) PRIMARY KEY,
    customer_id VARCHAR(20) NOT NULL,
    total       DECIMAL(12,2) NOT NULL DEFAULT 0,
    status      VARCHAR(20) NOT NULL DEFAULT 'PENDING',
    created_at  TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at  TIMESTAMP
);

CREATE TABLE order_items (
    id          SERIAL PRIMARY KEY,
    order_id    VARCHAR(20) NOT NULL REFERENCES orders(id),
    sku         VARCHAR(50) NOT NULL,
    quantity    INT NOT NULL,
    unit_price  DECIMAL(10,2) NOT NULL
);

CREATE TABLE order_events (
    id          SERIAL PRIMARY KEY,
    order_id    VARCHAR(20) NOT NULL,
    event_type  VARCHAR(30) NOT NULL,
    payload     JSONB,
    created_at  TIMESTAMP NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_orders_status ON orders(status);
CREATE INDEX idx_orders_customer ON orders(customer_id);
CREATE INDEX idx_events_order ON order_events(order_id);

8. مثال كامل: وثيقة تصميم بنية OrderProcessor

KOTLIN
// ============================================
// OrderProcessor - تصميم البنية المعمارية
// الميزة: مخطط معماري كامل
// ============================================

// --- طبقة النطاق ---
data class OrderItem(val sku: String, val qty: Int, val unitPrice: Double) {
    val subtotal: Double get() = qty * unitPrice
}

sealed class OrderStatus { 
    object Pending : OrderStatus()
    object Confirmed : OrderStatus()
    object Shipped : OrderStatus()
    object Delivered : OrderStatus()
    data class Cancelled(val reason: String) : OrderStatus()
}

sealed class OrderEvent {
    data class Created(val orderId: String, val total: Double, val customerId: String) : OrderEvent()
    data class Confirmed(val orderId: String) : OrderEvent()
    data class Shipped(val orderId: String, val trackingCode: String) : OrderEvent()
    data class Delivered(val orderId: String) : OrderEvent()
    data class Cancelled(val orderId: String, val reason: String) : OrderEvent()
}

data class Order(
    val id: String,
    val total: Double,
    var status: OrderStatus,
    val customerId: String,
    val items: List<OrderItem>
)

// --- آلة الحالة ---
object OrderStateMachine {
    fun transition(current: OrderStatus, event: OrderEvent): OrderStatus = when {
        current is OrderStatus.Pending && event is OrderEvent.Created -> OrderStatus.Pending
        current is OrderStatus.Pending && event is OrderEvent.Confirmed -> OrderStatus.Confirmed
        current is OrderStatus.Confirmed && event is OrderEvent.Shipped -> OrderStatus.Shipped
        current is OrderStatus.Shipped && event is OrderEvent.Delivered -> OrderStatus.Delivered
        current is OrderStatus.Pending && event is OrderEvent.Cancelled -> OrderStatus.Cancelled(event.reason)
        current is OrderStatus.Confirmed && event is OrderEvent.Cancelled -> OrderStatus.Cancelled(event.reason)
        else -> throw IllegalStateException("انتقال غير صالح: $current + $event")
    }
}

// --- ملخص البنية المعمارية ---
fun main() {
    println("=== تصميم بنية OrderProcessor ===\n")

    println("مجموعة التقنيات:")
    println("  إطار العمل: Spring Boot 3.2 + WebFlux")
    println("  اللغة:   كوتلن 1.9")
    println("  غير متزامن:      kotlinx.coroutines")
    println("  قاعدة البيانات:   PostgreSQL + R2DBC")
    println("  التخزين المؤقت:      Redis")
    println("  HTTP:       عميل Ktor")
    println("  الاختبار:    JUnit 5 + MockK")
    println("  المراقبة: Micrometer + Prometheus")

    println("\nآلة حالة الطلب:")
    val order = Order("ORD-001", 299.99, OrderStatus.Pending, "CUST-001", emptyList())
    val confirmed = OrderStateMachine.transition(order.status, OrderEvent.Confirmed("ORD-001"))
    println("  Pending + Confirmed = $confirmed")

    val shipped = OrderStateMachine.transition(confirmed, OrderEvent.Shipped("ORD-001", "TRK-ABC"))
    println("  Confirmed + Shipped = $shipped")

    val delivered = OrderStateMachine.transition(shipped, OrderEvent.Delivered("ORD-001"))
    println("  Shipped + Delivered = $delivered")

    println("\nنقاط نهاية API:")
    println("  POST   /api/v1/orders              - إنشاء طلب")
    println("  GET    /api/v1/orders              - قائمة الطلبات")
    println("  GET    /api/v1/orders/{id}         - الحصول على طلب")
    println("  PATCH  /api/v1/orders/{id}/status  - تحديث الحالة")
    println("  DELETE /api/v1/orders/{id}         - إلغاء طلب")
    println("  GET    /api/v1/orders/{id}/events  - سجل الأحداث")
}

الإخراج:

TEXT 📖 للعرض فقط
=== تصميم بنية OrderProcessor ===

مجموعة التقنيات:
  إطار العمل: Spring Boot 3.2 + WebFlux
  اللغة:   كوتلن 1.9
  غير متزامن:      kotlinx.coroutines
  قاعدة البيانات:   PostgreSQL + R2DBC
  التخزين المؤقت:      Redis
  HTTP:       عميل Ktor
  الاختبار:    JUnit 5 + MockK
  المراقبة: Micrometer + Prometheus

آلة حالة الطلب:
  Pending + Confirmed = Confirmed
  Confirmed + Shipped = Shipped
  Shipped + Delivered = Delivered

نقاط نهاية API:
  POST   /api/v1/orders              - إنشاء طلب
  GET    /api/v1/orders              - قائمة الطلبات
  GET    /api/v1/orders/{id}         - الحصول على طلب
  PATCH  /api/v1/orders/{id}/status  - تحديث الحالة
  DELETE /api/v1/orders/{id}         - إلغاء طلب
  GET    /api/v1/orders/{id}/events  - سجل الأحداث

9. أمثلة عملية سريعة

▶ مثال: تصميم Domain Model

KOTLIN
// نمط DDD: Aggregates, Entities, Value Objects
// Aggregate Root: Order
data class Order(
    val id: OrderId,           // Value Object
    val customerId: CustomerId, // Value Object
    val items: List<OrderItem>, // Entities
    val status: OrderStatus,   // Value Object (enum)
    val createdAt: Instant,
    val updatedAt: Instant
) {
    init {
        require(items.isNotEmpty()) { "Order must have at least one item" }
        require(items.size <= 100) { "Order cannot have more than 100 items" }
    }

    fun total(): Money = items.fold(Money.ZERO) { acc, item -> acc + item.subtotal() }

    fun confirm(): Order {
        require(status == OrderStatus.PENDING) { "Only PENDING orders can be confirmed" }
        return copy(status = OrderStatus.CONFIRMED, updatedAt = Instant.now())
    }

    fun cancel(): Order {
        require(status in setOf(OrderStatus.PENDING, OrderStatus.CONFIRMED)) {
            "Cannot cancel a ${status} order"
        }
        return copy(status = OrderStatus.CANCELLED, updatedAt = Instant.now())
    }

    fun ship(): Order {
        require(status == OrderStatus.CONFIRMED) { "Only CONFIRMED orders can be shipped" }
        return copy(status = OrderStatus.SHIPPED, updatedAt = Instant.now())
    }
}

// Value Objects
@JvmInline
value class OrderId(val value: String) {
    init { require(value.matches(Regex("^ORD-\\d{6}$"))) { "Invalid OrderId: $value" } }
}

@JvmInline
value class CustomerId(val value: String) {
    init { require(value.isNotBlank()) }
}

enum class OrderStatus { PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED }

data class OrderItem(
    val productId: String,
    val quantity: Int,
    val unitPrice: Money
) {
    init { require(quantity > 0) }
    fun subtotal(): Money = unitPrice * quantity
}

@JvmInline
value class Money(val cents: Long) : Comparable<Money> {
    operator fun plus(other: Money) = Money(cents + other.cents)
    operator fun times(qty: Int) = Money(cents * qty)
    override fun compareTo(other: Money) = cents.compareTo(other.cents)

    companion object {
        val ZERO = Money(0)
    }
}

**الإخراج:

TEXT 📖 للعرض فقط

▶ مثال: Repository Pattern

KOTLIN
// Repository interface in domain layer
interface OrderRepository {
    suspend fun save(order: Order): Order
    suspend fun findById(id: OrderId): Order?
    suspend fun findByCustomer(customerId: CustomerId): List<Order>
    suspend fun findByStatus(status: OrderStatus): List<Order>
    suspend fun delete(id: OrderId): Boolean
    fun observeAll(): Flow<Order>  // للوقت الحقيقي
}

// تطبيق باستخدام JPA
@Repository
class JpaOrderRepository(
    private val jpaRepo: JpaOrderEntityRepository,
    private val mapper: OrderMapper
) : OrderRepository {

    override suspend fun save(order: Order): Order {
        val entity = mapper.toEntity(order)
        val saved = jpaRepo.save(entity)
        return mapper.toDomain(saved)
    }

    override suspend fun findById(id: OrderId): Order? =
        jpaRepo.findById(id.value)
            .map { mapper.toDomain(it) }
            .orElse(null)

    override suspend fun findByCustomer(customerId: CustomerId): List<Order> =
        jpaRepo.findByCustomerId(customerId.value)
            .map { mapper.toDomain(it) }

    override suspend fun findByStatus(status: OrderStatus): List<Order> =
        jpaRepo.findByStatus(status.name)
            .map { mapper.toDomain(it) }

    override suspend fun delete(id: OrderId): Boolean {
        return if (jpaRepo.existsById(id.value)) {
            jpaRepo.deleteById(id.value)
            true
        } else false
    }

    override fun observeAll(): Flow<Order> =
        jpaRepo.findAll().map { mapper.toDomain(it) }.asFlow()
}

// في الذاكرة للاختبارات
class InMemoryOrderRepository : OrderRepository {
    private val store = mutableMapOf<OrderId, Order>()

    override suspend fun save(order: Order): Order {
        store[order.id] = order
        return order
    }

    override suspend fun findById(id: OrderId): Order? = store[id]

    override suspend fun findByCustomer(customerId: CustomerId): List<Order> =
        store.values.filter { it.customerId == customerId }

    override suspend fun findByStatus(status: OrderStatus): List<Order> =
        store.values.filter { it.status == status }

    override suspend fun delete(id: OrderId): Boolean = store.remove(id) != null

    override fun observeAll(): Flow<Order> = store.values.asFlow()
}

**الإخراج:

TEXT 📖 للعرض فقط

▶ مثال: Application Service

KOTLIN
@Service
class OrderApplicationService(
    private val orderRepo: OrderRepository,
    private val eventPublisher: EventPublisher
) {
    suspend fun placeOrder(command: PlaceOrderCommand): Order {
        // 1. بناء Aggregate
        val order = Order(
            id = OrderId.generate(),
            customerId = CustomerId(command.customerId),
            items = command.items.map {
                OrderItem(it.productId, it.quantity, Money(it.unitPriceCents))
            },
            status = OrderStatus.PENDING,
            createdAt = Instant.now(),
            updatedAt = Instant.now()
        )

        // 2. حفظ
        val saved = orderRepo.save(order)

        // 3. نشر حدث
        eventPublisher.publish(OrderPlacedEvent(saved.id, saved.total()))

        return saved
    }

    suspend fun confirmOrder(orderId: OrderId): Order {
        val order = orderRepo.findById(orderId)
            ?: throw OrderNotFoundException(orderId)

        val confirmed = order.confirm()
        val saved = orderRepo.save(confirmed)

        eventPublisher.publish(OrderConfirmedEvent(saved.id))
        return saved
    }

    suspend fun cancelOrder(orderId: OrderId): Order {
        val order = orderRepo.findById(orderId)
            ?: throw OrderNotFoundException(orderId)

        val cancelled = order.cancel()
        val saved = orderRepo.save(cancelled)

        eventPublisher.publish(OrderCancelledEvent(saved.id))
        return saved
    }
}

data class PlaceOrderCommand(
    val customerId: String,
    val items: List<OrderItemDto>
) {
    data class OrderItemDto(
        val productId: String,
        val quantity: Int,
        val unitPriceCents: Long
    )
}

**الإخراج:

TEXT 📖 للعرض فقط

▶ مثال: Event-driven architecture

KOTLIN
// Domain Event base
interface DomainEvent {
    val occurredAt: Instant
    val aggregateId: String
}

data class OrderPlacedEvent(
    val orderId: OrderId,
    val total: Money,
    override val occurredAt: Instant = Instant.now()
) : DomainEvent {
    override val aggregateId: String get() = orderId.value
}

data class OrderConfirmedEvent(
    val orderId: OrderId,
    override val occurredAt: Instant = Instant.now()
) : DomainEvent {
    override val aggregateId: String get() = orderId.value
}

// Event Publisher interface
interface EventPublisher {
    suspend fun publish(event: DomainEvent)
}

// تطبيق In-Memory
@Service
class InMemoryEventPublisher : EventPublisher {
    private val handlers = mutableMapOf<Class<*>, MutableList<suspend (Any) -> Unit>>()

    override suspend fun publish(event: DomainEvent) {
        handlers[event::class.java]?.forEach { it(event) }
    }

    fun <T : DomainEvent> subscribe(type: Class<T>, handler: suspend (T) -> Unit) {
        handlers.getOrPut(type) { mutableListOf() }.add { handler(it as T) }
    }
}

// Event Handler
@Service
class OrderEventHandler(
    private val emailService: EmailService,
    private val analyticsService: AnalyticsService
) {
    fun registerHandlers(publisher: InMemoryEventPublisher) {
        publisher.subscribe(OrderPlacedEvent::class.java) { event ->
            emailService.sendOrderConfirmation(event.orderId)
            analyticsService.trackOrderPlaced(event.total)
        }

        publisher.subscribe(OrderConfirmedEvent::class.java) { event ->
            analyticsService.trackOrderConfirmed(event.orderId)
        }
    }
}

interface EmailService {
    suspend fun sendOrderConfirmation(orderId: OrderId)
}

interface AnalyticsService {
    suspend fun trackOrderPlaced(total: Money)
    suspend fun trackOrderConfirmed(orderId: OrderId)
}

**الإخراج:

TEXT 📖 للعرض فقط

▶ مثال: REST API versioning

KOTLIN
// v1 - الإصدار الحالي
@RestController
@RequestMapping("/api/v1/orders")
class OrderControllerV1(private val service: OrderApplicationService) {

    @GetMapping
    suspend fun list(
        @RequestParam(defaultValue = "0") page: Int,
        @RequestParam(defaultValue = "20") size: Int
    ): Page<OrderResponseV1> {
        // ...
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    suspend fun create(@Valid @RequestBody req: CreateOrderRequestV1): OrderResponseV1 {
        // ...
    }
}

// v2 - إضافة حقول جديدة بدون كسر v1
@RestController
@RequestMapping("/api/v2/orders")
class OrderControllerV2(private val service: OrderApplicationService) {

    @GetMapping
    suspend fun list(
        @RequestParam(defaultValue = "0") page: Int,
        @RequestParam(defaultValue = "20") size: Int,
        @RequestParam(required = false) status: OrderStatus?
    ): Page<OrderResponseV2> {  // V2 له حقول إضافية
        // ...
    }
}

**الإخراج:

TEXT 📖 للعرض فقط

▶ مثال: Validation و Error handling موحد

KOTLIN
// تعريف أخطاء المجال
sealed class DomainError(message: String) : RuntimeException(message) {
    class NotFound(id: Any) : DomainError("Resource $id not found")
    class InvalidOperation(msg: String) : DomainError(msg)
    class ValidationFailed(val violations: List<String>) : DomainError("Validation failed")
    class Unauthorized(reason: String) : DomainError("Unauthorized: $reason")
}

// معالجة موحدة
@ControllerAdvice
class GlobalErrorHandler {

    @ExceptionHandler(DomainError.NotFound::class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    fun handleNotFound(ex: DomainError.NotFound) = ErrorResponse(
        code = "NOT_FOUND",
        message = ex.message ?: "Resource not found",
        timestamp = Instant.now()
    )

    @ExceptionHandler(DomainError.InvalidOperation::class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    fun handleInvalid(ex: DomainError.InvalidOperation) = ErrorResponse(
        code = "INVALID_OPERATION",
        message = ex.message ?: "Invalid operation"
    )

    @ExceptionHandler(DomainError.ValidationFailed::class)
    @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
    fun handleValidation(ex: DomainError.ValidationFailed) = ErrorResponse(
        code = "VALIDATION_FAILED",
        message = ex.message ?: "Validation failed",
        details = ex.violations
    )

    @ExceptionHandler(DomainError.Unauthorized::class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    fun handleUnauthorized(ex: DomainError.Unauthorized) = ErrorResponse(
        code = "UNAUTHORIZED",
        message = ex.message ?: "Unauthorized"
    )

    @ExceptionHandler(Exception::class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    fun handleGeneric(ex: Exception) = ErrorResponse(
        code = "INTERNAL_ERROR",
        message = ex.message ?: "An unexpected error occurred"
    )
}

data class ErrorResponse(
    val code: String,
    val message: String,
    val timestamp: Instant = Instant.now(),
    val details: List<String>? = null
)

**الإخراج:

TEXT 📖 للعرض فقط
404 Not Found: {"code":"NOT_FOUND","message":"Order ORD-999 not found","timestamp":"..."}
422 Unprocessable: {"code":"VALIDATION_FAILED","message":"...","details":["amount must be > 0"]}

❓ أسئلة شائعة

س تصميم قاعدة البيانات أولاً أم واجهة برمجة التطبيقات أولاً؟
ج صمم نموذج النطاق أولاً (نهج DDD). عندها تكون كل من API وقاعدة البيانات إسقاطات لنموذج النطاق. نموذج النطاق هو الأساس؛ وAPI والمخطط هوما الطرفيات.
س ما الفرق بين مصدر الأحداث و CRUD؟
ج CRUD يخزن الحالة الحالية فقط. مصدر الأحداث يخزن جميع أحداث تغيير الحالة. مصدر الأحداث يدعم التدقيق وإعادة التشغيل والسفر عبر الزمن، لكن تعقيده أعلى. ابدأ بـ CRUD؛ أضف الأحداث عند الحاجة.
س R2DBC أم JPA - كيف تختار؟
ج مشروع جديد + WebFlux + كوروتينات ← R2DBC (غير حظرية). مشروع JPA حالي أو فريق غير ملم بالتفاعلية ← JPA + امتدادات الكوروتينات.
س ما حجم الخدمة المصغرة المناسب؟
ج خدمة مصغرة واحدة لكل سياق محدد. OrderProcessor هو سياق "معالجة الطلبات" — لا تضع الدفع والمخزون فيه أيضًا.
س كيف تتعامل مع إصدارات API؟
ج إصدارات مسار URL (/api/v1/) هي الأبسط والأكثر بديهية. الإصدارات القائمة على الرأس أكثر توافقًا مع REST لكن أكثر تعقيدًا. يُوصى بإصدارات URL.
س كيف تضمن أن التصميم المعماري قابل للتنفيذ؟
ج كل قرار معماري يجب أن يكون له كود POC (إثبات المفهوم). بنية على الورق + نموذج أولي قابل للتشغيل = تصميم قابل للتنفيذ.

📖 ملخص


📝 تمارين

  1. مبتدئ (⭐): استخدم sealed class لتصميم آلة حالة لنظام إدارة مهام بسيط (Todo ← InProgress ← Done / Cancelled). تلميح: sealed class TaskStatus
  2. متوسط (⭐⭐): صمم عقد API RESTful كامل مع فئات data للطلب/الاستجابة لـ 5 نقاط نهاية. تلميح: CreateOrderRequest / OrderResponse
  3. متقدم (⭐⭐⭐): صمم وثيقة بنية معمارية طبقية كاملة لنظام تجارة إلكترونية، بما في ذلك نموذج النطاق، آلة الحالة، API، مخطط قاعدة البيانات، ومجموعة التقنيات. تلميح: راجع جميع أقسام هذا الدرس

← السابق | التالي →

Web-Tutorial.com

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

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

100%