BAB 40. DEPENDENCY INJECTION
Konsep
Dependency Injection (DI) adalah pola desain di mana dependensi sebuah objek disediakan dari luar, bukan dibuat sendiri di dalam objek tersebut. Ini memungkinkan loose coupling, testability, dan maintainability yang tinggi.
Flutter — Riverpod sebagai DI Container
Riverpod bertindak sebagai DI container di Flutter. Setiap dependensi didefinisikan sebagai provider dan dapat di-inject ke provider lainnya.
Dependency Graph (Flutter)
flowchart TD
Page["📱 Page\n(ConsumerWidget)"] --> CNP["Controller\nProvider"]
CNP --> UCP["Use Case\nProvider"]
UCP --> REPO_P["Repository\nProvider"]
REPO_P --> DS_P["Datasource\nProvider"]
DS_P --> DIO["Dio Client\nProvider"]
DIO --> BASE["Base URL\nConfig"]
style Page fill:#4f63d2,color:#fff
style DIO fill:#059669,color:#fff
Implementasi di Flutter
// ─── Network Layer ───────────────────────────────────
final dioProvider = Provider<Dio>((ref) {
final dio = Dio(BaseOptions(baseUrl: AppConfig.baseUrl));
dio.interceptors.add(AuthInterceptor(ref));
return dio;
});
// ─── Data Source ─────────────────────────────────────
final requestRemoteDatasourceProvider = Provider<RequestRemoteDatasource>((ref) {
return RequestRemoteDatasourceImpl(ref.watch(dioProvider));
});
// ─── Repository ──────────────────────────────────────
final requestRepositoryProvider = Provider<RequestRepository>((ref) {
return RequestRepositoryImpl(ref.watch(requestRemoteDatasourceProvider));
});
// ─── Use Case ────────────────────────────────────────
final createRequestUseCaseProvider = Provider<CreateRequestUseCase>((ref) {
return CreateRequestUseCase(ref.watch(requestRepositoryProvider));
});
// ─── Controller ──────────────────────────────────────
final createRequestControllerProvider = StateNotifierProvider
.autoDispose<CreateRequestController, AsyncValue<void>>((ref) {
return CreateRequestController(ref.watch(createRequestUseCaseProvider));
});
Laravel — Service Container
Laravel menggunakan Service Container bawaan sebagai DI container. Binding interface ke implementasi dilakukan di Service Provider.
Dependency Graph (Laravel)
flowchart TD
C["RequestController"] --> S["RequestService"]
S --> RI["RequestRepositoryInterface"]
RI --> R["RequestRepository"]
R --> M["Request Model\n(Eloquent)"]
M --> DB[("MySQL")]
S --> ME["MatchingEngine"]
ME --> CGI["CandidateGeneratorInterface"]
CGI --> CG["CandidateGenerator"]
style RI fill:#7c3aed,color:#fff
style CGI fill:#7c3aed,color:#fff
style ME fill:#b45309,color:#fff
Implementasi di Laravel
// app/Providers/AppServiceProvider.php
public function register(): void
{
// Repository bindings
$this->app->bind(
RequestRepositoryInterface::class,
RequestRepository::class
);
$this->app->bind(
BookingRepositoryInterface::class,
BookingRepository::class
);
$this->app->bind(
MeetingRepositoryInterface::class,
MeetingRepository::class
);
// Core service bindings
$this->app->bind(
NotificationServiceInterface::class,
FcmNotificationService::class
);
$this->app->singleton(
MatchingEngine::class,
fn ($app) => new MatchingEngine(
$app->make(CandidateGeneratorInterface::class),
$app->make(ScoringEngine::class),
$app->make(NotificationServiceInterface::class),
)
);
}
Constructor Injection di Controller
// app/Modules/Requests/Controllers/RequestController.php
class RequestController extends Controller
{
// Laravel otomatis inject RequestService
public function __construct(
private RequestService $requestService
) {}
public function store(CreateRequestRequest $request): JsonResponse
{
$dto = CreateRequestDTO::fromRequest($request);
$result = $this->requestService->createRequest($dto);
return RequestResource::make($result)->response()->setStatusCode(201);
}
}
Keuntungan Dependency Injection
| Keuntungan | Penjelasan |
|---|---|
| Loose Coupling | Komponen tidak bergantung pada implementasi konkret |
| Testability | Dependensi dapat diganti dengan mock saat testing |
| Maintainability | Ganti implementasi tanpa ubah kode yang menggunakannya |
| Single Responsibility | Objek fokus pada tugasnya, bukan membuat dependensinya |
| Scalability | Mudah swap ke implementasi berbeda (misal: ganti FCM ke OneSignal) |
Contoh: Swap Implementasi Tanpa Ubah Kode
Jika ingin mengganti FCM ke implementasi notifikasi lain, cukup ubah satu baris binding:
// Sebelum
$this->app->bind(NotificationServiceInterface::class, FcmNotificationService::class);
// Sesudah — hanya ubah INI, seluruh kode lain tidak perlu diubah
$this->app->bind(NotificationServiceInterface::class, OneSignalNotificationService::class);
:::tip Keunggulan DI Ini adalah kekuatan utama Dependency Injection — kode bisnis tidak terikat pada detail implementasi teknis. :::
Ringkasan: DI di TemuBelajar
| Platform | Tool | Mekanisme |
|---|---|---|
| Flutter | Riverpod | Provider graph, auto-dispose |
| Laravel | Service Container | Interface binding, constructor injection |