اولویت بارگذاری و ساختار لایهای
در پروژههای بزرگ، ماژولها به صورت تصادفی یا بدون نظم نباید بارگذاری شوند.
برای جلوگیری از تداخل، وابستگیهای پیچیده و کدهای تکراری، بهتر است از یک ساختار لایهای (Layered Module Loading) استفاده کنیم.
Base Modules (ماژولهای پایه)
- ماژولهایی که شامل کد مشترک و ابزارهای عمومی هستند.
- این ماژولها به سایر ماژولها وابسته نیستند، ولی تقریباً همهی ماژولها به آنها نیاز دارند.
- مثالها:
Core→ توابع کمکی، Middlewareهای عمومی، Exception HandlingUser→ مدل کاربر، احراز هویت پایه (Authentication)Notification→ سیستم اعلانهاFileManager→ مدیریت آپلود و ذخیرهسازی فایلها
اگر چندین ماژول نیازمند یک قابلیت مشترک باشند (مثلاً آپلود فایل یا مدیریت کاربر)، آن قابلیت باید در یک Base Module جداگانه قرار گیرد و ماژولهای دیگر از آن استفاده کنند.
این کار جلوی تکرار (Duplication) و وابستگیهای حلقهای (Circular Dependency) را میگیرد.
Feature Modules (ماژولهای قابلیتها)
- ماژولهایی که یک Business Feature خاص را پیادهسازی میکنند.
- معمولاً وابسته به یک یا چند Base Module هستند.
- مثالها:
Bank→ مدیریت حسابها و تراکنشهاPayment→ مدیریت درگاههای پرداختOrder→ مدیریت سفارشها
Integration Modules (ماژولهای یکپارچهسازی)
- ماژولهایی که ارتباط بین سیستم شما و سرویسهای خارجی یا بین Feature Moduleها را مدیریت میکنند.
- مثالها:
BankGateway→ ارتباط با API بانک یPaymentBridge→ ارتباط بین Payment و Bank
این ماژولها معمولاً در لایه آخر بارگذاری میشوند.
پیشنهاد ساختار اولویت (Priority)
به جای یک مقدار عددی ساده (۰،۱،۲)، میتوانید لایهها را به صورت زیر در نظر بگیرید:
| Priority | Layer | توضیحات |
|---|---|---|
| 0 | Base Modules | همیشه اول بارگذاری میشوند (Core, User, Shared) |
| 1 | Feature Modules | قابلیتهای اصلی کسبوکار (Bank, Order, Payment) |
| 2 | Integration | ارتباطات خارجی و ماژولهای وابسته به Feature ها |
نمونه پیکربندی ماژولها
{
"name": "Bank",
"alias": "bank",
"priority": 1,
"providers": [
"Modules\\Bank\\Providers\\BankServiceProvider",
"Modules\\Bank\\Providers\\RouteServiceProvider"
]
}
{
"name": "Core",
"alias": "core",
"priority": 0,
"providers": [
"Modules\\Core\\Providers\\CoreServiceProvider"
]
}
مثال عملی: Base Module برای مدیریت فایل
فرض کنید چندین ماژول نیازمند آپلود فایل باشند:
- ماژول
User→ آپلود عکس پروفایل - ماژول
Bank→ آپلود مدارک هویتی - ماژول
Product→ آپلود تصویر محصول
به جای تکرار منطق آپلود در هر ماژول، یک Base Module به نام FileManager ایجاد میکنیم:
Modules/
└── FileManager/
├── app/Services/FileUploader.php
├── database/migrations/create_files_table.php
├── routes/api.php
└── resources/views/uploader.blade.php
سپس سایر ماژولها تنها از سرویس FileUploader استفاده میکنند:
use Modules\FileManager\Services\FileUploader;
class BankController extends Controller
{
public function uploadDocument(Request $request, FileUploader $uploader)
{
$file = $uploader->upload($request->file('document'), 'bank-docs');
return response()->json(['path' => $file->path]);
}
}
نکات مهم برای ماژولهای قابل انتقال
اگر احتمال دارد یک ماژول به پروژه دیگری منتقل شود، رعایت موارد زیر ضروری است:
استقلال کامل از پروژه اصلی
- ماژول نباید به کدهای
app/یا مسیرهای اصلی پروژه وابسته باشد. - تمام Route ها و Config ها و Controller ها و غیره باید داخل خود ماژول باشند.
Resources ایزوله
- Viewها، Translationها و غیره داخل پوشه مختص به خود در ماژول نگهداری شوند.
- برای View و Lang از namespace ماژول استفاده کنید
view('bank::index')یا('bank::validation.name')__.
وابستگیها مدیریت شده
- اگر ماژول به پکیج یا سرویس خارجی نیاز دارد، آن را در
composer.jsonماژول تعریف کنید. - این کار هنگام انتقال ماژول، نصب خودکار وابستگیها را تضمین میکند.
نکات حرفهای
- تکرار ممنوع: اگر بخشی از کد (مثل
FileUploader) در چند ماژول تکرار شده، باید آن را به یک Base Module منتقل کنید. - استقلال ماژولها: Feature Module ها نباید وابسته به یکدیگر باشند، فقط به Base Module ها.
- Shared Base Module: اگر چندین قابلیت عمومی (Helper، Traits، DTOها) در پروژه دارید، آنها را در یک ماژول مثل
SharedیاCoreقرار دهید.
چرا این ساختار حرفهایتر است؟
- توسعه تیمی سادهتر میشود (هر تیم روی یک لایه کار میکند).
- Dependency ها کنترلشده هستند و هیچ حلقهای ایجاد نمیشود.
- اگر در آینده ماژول جدید اضافه کنید، کافیست Priority و Layer را مشخص کنید.