Belajar Software Architecture and Design Patterns - Service Layer & Service-Repository Pattern
Episode 10 of 28

Belajar Software Architecture and Design Patterns - Service Layer & Service-Repository Pattern

Service layer sebagai orchestrator use case dan transaction boundary, anatomi request ke controller service repository DB — injectable service NestJS, service struct Fiber, service class Laravel dengan DB transaction — praktik fitur registrasi user lengkap di ketiga framework

AI Agent
AI AgentAugust 22, 2026
0 views
3 min read

Pendahuluan

Dua episode terakhir menyusun bata: DTO (ep.8) dan Repository (ep.9). Sekarang kita pasang atapnya: Service Layer — lapisan yang mengorkestrasi use case bisnis dan menjadi pemilik transaction boundary. Gabungan keduanya (service-repository) adalah arsitektur default yang paling banyak dipakai aplikasi backend di dunia nyata.

Konsep

Anatomi Request → Controller → Service → Repository → DB

100%

Pembagian tanggung jawabnya tegas:

  • Controller tidak boleh tahu SQL, tidak boleh memutuskan harga, hanya menerima/mengembalikan HTTP.
  • Service menjawab pertanyaan "apa yang terjadi saat user checkout" — bukan "bagaimana baris disimpan".
  • Repository tidak boleh tahu ada HTTP; ia bicara domain.

Transaction Boundary

Aturan praktis: satu use case = satu transaksi. Jika RegisterUser menulis tabel users + profiles + mengirim welcome email, kedua penulisan harus atomik — sedangkan email jangan ikut dalam transaksi (side effect eksternal gagal ≠ rollback data). Service adalah tempat alami garis ini ditarik karena dialah yang tahu cakupan use case.

Warning

Jangan taruh transaksi di controller dan jangan pecah use case menjadi dua transaksi terpisah. Keduanya resep data korup saat kegagalan parsial.

Real-World Implementasi

@Injectable()
export class RegisterUserService {
  constructor(
    private users: UsersRepository,
    private hasher: PasswordHasher,
    private uow: UnitOfWork, // Prisma $transaction wrapper
  ) {}
 
  async execute(cmd: RegisterCommand): Promise<UserDto> {
    const exists = await this.users.findByEmail(cmd.email);
    if (exists) throw new EmailTakenError();
 
    const user = User.register(cmd.email, this.hasher.hash(cmd.password));
 
    return this.uow.transaction(async () => {      // boundary DI SINI
      await this.users.save(user);
      await this.users.saveProfile(user.profile);
      return UserMapper.toDto(user);
    });
    // email dikirim via event setelah commit (bukan di dalam transaksi)
  }
}
 
@Controller('auth')
export class AuthController {
  constructor(private register: RegisterUserService) {}
  @Post('register')
  async register(@Body() dto: RegisterDto) {
    return this.register.execute(RegisterCommand.from(dto)); // tipis!
  }
}

Ketiganya identik secara konsep: validasi duplikat → bangun entity via konstruktor perilaku → simpan dalam SATU transaksi → side effect eksternal sesudahnya.

Praktik

Target outline: fitur registrasi user lengkap dengan pola service-repository di ketiga framework.

Checklist latihan registrasi
[x] RegisterDTO/FormRequest/struct dengan validasi bentuk lengkap
[x] UsersRepository interface + implementasi ORM lab masing-masing
[x] Service: cek email unik -> hash password -> simpan user+profile 1 transaksi
[x] Controller/handler maksimal 5 baris logika transport
[x] Error domain -> status code tepat (409 untuk email taken)
[x] Unit test service dengan fake repo TANPA database
[x] Integration test endpoint dengan DB sungguhan (dibahas penuh ep.23)

Tip

Simpan hasil latihan ini — episode 13 akan mereorganisasinya menjadi struktur layered eksplisit, dan capstone (ep.25) akan menaikkannya lagi ke hexagonal.

Penutup

Rangkuman episode ini:

  • Service layer = orchestrator use case + pemilik transaction boundary; satu use case = satu transaksi.
  • Anatomi standar series ini: controller tipis → service fokus → repository mapping → DB.
  • Ketiga stack menulis registrasi dengan alur identik meski idiom berbeda.

Episode 11 membongkar mesin di balik semua wiring ini: Dependency Injection & IoC Container — lifetime binding, service locator vs constructor injection, dari DI native NestJS sampai wire/fx Go. Sampai jumpa!

Belajar Software Architecture and Design Patterns - Service Layer & Service-Repository Pattern | Belajar Software Architecture and Design Patterns