Claude Code لبناء هيكلية API بلغة Go: عرّف نمط معالج-خدمة-خطأ واحد، ثم ولِّد نقاط نهاية جديدة تحافظ على الاتساق عبر API الخاص بك.

عادةً ما تبدأ واجهات برمجة تطبيقات Go نظيفة: بضع نقاط نهاية، شخص أو اثنان، وكل شيء في رأس الجميع. ثم ينمو الـ API، تُنفّذ الميزات تحت ضغط، وتدخل اختلافات صغيرة تدريجياً. كل واحدة تبدو ضئيلة، لكن مجتمعة تبطئ أي تغيير مستقبلي.
مثال شائع: معالج واحد يفكك JSON إلى بنية ويعيد 400 مع رسالة مفيدة، وآخر يُرجع 422 بشكل مختلف، وثالث يسجل الأخطاء بصيغة مختلفة. لا يكسر أي من ذلك التجميع. لكنه يخلق قرارية مستمرة وإعادة كتابة صغيرة في كل مرة تضيف فيها شيئاً.
تشعر بالفوضى في أماكن مثل:
CreateUser, AddUser, RegisterUser) ما يجعل البحث أصعب.المقصود بـ"الهيكلة" هنا قالب قابل للتكرار للعمل الجديد: أين يذهب الكود، ماذا يفعل كل طبقة، وكيف تبدو الاستجابات. الفكرة أقل عن توليد الكثير من الكود وأكثر عن تثبيت شكل متسق.
أدوات مثل Claude يمكن أن تساعد في توليد نقاط نهاية بسرعة، لكن فائدتها تبقى عندما تُعامل النمط كقاعدة. أنت تعرف القواعد، تراجع كل فرق، وتشغل الاختبارات. النموذج يملأ الأجزاء القياسية؛ لا يملك حرية إعادة تعريف معماريتك.
تبقى واجهة Go سهلة النمو عندما يتبع كل طلب نفس المسار. قبل أن تبدأ بتوليد نقاط نهاية، اختر تقسيم طبقات واحد والتزم به.
مهمة المعالج HTTP فقط: قراءة الطلب، استدعاء الخدمة، وكتابة الاستجابة. لا ينبغي أن يحتوي على قواعد عمل، SQL، أو منطق "هذه الحالة الخاصة فقط".
تملك الخدمة حالة الاستخدام: قواعد العمل، القرارات، والتنسيق عبر المستودعات أو النداءات الخارجية. لا ينبغي أن تعرف عن اهتمامات HTTP مثل رموز الحالة، الرؤوس، أو كيفية عرض الأخطاء.
يملك الوصول إلى البيانات (المستودع/المخزن) تفاصيل الاستمرارية. يترجم نية الخدمة إلى SQL/استعلامات/معاملات. لا ينبغي أن يفرض قواعد عمل ما عدا تكامل البيانات الأساسي، ولا يشكل استجابات الـ API.
قائمة فحص للفصل تبقى عملية:
اختر قاعدة واحدة ولا تلتف عنها.
نهج بسيط:
مثال: المعالج يتأكد أن email موجود ويشبه بريدًا إلكترونياً. الخدمة تتأكد أن البريد مسموح به وليس مستخدماً بالفعل.
قرّر مبكراً ما إذا كانت الخدمات تُعيد أنواع المجال أم DTOs.
الافتراض النظيف الافتراضي: المعالجات تستخدم DTOs للطلب/الاستجابة، الخدمات تستخدم أنواع المجال، ويقوم المعالج بتحويل المجال إلى استجابة. هذا يحافظ على ثبات الخدمة حتى عندما يتغير عقد HTTP.
إذا بدا التحويل ثقيلاً، حافظ على الاتساق على أي حال: اجعل الخدمة تُعيد نوع مجال زائد خطأ مكلّف، وابقِ تشكيل JSON في المعالج.
إذا أردت أن تبدو النقاط النهائية المولَّدة كأنها كتبها نفس الشخص، ثَبّت شكل استجابات الأخطاء مبكراً. يعمل التوليد أفضل عندما يكون شكل الإخراج غير قابل للتفاوض: غلاف JSON واحد، خريطة رموز حالة واحدة، وقاعدة واحدة لما يتم كشفه.
ابدأ بغلاف خطأ واحد تُعيده كل نقطة نهاية عند الفشل. اجعله صغيراً ومتوقعاً:
{
"code": "validation_failed",
"message": "One or more fields are invalid.",
"details": {
"fields": {
"email": "must be a valid email address",
"age": "must be greater than 0"
}
},
"request_id": "req_01HR..."
}
استخدم code للآلات (ثابت ومتوقع) وmessage للبشر (قصير وآمن). ضع البيانات الهيكلية الاختيارية في details. بالنسبة للتحقق، خريطة details.fields بسيطة وسهلة للتوليد وسهلة لعرضها من قبل العملاء بجانب الحقول.
بعد ذلك، اكتب خريطة رموز الحالة والتزم بها. كلما قلت المناقشة لكل نقطة نهاية، كان أفضل. إذا أردت كل من 400 و422، اجعل التقسيم صريحاً:
bad_json -> 400 Bad Request (JSON معطوب)validation_failed -> 422 Unprocessable Content (JSON صحيح التركيب، حقول غير صالحة)not_found -> 404 Not Foundconflict -> 409 Conflict (مفتاح مكرر، عدم تطابق نسخة)unauthorized -> 401 Unauthorizedforbidden -> 403 Forbiddeninternal -> 500 Internal Server Errorقرر ما الذي تسجّله مقابل ما تعيده. قاعدة جيدة: العملاء يحصلون على رسالة آمنة ومعرف طلب؛ السجلات تحتوي على الخطأ الكامل والسياق الداخلي (SQL، حمولات نُقلت للأعلى، معرفات المستخدم) التي لا تريد تسريبها أبداً.
أخيراً، قيِّد request_id. اقبل رأس معرف قادم إذا وُجد (من بوابة API)، وإلا أنشئ واحداً عند الحافة (middleware). أرفقه في السياق، ضمه في السجلات، وأعده في كل استجابة خطأ.
إذا أردت بقاء التوليد متسقاً، يجب أن يكون تخطيط مجلداتك مملاً وقابلاً للتكرار. المولّدات تتبع أنماطاً يمكنها رؤيتها، لكنها تنحرف عندما تتناثر الملفات أو تتغير الأسماء حسب الميزة.
اختر قاعدة تسمية واحدة ولا تلتف عنها. اختر كلمة واحدة لكل شيء واحتفظ بها: handler, service, repo, request, response. إذا كان المسار POST /users، سمّ الملفات والأنواع حول users وcreate (لا تستخدم أحياناً register وأحياناً addUser).
تخطيط بسيط يطابق الطبقات المعتادة:
internal/
httpapi/
handlers/
users_handler.go
services/
users_service.go
data/
users_repo.go
apitypes/
users_types.go
قرّر أين تعيش الأنواع المشتركة، لأن هذا المكان عادة ما يصبح فوضوياً. قاعدة مفيدة:
internal/apitypes (تطابق JSON واحتياجات التحقق).إذا كان النوع يحتوي على وسوم JSON ومصمم للعملاء، اعتبره نوع API.
احفظ تبعيات المعالج محدودة واجعل تلك القاعدة صريحة:
اكتب وثيقة نمط قصيرة في جذر المستودع (Markdown عادي كافٍ). أدرج شجرة المجلدات، قواعد التسمية، ومجرى مثال صغير (المعالج -> الخدمة -> المستودع، مع الملف الذي ينتمي إليه كل جزء). هذا هو المرجع الدقيق الذي تلصقه في مولّدك حتى تطابق نقاط النهاية الجديدة البنية في كل مرة.
قبل توليد عشر نقاط نهاية، أنشئ نقطة نهاية واحدة تثق بها. هذه هي المعيار الذهبي: الملف الذي تشير إليه وتقول، "يجب أن يبدو الكود الجديد هكذا." يمكنك كتابته من الصفر أو إعادة صياغة واحد قائم حتى يطابق.
حافظ على المعالج نحيفاً. خطوة واحدة تساعد كثيراً: ضع واجهة بين المعالج والخدمة لكي يعتمد المعالج على عقدة، لا على بنية ملموسة.
أضف تعليقات صغيرة في نقطة النهاية المرجعية فقط حيث قد يتعثّر الكود المولَّد مستقبلاً. اشرح القرارات (لماذا 400 مقابل 422، لماذا يعيد الإنشاء 201، لماذا تُخفي الأخطاء الداخلية وراء رسالة عامة). تجنّب التعليقات التي تعيد صياغة الكود فقط.
بمجرد أن تعمل نقطة النهاية المرجعية، استخرج المساعدين حتى تقل فرص الانحراف في كل نقطة نهاية جديدة. أكثر المساعدين قابلية لإعادة الاستخدام عادة هم:
هذا مثال لما يمكن أن يبدو عليه "معالج نحيف + واجهة" عملياً:
type UserService interface {
CreateUser(ctx context.Context, in CreateUserInput) (User, error)
}
func (h *Handler) CreateUser(w http.ResponseWriter, r *http.Request) {
var in CreateUserRequest
if err := BindJSON(r, &in); err != nil {
WriteError(w, ErrBadJSON) // 400: malformed JSON
return
}
if err := Validate(in); err != nil {
WriteError(w, err) // 422: validation details
return
}
user, err := h.svc.CreateUser(r.Context(), in.ToInput())
if err != nil {
WriteError(w, err)
return
}
WriteJSON(w, http.StatusCreated, user)
}
ثبت ذلك مع عدد قليل من الاختبارات (حتى اختبار جدولي صغير لخريطة الأخطاء). يعمل التوليد أفضل عندما يكون له هدف نظيف واحد يقلده.
يبدأ الاتساق بما تلصقه وما تحظره. لنقطة نهاية جديدة، قدّم شيئين:
أدرج المعالج، طريقة الخدمة، أنواع الطلب/الاستجابة، وأي مساعدين مشترَكين تستخدمهم النقطة النهائية. ثم اذكر العقدة بعبارات واضحة:
POST /v1/widgets)كن صريحاً بشأن ما يجب أن يتطابق: التسمية، مسارات الحزم، ودوال المساعد (WriteJSON, BindJSON, WriteError, المدقّق الخاص بك).
مطالبة ضيقة تمنع التعديلات "المفيدة". على سبيل المثال:
Using the reference endpoint below and the pattern notes, generate a new endpoint.
Contract:
- Route: POST /v1/widgets
- Request: {"name": string, "color": string}
- Response: {"id": string, "name": string, "color": string, "createdAt": string}
- Errors: invalid JSON -> 400; validation -> 422; duplicate name -> 409; unexpected -> 500
Output ONLY these files:
1) internal/http/handlers/widgets_create.go
2) internal/service/widgets.go (add method only)
3) internal/types/widgets.go (add types only)
Do not change: router setup, existing error format, existing helpers, or unrelated files.
Must use: package paths and helper functions exactly as in the reference.
إذا استخدمت اختبارات، اطلبها صراحة (وسمِّ ملف الاختبار). وإلا فقد يتخطى النموذج إضافتها أو يخترع إعداد اختبار غير متوافق.
قم بفحص سريع للفروقات بعد التوليد. إذا عدّل المساعدون المشتركون، أو تسجيل المسارات، أو شكل خطأك المعياري، ارفض المخرجات وأعد صياغة قواعد "لا تغير" بشكل أكثر صرامة.
المخرجات متسقة بقدر ما هو مدخلها. أسرع طريقة لتجنب كود "قريب من الصحيح" هي إعادة استخدام مطالبة واحدة في كل مرة، مع لقطة سياق صغيرة مأخوذة من مستودعك.
انسخ، ألصق، واملأ الحقول:
You are editing an existing Go HTTP API.
CONTEXT
- Folder tree (only the relevant parts):
<paste a small tree: internal/http, internal/service, internal/repo, etc>
- Key types and patterns:
- Handler signature style: <example>
- Service interface style: <example>
- Request/response DTOs live in: <package>
- Standard error response JSON:
{
"error": {
"code": "invalid_argument",
"message": "...",
"details": {"field": "reason"}
}
}
- Status code map:
invalid_json -> 400
invalid_argument -> 422
not_found -> 404
conflict -> 409
internal -> 500
TASK
Add a new endpoint: <METHOD> <PATH>
- Handler name: <Name>
- Service method: <Name>
- Request JSON example:
{"name":"Acme"}
- Success response JSON example:
{"id":"123","name":"Acme"}
CONSTRAINTS
- No new dependencies.
- Keep functions small and single-purpose.
- Match existing naming, folder layout, and error style exactly.
- Do not refactor unrelated files.
ACCEPTANCE CHECKS
- Code builds.
- Existing tests pass (add tests only if the repo already uses them for handlers/services).
- Run gofmt on changed files.
FINAL INSTRUCTION
Before writing code, list any assumptions you must make. If an assumption is risky, ask a short question instead.
ينجح هذا لأنّه يفرض ثلاث أشياء: كتلة سياق (ما هو موجود)، كتلة قيود (ما لا يجب فعله)، وأمثلة JSON ملموسة (حتى لا تنجرف الأشكال). التعليم النهائي هو شبكة أمان: إذا كان النموذج غير متأكد، يجب أن يسأل قبل أن يلتزم بتغيير نمطي.
لنفترض أنك تريد إضافة نقطة نهاية "Create project". الهدف بسيط: قبول اسم، فرض بعض القواعد، تخزينه، وإرجاع معرف جديد. الجزء المعقّد هو الحفاظ على نفس تقسيم المعالج-الخدمة-المستودع ونفس شكل خطأ JSON الذي تستخدمه بالفعل.
تدفق متسق يبدو كالتالي:
هذا هو الطلب الذي يقبله المعالج:
{ "name": "Roadmap", "owner_id": "u_123" }
عند النجاح، أعد 201 Created. يجب أن يأتي المعرف من مصدر واحد في كل مرة. على سبيل المثال، اجعل Postgres يُولّد المعرف ويترك المستودع إرجاعه:
{ "id": "p_456", "name": "Roadmap", "owner_id": "u_123", "created_at": "2026-01-09T12:34:56Z" }
مساران فاشلان واقعيان:
إذا فشل التحقق (اسم مفقود أو قصير جداً)، أعد خطأ على مستوى الحقول باستخدام الشكل المعياري وحالة الاستجابة التي اخترتها:
{ "error": { "code": "VALIDATION_ERROR", "message": "Invalid request", "details": { "name": "must be at least 3 characters" } } }
إذا كان الاسم يجب أن يكون فريداً لكل مالك ووجدت الخدمة مشروعاً موجوداً، أعد 409 Conflict:
{ "error": { "code": "PROJECT_NAME_TAKEN", "message": "Project name already exists", "details": { "name": "Roadmap" } } }
قرار واحد يحافظ على النمط نظيفاً: المعالج يتحقق "هل هذا الطلب مُشكَّل بشكل صحيح؟" بينما تملك الخدمة "هل هذا مسموح؟" هذا الفصل يجعل النقاط النهائية المولّدة متوقعة.
أسرع طريق لفقدان الاتساق هو ترك المولّد يتصرّف بحرية.
انحراف شائع هو شكل خطأ جديد. نقطة نهاية تعيد {error: "..."}، أخرى تعيد {message: "..."}، وثالثة تضيف كائنًا متداخلاً. أصلح هذا بالحفاظ على غلاف خطأ واحد وخريطة رموز حالة واحدة في مكان واحد، ثم إجبار النقاط الجديدة على إعادة استخدامها عبر مسار استيراد واسم دالة. إذا اقترح المولّد حقلاً جديداً، اعتبره طلب تغيير API، لا ميزة مُريحة.
انحراف آخر هو تضخّم المعالج. يبدأ صغيراً: تحقق، ثم تحقق أذونات، ثم استعلام DB، ثم تفرعات منطق العمل. قريباً، كل معالج يبدو مختلفاً. احفظ قاعدة واحدة: المعالجات تترجم HTTP إلى مدخلات ومخرجات منمطة؛ الخدمات تملك القرارات؛ الوصول إلى البيانات يملك الاستعلامات.
التسمية المتباينة تضيف أيضاً عبئاً. إذا استخدمت نقطة نهاية واحدة CreateUserRequest وأخرى NewUserPayload, ستضيّع وقتاً في البحث عن الأنواع وكتابة ضمادات. اختر نظام تسمية واحد وارفض الأسماء الجديدة إلا لسبب قوي.
لا تُعد أبداً أخطاء قاعدة البيانات الخام للعملاء. بخلاف تسريب التفاصيل، يخلق رسائل غير متسقة ورموز حالة متداخلة. لف الأخطاء الداخلية، سجّل السبب، وأعد رمز خطأ عام ثابت.
تجنّب إضافة مكتبات جديدة "من أجل الراحة". كل مدقق إضافي، مساعد راوتر، أو حزمة أخطاء تصبح نمطاً آخر يجب مطابقته.
حواجز تمنع معظم الأضرار:
إذا لم تستطع عمل diff بين نقطتين ورؤية نفس الشكل (الاستيرادات، التدفق، التعامل مع الأخطاء)، شدّد المطالبة وأعد التوليد قبل الدمج.
قبل دمج أي شيء مولَّد، افحص البنية أولاً. إذا كان الشكل صحيحاً، تصبح أخطاء المنطق أسهل الاكتشاف.
فحوص البنية:
request_id.فحوص السلوك:
عامل نمطك كعقد مشترك، لا تفضيل شخصي. احتفظ بدليل "كيف نبني النقاط النهائية" بجانب الكود، وحافظ على نقطة نهاية مرجعية واحدة تُظهر النهج كاملًا.
وسع التوليد على دفعات صغيرة. ولّد 2 إلى 3 نقاط نهاية تصل حواف مختلفة (قراءة بسيطة، إنشاء مع تحقق، تحديث مع حالة غير موجود). ثم توقف وراجِع. إذا استمرت المراجعات في إيجاد نفس انحراف النمط، حدِّث وثيقة الأساس ونقطة النهاية المرجعية قبل توليد المزيد.
حلقة يمكنك تكرارها:
إذا أردت حلقة بناء-مراجعة أكثر إحكاماً، منصة فكاهة ترميزية مثل Koder.ai يمكن أن تساعدك في توليد وتكرار بسرعة في سير عمل مدفوع بالدردشة، ثم تصدير الشيفرة عند تطابقها مع المعيار. الأداة أقل أهمية من القاعدة: يجب أن يبقى الأساس هو المسيطر.
قفل قالب قابل للتكرار مبكراً: فصل طبقات متسق (handler → service → data access)، غلاف أخطاء موحد، وخريطة رموز حالة. ثم استخدم "نقطة النهاية المرجعية" الواحدة كمثال يجب أن ينسخ إليه كل كود جديد.
حافظ على المعالجات HTTP-only:
إذا وجدت SQL، أو فحوصات صلاحية، أو تفرعات عمل في المعالج، انقلها إلى الخدمة.
ضع قواعد العمل والقرارات في الخدمة:
يجب أن تُعيد الخدمة نتائج النطاق وأخطاء مكلّفة—بدون رموز حالة HTTP أو تشكيل JSON.
عزل اهتمامات التخزين:
تجنّب ترميز صيغ استجابة API أو فرض قواعد عمل في المستودع بخلاف تكامل البيانات الأساسي.
افتراض بسيط افتراضي:
مثال: المعالج يتأكد من وجود email ومظهره كبريد؛ الخدمة تتأكد أنه مسموح وغير مستخدم بالفعل.
استخدم غلاف خطأ واحد موحد في كل مكان وحافظ عليه ثابتا. شكل عملي:
code للآلات (ثابت)message للبشر (قصير وآمن)details للبيانات المهيكلة (مثل أخطاء الحقول)request_id للتتبعهذا يتجنب حالات خاصة في جانب العميل ويجعل النقاط النهائية المولّدة متوقعة.
دوّن خريطة رموز الحالة واتبعها دائماً. تقسيم شائع:
أعد أخطاء عامة وآمنة، وسجّل السبب الكامل داخلياً.
code ثابت، message قصير، ومعرف الطلب request_idهذا يمنع تسريب التفاصيل ويجنب اختلاف رسائل الأخطاء عبر النقاط النهائية.
إنشاء نقطة نهاية "ذهبية" واحدة تثق بها واجبر النقاط الجديدة على محاكاتها:
BindJSON, WriteJSON, WriteError, إلخ)ثم أضف بعض الاختبارات الصغيرة (حتى اختبارات جدولية لخريطة الأخطاء) لتثبيت النمط.
زوّد النموذج بمحتوى صارم وقيود واضحة:
بعد التوليد، ارفض الاختلافات التي "تحسّن" البنية بدلاً من اتباع الأساس.
400 للـ JSON المعطوب (bad_json)422 لفشل التحقق (validation_failed)404 للموارد المفقودة (not_found)409 للصراعات (مفردات/تعارض الإصدارات)500 للأخطاء غير المتوقعةالمهم هو الاتساق: لا نقاش لكل نقطة نهاية.