هندسة المشاريع المعقدة مع الذكاء الاصطناعي

تطوير ميزات Full-Stack

5 دقيقة للقراءة

نهج الشريحة العمودية

بدلاً من البناء طبقة بطبقة، نهج الشريحة العمودية يبني ميزة كاملة من قاعدة البيانات إلى UI. مساعدو الذكاء الاصطناعي يتفوقون في هذا لأنهم يمكنهم الحفاظ على السياق عبر جميع الطبقات.

دراسة حالة: بناء ميزة التعليقات

لنبنِ نظام تعليقات كامل لمنصة مدونة.

المرحلة 1: التخطيط مع Claude Code

claude "خطط لميزة تعليقات لمدونة Next.js الخاصة بنا.
المتطلبات:
- تعليقات متداخلة (مستويان كحد أقصى)
- تحديثات الوقت الحقيقي
- دعم Markdown
- صور الملف الشخصي للكتّاب
- تعديل/حذف التعليقات الخاصة

حلل أنماطنا الموجودة وأنشئ:
1. مخطط قاعدة البيانات
2. نقاط نهاية API
3. هيكل المكونات
4. نهج إدارة الحالة"

المرحلة 2: طبقة قاعدة البيانات

Claude Code يولد المخطط:

// إضافات prisma/schema.prisma

model Comment {
  id        String   @id @default(cuid())
  content   String   @db.Text
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // العلاقات
  author    User     @relation(fields: [authorId], references: [id])
  authorId  String
  post      Post     @relation(fields: [postId], references: [id])
  postId    String
  parent    Comment? @relation("CommentReplies", fields: [parentId], references: [id])
  parentId  String?
  replies   Comment[] @relation("CommentReplies")

  @@index([postId])
  @@index([authorId])
  @@index([parentId])
}

تنفيذ الهجرة:

claude "شغّل هجرة Prisma لنموذج Comment
وولّد العميل"

المرحلة 3: طبقة API

claude "أنشئ مسارات API للتعليقات متبعًا أنماطنا
الموجودة في src/app/api/posts/. ضمّن:
- GET /api/posts/[postId]/comments (مع التصفح)
- POST /api/posts/[postId]/comments
- PATCH /api/comments/[id]
- DELETE /api/comments/[id]

استخدم middleware المصادقة ومعالجة الأخطاء الموجودة."

هيكل المسار المُولَّد:

// src/app/api/posts/[postId]/comments/route.ts

import { NextRequest } from 'next/server';
import { getServerSession } from 'next-auth';
import { prisma } from '@/lib/db';
import { commentSchema } from '@/lib/validations/comment';

export async function GET(
  request: NextRequest,
  { params }: { params: { postId: string } }
) {
  const { searchParams } = new URL(request.url);
  const page = parseInt(searchParams.get('page') ?? '1');
  const limit = parseInt(searchParams.get('limit') ?? '20');

  const comments = await prisma.comment.findMany({
    where: {
      postId: params.postId,
      parentId: null, // التعليقات الرئيسية فقط
    },
    include: {
      author: { select: { id: true, name: true, image: true } },
      replies: {
        include: {
          author: { select: { id: true, name: true, image: true } },
        },
        orderBy: { createdAt: 'asc' },
      },
    },
    orderBy: { createdAt: 'desc' },
    skip: (page - 1) * limit,
    take: limit,
  });

  return Response.json({ comments, page, limit });
}

export async function POST(
  request: NextRequest,
  { params }: { params: { postId: string } }
) {
  const session = await getServerSession();
  if (!session?.user) {
    return Response.json({ error: 'غير مصرح' }, { status: 401 });
  }

  const body = await request.json();
  const validated = commentSchema.parse(body);

  const comment = await prisma.comment.create({
    data: {
      content: validated.content,
      postId: params.postId,
      authorId: session.user.id,
      parentId: validated.parentId,
    },
    include: {
      author: { select: { id: true, name: true, image: true } },
    },
  });

  return Response.json(comment, { status: 201 });
}

المرحلة 4: مكونات الواجهة الأمامية

انتقل إلى Cursor لتطوير المكونات الفوري:

في Cursor: "أنشئ مكون CommentSection الذي:
- يجلب التعليقات مع React Query
- يعرض التعليقات المتداخلة مع CommentCard
- لديه CommentForm للتعليقات الجديدة
- يدعم التحديثات التفاؤلية
- يستخدم مكونات Avatar و Button الموجودة"

هيكل المكون:

// src/components/comments/CommentSection.tsx

'use client';

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { CommentCard } from './CommentCard';
import { CommentForm } from './CommentForm';
import { Skeleton } from '@/components/ui/Skeleton';

