هندسة المشاريع المعقدة مع الذكاء الاصطناعي
تطوير ميزات 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 المحددة وكيفية تنسيق الذكاء الاصطناعي عبر حزم متعددة. :::