Lewati ke konten utama

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

KeuntunganPenjelasan
Loose CouplingKomponen tidak bergantung pada implementasi konkret
TestabilityDependensi dapat diganti dengan mock saat testing
MaintainabilityGanti implementasi tanpa ubah kode yang menggunakannya
Single ResponsibilityObjek fokus pada tugasnya, bukan membuat dependensinya
ScalabilityMudah 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

PlatformToolMekanisme
FlutterRiverpodProvider graph, auto-dispose
LaravelService ContainerInterface binding, constructor injection