interface CommentSectionProps {
  postId: string;
}

export function CommentSection({ postId }: CommentSectionProps) {
  const queryClient = useQueryClient();

  const { data, isLoading } = useQuery({
    queryKey: ['comments', postId],
    queryFn: () => fetchComments(postId),
  });

  const addComment = useMutation({
    mutationFn: (content: string) => postComment(postId, content),
    onMutate: async (content) => {
      // تحديث تفاؤلي
      await queryClient.cancelQueries(['comments', postId]);
      const previous = queryClient.getQueryData(['comments', postId]);
      queryClient.setQueryData(['comments', postId], (old: any) => ({
        ...old,
        comments: [createOptimisticComment(content), ...old.comments],
      }));
      return { previous };
    },
    onError: (err, content, context) => {
      queryClient.setQueryData(['comments', postId], context?.previous);
    },
    onSettled: () => {
      queryClient.invalidateQueries(['comments', postId]);
    },
  });

  if (isLoading) return <CommentsSkeleton />;

  return (
    <section className="space-y-6">
      <h2 className="text-xl font-semibold">
        التعليقات ({data?.comments.length ?? 0})
      </h2>

      <CommentForm onSubmit={(content) => addComment.mutate(content)} />

      <div className="space-y-4">
        {data?.comments.map((comment) => (
          <CommentCard
            key={comment.id}
            comment={comment}
            postId={postId}
          />
        ))}
      </div>
    </section>
  );
}

المرحلة 5: تحديثات الوقت الحقيقي

claude "أضف اشتراكات Supabase للوقت الحقيقي إلى CommentSection.
عندما يُضاف تعليق جديد بواسطة مستخدم آخر، يجب أن
يظهر تلقائيًا بدون تحديث."
// أضف إلى CommentSection.tsx

useEffect(() => {
  const channel = supabase
    .channel(`comments:${postId}`)
    .on(
      'postgres_changes',
      {
        event: 'INSERT',
        schema: 'public',
        table: 'Comment',
        filter: `postId=eq.${postId}`,
      },
      (payload) => {
        queryClient.invalidateQueries(['comments', postId]);
      }
    )
    .subscribe();

  return () => {
    supabase.removeChannel(channel);
  };
}, [postId, queryClient]);

المرحلة 6: اختبار التكامل

claude "اكتب اختبارات تكامل لميزة التعليقات:
1. اختبارات مسار API مع MSW
2. اختبارات المكونات مع Testing Library
3. اختبار E2E مع Playwright للتدفق الكامل"

نصائح التنسيق لميزات Full-Stack

1. مشاركة الأنواع

ولّد الأنواع من مخطط قاعدة بياناتك:

claude "أنشئ أنواع TypeScript لـ Comment يمكن
مشاركتها بين API والواجهة الأمامية. ضعها في src/types/."

2. مشاركة التحقق

// src/lib/validations/comment.ts
import { z } from 'zod';

export const commentSchema = z.object({
  content: z.string().min(1).max(10000),
  parentId: z.string().optional(),
});

// يُستخدم في كل من مسار API ونموذج الواجهة الأمامية
export type CommentInput = z.infer<typeof commentSchema>;

3. تناسق معالجة الأخطاء

claude "تأكد أن معالجة أخطاء التعليقات تتبع النمط
المنشأ في src/lib/errors.ts. ارسم أخطاء قاعدة البيانات
إلى رموز حالة HTTP المناسبة."

التحسين التكراري

بعد التنفيذ الأولي:

claude "راجع ميزة التعليقات التي بنيناها للتو:
1. تحقق من مشاكل استعلام N+1
2. تحقق من حدود الأخطاء المناسبة
3. تأكد من الامتثال لإمكانية الوصول
4. حدد حالات الحافة المفقودة"

الذكاء الاصطناعي يحدد التحسينات:

## نتائج المراجعة

1. **استعلام N+1**: مُصلح - يستخدم includes بشكل صحيح
2. **حد الأخطاء**: مفقود - أضف ErrorBoundary حول CommentSection
3. **إمكانية الوصول**: أضف aria-labels لأزرار الإجراءات
4. **حالات الحافة**:
   - معالجة التعليق الأصلي المحذوف (الردود اليتيمة)
   - معالجة الكاتب المحذوف (أظهر "[محذوف]")
   - تحديد معدل على نقطة نهاية POST

الدرس التالي

سنستكشف أنماط monorepo المحددة وكيفية تنسيق الذكاء الاصطناعي عبر حزم متعددة. :::

اختبار

الوحدة 2: هندسة المشاريع المعقدة مع الذكاء الاصطناعي

خذ الاختبار