Lewati ke konten utama

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

MethodTanggung 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

MethodTanggung 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:

  1. Menghitung ulang average_rating tutor
  2. Menambahkan XP berdasarkan durasi dan rating
  3. Men-trigger SISService untuk update Social Impact Score
  4. 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;
}