BAB 44. SERVICE LAYER
Tujuan
Service Layer menyimpan seluruh business logic aplikasi. Controller tidak boleh berisi logika kompleks — hanya menerima request, mendelegasikan ke service, dan mengembalikan response.
Prinsip: Thin Controller, Fat Service.
Struktur Service
app/Modules/
├── Requests/Services/
│ └── RequestService.php
├── Matching/Services/
│ └── MatchingService.php
├── Bookings/Services/
│ └── BookingService.php
├── Meetings/Services/
│ └── MeetingService.php
├── Ratings/Services/
│ └── RatingService.php
├── SIS/Services/
│ └── SISService.php
└── Notifications/Services/
└── NotificationService.php
Alur Service Utama
flowchart TD
C["🎛️ Controller"] --> RS["RequestService\n.createRequest()"]
RS --> REPO["RequestRepository\n.create()"]
RS --> MS["MatchingService\n.dispatch()"]
MS --> Q["⏳ Queue Job\nRunAIMatchingJob"]
RS --> NS["NotificationService\n(opsional)"]
RS --> EV["📡 Event\nRequestCreated"]
style RS fill:#7c3aed,color:#fff
style Q fill:#b45309,color:#fff
RequestService
// app/Modules/Requests/Services/RequestService.php
class RequestService
{
public function __construct(
private RequestRepositoryInterface $repository,
private MatchingService $matchingService,
) {}
public function createRequest(CreateRequestDTO $dto): Request
{
// 1. Business validation
$this->validateStudentEligibility($dto->studentId);
// 2. Persist
$request = $this->repository->create($dto->toArray());
// 3. Trigger AI Matching
$this->matchingService->dispatch($request);
// 4. Fire event
event(new RequestCreated($request));
return $request;
}
private function validateStudentEligibility(int $studentId): void
{
$activeRequests = $this->repository
->countActiveByStudent($studentId);
if ($activeRequests >= 3) {
throw new MaxActiveRequestsException(
'Maksimum 3 request aktif dalam satu waktu.'
);
}
}
}
BookingService
| Method | Tanggung Jawab |
|---|---|
acceptBooking(int $id) | Accept request, buat chat room, kirim notifikasi |
rejectBooking(int $id, string $reason) | Reject, trigger AI cari tutor alternatif |
reschedule(int $id, datetime $newTime) | Propose jadwal baru ke siswa |
cancelBooking(int $id) | Cancel oleh host sebelum sesi dimulai |
public function acceptBooking(int $bookingId): Booking
{
$booking = $this->bookingRepository->findByIdOrFail($bookingId);
// Update status
$booking->update(['status' => BookingStatus::ACCEPTED]);
// Create chat room
$this->chatService->createRoom($booking);
// Notify student
$this->notificationService->sendToUser(
$booking->request->student->user_id,
'Tutor Ditemukan! 🎉',
"Booking kamu telah diterima oleh {$booking->tutor->user->name}"
);
event(new BookingAccepted($booking));
return $booking;
}
MeetingService
| Method | Tanggung Jawab |
|---|---|
checkIn(CheckInDTO $dto) | Validasi QR + GPS, mulai timer sesi |
checkOut(CheckOutDTO $dto) | Akhiri sesi, hitung durasi, buka form rating |
addSummary(int $id, array $data) | Tambah ringkasan dan foto dokumentasi |
RatingService
Setelah rating disimpan, RatingService secara otomatis:
- Menghitung ulang average_rating tutor
- Menambahkan XP berdasarkan durasi dan rating
- Men-trigger SISService untuk update Social Impact Score
- Men-trigger pembaruan Leaderboard
public function submitRating(CreateRatingDTO $dto): Rating
{
$rating = $this->ratingRepository->create($dto->toArray());
// Recalculate tutor stats
$this->studentService->recalculateRating($dto->rateeId);
// Award XP
$this->xpService->award($dto->raterId, $dto->meetingId);
// Update SIS
$this->sisService->recalculate($dto->rateeId);
event(new RatingSubmitted($rating));
return $rating;
}