مهدی خانزادی
۲۹ تیر ۱۴۰۵
در این قسمت به معرفی سرویس کانتینر و سرویس پروایدر برای مدیریت وابستگیها (Dependency Management) و مدیریت چرخه عمر سرویسها (Service Lifecycle) میپردازیم.
یکی از الگوهای مهم در طراحی نرمافزارهای بزرگ، مدیریت وابستگیها (Dependency Management) و مدیریت چرخه عمر سرویسها (Service Lifecycle) است. در اکوسیستم Danceable این مسئولیت بر عهده دو پکیج container و provider قرار گرفته است که در کنار یکدیگر ساختاری ساده، منعطف و قابل توسعه برای مدیریت سرویسهای برنامه فراهم میکنند.
سرویس کانتینر (Service Container) یک الگوی طراحی است که وظیفه مدیریت وابستگیهای (Dependencies) برنامه را بر عهده دارد. به زبان ساده، به جای اینکه هر بخش از برنامه خودش اشیاء موردنیازش را بسازد، از کانتینر درخواست میکند تا آنها را در اختیارش قرار دهد!
فرض کنید یک UserService برای کار کردن به UserRepository نیاز دارد و UserRepository نیز به Database وابسته است. بدون استفاده از Service Container، ممکن است کدی شبیه زیر داشته باشید:
در پروژههای کوچک این روش مشکلی ایجاد نمیکند اما هرچه تعداد اشیا بیشتر شود, مدیریت و ساختن وابستگی ها پیچیدهتر خواهد شد.
با استفاده از Service Container, فقط نحوه ساخت هر قسمت را یکبار ثبت میکنیم:
سپس هر زمان که به UserService نیاز داشته باشیم, تنها کافی است که آن را از کانتینر دریافت کنیم:
کانتینر خودش مراحل زیر را انجام میدهد:
بنابراین تنها هر قسمت را درون Container قرار میدهیم و سپس Container خودش تشخیص میدهد وابستگی ها را چطور فراهم کند.
این دو مفهوم معمولاً با هم اشتباه گرفته میشوند.
Dependency Injection (DI) یک الگو (Pattern) است که میگوید وابستگیها نباید داخل کلاس یا سرویس ساخته شوند، بلکه باید از بیرون به آن تزریق شوند.
پکیج danceable/container همین نقش را بر عهده دارد؛ یعنی مرکزی برای ثبت، ساخت و مدیریت تمام وابستگیهای برنامه است.
Service Provider یا سرویس پروایدر مفهومی است که برای سازماندهی فرآیند راهاندازی (Bootstrap) و خاموش شدن (Shutdown) برنامه استفاده میشود.
اگر Service Container را انبار سرویسهای برنامه در نظر بگیریم، Service Provider مسئول پر کردن این انبار و مدیریت چرخه عمر آن سرویسها است.
به بیان ساده:
فرض کنید برنامه شما از سرویسهای زیر استفاده میکند:
اگر هیچ Service Provider نداشته باشید، تمام کدهای مربوط به ثبت این سرویسها و راهاندازی آنها در فایل main.go قرار میگیرد:
با بزرگتر شدن پروژه، فایل main.go به محلی برای تمام عملیات راهاندازی تبدیل میشود و نگهداری آن دشوار خواهد شد. Service Provider این مسئولیت را از main.go جدا میکند.
یک Service Provider معمولاً سه مسئولیت اصلی دارد:
هر Service Provider در طول اجرای برنامه سه مرحله اصلی را طی میکند:
اولین مرحله، Register است. در این مرحله، Provider سرویسهای خود را در Container ثبت میکند. معمولاً در این مرحله هیچ منبع خارجی (مانند اتصال به پایگاه داده یا Redis) ایجاد نمیشود و تنها نحوه ساخت سرویسها (Factoryها) در Container ذخیره میشود. کانتینر پس از پایان مرحله Register، میداند که چگونه هر سرویس را در صورت نیاز ایجاد کند.
فرض کنید پروژه شما شامل چندین بخش مستقل است:
هر Provider فقط مسئول همان بخش است.
برای مثال:
DatabaseProvider فقط Database را مدیریت میکند.RedisProvider فقط Redis را مدیریت میکند.LoggerProvider فقط Logger را مدیریت میکند.در نتیجه مسئولیتها از هم جدا میشوند و اضافه کردن یا حذف یک سرویس، تأثیر کمی روی سایر بخشهای برنامه دارد.
پکیج danceable/provider نقش Service Provider را بر عهده دارد؛ یعنی مسئولیت مدیریت چرخه عمر سرویسهای برنامه را بر عهده گرفته و مشخص میکند که چه سرویسهایی باید در زمان اجرای برنامه درون Container ثبت شوند، چه عملیاتهایی برای آمادهسازی و راهاندازی سرویسها انجام شود و در هنگام پایان اجرای برنامه چگونه منابع مورد استفاده آزاد و پاکسازی شوند.