حاوية حقن التبعية (Dependency Injection) للغة Go (Golang): Wire وDig وFx
في عالم التطوير بلغة Go (Golang)، يُعتبر حقن التبعية (DI) ليس مجرد نمط شائع، بل أداة قوية لإنشاء كود نظيف وقابل للاختبار والصيانة. على عكس العديد من اللغات الموجهة للكائنات، لا تحتوي Go على دعم مدمج لحاويات DI. ومع ذلك، أنشأ المجتمع العديد من المكتبات الأنيقة التي تحل هذه المشكلة. في PythonLib، سنستعرض ثلاثًا من أشهر الأدوات: Wire من Google، وDig من Uber، وFx (أيضًا من Uber).
لماذا نحتاج إلى DI في Go؟
في تطبيق Go النموذجي، غالبًا ما تكتب تهيئة يدوية للتبعيات:
func main() { db := database.NewConnection() repo := repository.NewUserRepo(db) service := service.NewUserService(repo) handler := handler.NewUserHandler(service) // ... وهكذا}هذا يعمل مع المشاريع الصغيرة، ولكن عندما يكبر التطبيق، تظهر المشاكل:
- الارتباطات الصلبة: تغيير مُنشئ واحد يكسر جميع سلاسل التهيئة.
- تكرار الكود: في الاختبارات، يجب إعادة كتابة جميع الارتباطات اليدوية.
- صعوبة إعادة الهيكلة: أي تغيير في التبعيات يتطلب المرور يدويًا على جميع أماكن إنشاء الكائنات.
تعمل حاوية DI على أتمتة هذه العملية، وتتولى إدارة دورة حياة الكائنات وارتباطاتها.
1. Wire من Google — توليد الكود بدون انعكاس (Reflection)
Wire ليست مجرد مكتبة، بل أداة لتوليد الكود. تقوم بتحليل المُنشئات الخاصة بك وتوليد كود التهيئة في مرحلة التجميع. هذا هو الفرق الرئيسي: لا تستخدم Wire reflect (الانعكاس)، مما يجعلها مثالية للتطبيقات عالية التحميل حيث تكون الأداء مهمًا.
التثبيت ومثال
go install github.com/google/wire/cmd/wire@latestمثال على الهيكل (ملف wire.go):
// +build wireinject
package main
import ( "github.com/google/wire")
func InitializeUserHandler() (*UserHandler, error) { wire.Build( NewDatabase, NewUserRepository, NewUserService, NewUserHandler, ) return nil, nil}بعد تشغيل wire، يتم إنشاء ملف wire_gen.go مع كود التهيئة الجاهز. تحصل على:
- أمان الأنواع: يتم اكتشاف الأخطاء في مرحلة التجميع.
- تكلفة صفرية في وقت التشغيل: لا يوجد انعكاس، ولا تأخير.
- الشفافية: الكود المُنشأ سهل القراءة والتصحيح.
2. Dig من Uber — حاوية ديناميكية مع الانعكاس (Reflection)
Dig هي حاوية DI كاملة تعمل في وقت التشغيل. تستخدم الانعكاس لتحليل الأنواع وبناء رسم بياني للتبعيات تلقائيًا. Dig مثالية للنماذج الأولية والتطبيقات حيث تكون المرونة أكثر أهمية من الأداء المطلق.
مثال على استخدام Dig
package main
import ( "go.uber.org/dig")
func main() { container := dig.New()
// تقديم المُنشئات container.Provide(NewDatabase) container.Provide(NewUserRepository) container.Provide(NewUserService) container.Provide(NewUserHandler)
// تنفيذ دالة تستخدم التبعيات container.Invoke(func(handler *UserHandler) { handler.HandleRequest() })}الإمكانيات الرئيسية لـ Dig:
- الحل التلقائي للتبعيات: الحاوية تجد الأنواع المطلوبة بنفسها.
- دعم الحالات المسماة: يمكن تسجيل عدة تطبيقات لواجهة واحدة.
- المزينات (Decorators)