Programming Tutorial Laravel Best Practices 27 July 2025

Modular Monolith Laravel: Cara Modern Bangun Aplikasi Tanpa Pusing

Modular Monolith Laravel: Cara Modern Bangun Aplikasi Tanpa Pusing
Bagikan:

Pernah nggak sih kamu merasa aplikasi Laravel yang awalnya rapi, lama-lama jadi berantakan? Awal coding semangat, tapi makin lama, controller makin gemuk, service makin ribet, dan setiap nambah fitur baru, rasanya kayak main Jenga—takut rubuh! Nah, artikel ini bakal ngajak kamu kenalan sama konsep modular monolith di Laravel. Konsep ini bikin project kamu tetap rapi, scalable, dan gampang di-maintain, tanpa harus pusing mikirin microservices.

Yuk, kita mulai perjalanan seru ini bareng-bareng!


Apa Itu Modular Monolith? Bayangin Satu Rumah, Banyak Kamar

Coba bayangkan kamu punya satu rumah besar. Di dalamnya ada banyak kamar: kamar tidur, dapur, ruang tamu, dan sebagainya. Setiap kamar punya fungsi sendiri, tapi tetap satu atap. Modular monolith itu seperti rumah ini—satu aplikasi, tapi dipecah jadi modul-modul yang jelas batasannya. Setiap modul punya domain sendiri, nggak saling mengganggu urusan dapur tetangga.

Kenapa Laravel Sering Jadi Spaghetti?

Laravel itu enak banget buat prototyping. Tapi, kalau nggak hati-hati, lama-lama project jadi:

  • Controller raksasa
  • Service class yang bengkak
  • Logic nyebrang ke mana-mana
  • Refactor jadi horor

Ini bukan salah framework-nya, tapi karena kurang struktur. Nah, modular monolith hadir buat ngatasin masalah ini.

Struktur Modular: Satu Project, Banyak Mini-Aplikasi

Bayangin struktur kayak gini:

/Modules /Orders /Domain /Application /Infrastructure /UI

Setiap modul kayak mini-aplikasi. Domain buat logika bisnis, Application buat use case, Infrastructure buat akses database, UI buat HTTP controller. Nggak ada yang saling nyampur. Satu deployment, tapi rapi kayak sushi, bukan spaghetti!

Golden Rules: Biar Modul Nggak Saling Ganggu

  • Setiap modul punya tujuan jelas (misal: Billing, Orders, Users)
  • Internal modul disembunyikan, cuma expose interface yang dibutuhkan
  • Komunikasi antar modul pakai event, bukan panggilan langsung
  • Bergantung pada abstraksi, bukan detail modul lain

Contoh Modular di Laravel: Orders Module

Misal kamu punya Orders module:

/Modules /Orders /Domain Order.php OrderStatus.php /Application PlaceOrder.php CancelOrder.php /Infrastructure OrderRepository.php /UI OrderController.php

  • Domain: logika bisnis murni
  • Application: use case (misal, PlaceOrder)
  • Infrastructure: akses database
  • UI: HTTP entry point

Nggak ada logic bisnis di controller, nggak ada query di domain. Semua punya tempatnya sendiri.

Tips Laravel Biar Modular Beneran

  • Bikin service provider khusus per modul
  • Pakai interface, hindari facade statis
  • Komunikasi antar modul lewat event
  • Binding dependency cukup di modul masing-masing

Domain vs Infrastructure: Jangan Campur Aduk

  • Domain logic ≠ Eloquent
  • Controller ≠ business rules
  • Simpan behavior di Application layer, bukan di model atau controller
  • Eloquent cukup jadi data mapper

Organisasi Berdasarkan Fitur, Bukan Tipe File

Daripada /Controllers, /Models, mending /Modules/Orders, /Modules/Users, dst. Onboarding dev baru jadi gampang, dan setiap fitur punya “rumah” sendiri.

Shared Kernel: Jangan Jadi Tempat Sampah

Kadang ada logic yang memang harus dipakai bareng, kayak Currency, UserRole, TimeZone. Tapi shared kernel harus kecil, stabil, dan abstrak. Anggap aja kayak “standard library” di aplikasi kamu.

Events Lebih Baik dari Static Call

Daripada:

Billing::charge($user);

Mending:

event(new OrderPaid($order));

Ini bikin modul nggak saling ketergantungan, gampang di-test, dan siap kalau suatu saat mau migrasi ke microservices.

Testing Modular: Lebih Mudah dan Menyenangkan

Karena domain dan application layer nggak tergantung Laravel, kamu bisa test logic pakai PHP murni. Contoh:

$orderPlacer = new PlaceOrder($orderRepository, $paymentGateway);
$orderPlacer->handle($request);

Nggak perlu database, nggak perlu HTTP. Fokus ke rules dan behavior.

Transisi Bertahap, Nggak Perlu Sekaligus

Nggak harus refactor semua sekaligus. Mulai aja dari satu domain, misal Users. Bikin /Modules/Users, pindahin logic pelan-pelan, daftarin service provider, binding interface, dan ulangi untuk modul lain. Aman dan terukur.

Siap Microservices, Tapi Nggak Ribet

Modular monolith bikin kamu siap kalau suatu saat mau pecah jadi microservices. Tapi sekarang, kamu bisa nikmatin:

  • Deploy cepat
  • Debugging simpel
  • Sedikit moving parts
  • Kecepatan development

Kolaborasi Tim Lebih Lancar

Setiap tim bisa “punya” modul sendiri. Onboarding dev baru? Tinggal buka /Modules/Analytics, langsung paham scope kerjanya. Nggak ada lagi saling tunjuk kalau ada bug.

Penutup: Bangun Modular, Tetap Monolith

Microservices itu buat perusahaan raksasa. Modular monolith cocok buat kita yang pengen aplikasi rapi, scalable, dan nggak bikin pusing. Laravel sangat mendukung pola ini, asal kamu disiplin.

Jadi, yuk mulai bangun aplikasi dengan cara yang benar. Tim kamu, masa depan kamu, dan aplikasi kamu bakal berterima kasih!


Struktur Folder Modular Laravel
/Modules
  /Orders
    /Domain
    /Application
    /Infrastructure
    /UI
  /Payments
  /Users
  /Subscriptions
Contoh Event di Laravel Modular
// Mengirim event setelah order dibayar
// File: Modules/Orders/Application/OrderPaid.php
namespace Modules\Orders\Application;

use Illuminate\Foundation\Events\Dispatchable;

class OrderPaid
{
    use Dispatchable;

    public $order;

    public function __construct($order)
    {
        $this->order = $order;
    }
}

// Memanggil event
event(new OrderPaid($order));
Contoh Testing Use Case Modular
// File: Modules/Orders/Application/PlaceOrderTest.php
namespace Modules\Orders\Application;

use PHPUnit\Framework\TestCase;

class PlaceOrderTest extends TestCase
{
    public function testPlaceOrder()
    {
        $orderRepository = $this->createMock(OrderRepository::class);
        $paymentGateway = $this->createMock(PaymentGateway::class);
        $orderPlacer = new PlaceOrder($orderRepository, $paymentGateway);
        $request = [/* data order */];
        $result = $orderPlacer->handle($request);
        $this->assertTrue($result);
    }
}
Referensi & Resource