Dari Arsitektur Berlapis ke Arsitektur Bersih
Pahami alasan dependensi seharusnya mengarah ke bagian dalam.
Dari Arsitektur Berlapis ke Arsitektur Bersih adalah pelajaran PHP Academy gratis di CoddyKit. Ini adalah pelajaran 1 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar PHP Academy, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus PHP Academy mencakup 4 pelajaran total.
Mengapa Arsitektur Bersih
Anda sudah mengenal tumpukan PHP tiga lapis klasik: Pengontrol → Layanan → Repositori → Basis Data. Tumpukan ini berfungsi, tetapi logika bisnis akhirnya terikat pada Eloquent, Doctrine, permintaan HTTP, dan siklus hidup kerangka kerja. Arsitektur Bersih membalik arah dependensi sehingga domain Anda tidak mengetahui apa pun tentang infrastruktur. Hasilnya: kasus penggunaan yang mudah diuji, adaptor yang dapat ditukar, dan basis kode yang tetap bertahan saat kerangka kerja ditingkatkan.
Aturan Dependensi
Satu-satunya aturan Arsitektur Bersih: dependensi kode sumber hanya mengarah ke dalam. Lingkaran dalam (entitas, kasus penggunaan) tidak boleh pernah merujuk ke lingkaran luar (pengontrol, ORM, kerangka kerja). Saat runtime, alur kendali bergerak keluar melalui antarmuka, tetapi pada waktu kompilasi/impor, tidak ada bagian dalam yang mengimpor bagian luar.
- Entitas: aturan perusahaan
- Kasus Penggunaan: aturan aplikasi
- Adaptor: pengontrol, penyaji, gerbang
- Kerangka Kerja & Penggerak: basis data, HTTP, web
Contoh Lapisan yang Saling Terikat
Berikut jenis layanan yang digunakan sebagian besar aplikasi PHP. Perhatikan bahwa logika domain terbelit dengan Eloquent dan respons HTTP. Anda tidak dapat menguji unit aturan diskon tanpa basis data dan kerangka kerja.
<?php
class OrderService
{
public function place(Request $request)
{
$user = User::find($request->user_id); // Eloquent
$total = 0;
foreach ($request->items as $i) {
$total += Product::find($i['id'])->price * $i['qty'];
}
if ($user->is_vip) {
$total *= 0.9; // business rule trapped in infra code
}
Order::create(['user_id' => $user->id, 'total' => $total]);
return response()->json(['total' => $total]);
}
}Entitas: Domain Tanpa Kerangka Kerja
Sebuah entitas menyandikan aturan yang berlaku di seluruh perusahaan dan tidak bergantung pada apa pun. PHP murni, tanpa anotasi, tanpa kelas dasar dari ORM. Entitas ini dapat dibuat sepenuhnya dalam pengujian.
<?php
final class Money
{
public function __construct(public readonly int $cents) {
if ($cents < 0) throw new InvalidArgumentException('negative money');
}
public function multiply(float $factor): self {
return new self((int) round($this->cents * $factor));
}
}
final class Order
{
/** @param array<int,int> $lineCents */
public function __construct(private array $lineCents, private bool $vip) {}
public function total(): Money {
$sum = array_sum($this->lineCents);
$money = new Money($sum);
return $this->vip ? $money->multiply(0.9) : $money;
}
}
echo (new Order([1000, 2000], true))->total()->cents, PHP_EOL; // 2700Kasus Penggunaan Memiliki Alur Kerja
Sebuah kasus penggunaan (interaktor) mengorkestrasi entitas dan berkomunikasi dengan dunia luar hanya melalui antarmuka (port). Kasus ini menerima objek transfer data permintaan dan mengembalikan objek transfer data respons — tidak pernah objek HTTP.
<?php
interface OrderRepository {
public function save(Order $order): void;
}
final class PlaceOrder
{
public function __construct(private OrderRepository $orders) {}
public function execute(array $lineCents, bool $vip): int {
$order = new Order($lineCents, $vip);
$this->orders->save($order);
return $order->total()->cents;
}
}Batasnya adalah Antarmuka
Kasus penggunaan mendeklarasikan antarmuka OrderRepository yang dibutuhkannya. Antarmuka tersebut berada di lingkaran dalam; implementasi konkret Eloquent/Doctrine berada di luar dan bergantung ke dalam. Inilah penerapan Prinsip Pembalikan Dependensi pada batas arsitektur.
Arah dependensi kode sumber: EloquentOrderRepository → OrderRepository (antarmuka), bukan sebaliknya.
<?php
// Lives in infrastructure layer, points INWARD to the domain interface
final class EloquentOrderRepository implements OrderRepository
{
public function save(Order $order): void {
OrderModel::create(['total' => $order->total()->cents]);
}
}Pengujian Tanpa Infrastruktur
Karena kasus penggunaan bergantung pada antarmuka, pengujian dapat menyuntikkan tiruan. Tanpa basis data, tanpa memulai kerangka kerja — pengujian unit secepat mikrodetik yang memastikan perilaku bisnis murni.
<?php
final class InMemoryOrders implements OrderRepository {
public array $saved = [];
public function save(Order $o): void { $this->saved[] = $o; }
}
$repo = new InMemoryOrders();
$useCase = new PlaceOrder($repo);
$total = $useCase->execute([1000, 2000], true);
assert($total === 2700);
assert(count($repo->saved) === 1);
echo "PASS total=$total saved=" . count($repo->saved) . PHP_EOL;Pengontrol Menjadi Adaptor Tipis
Pengontrol kini menjadi adaptor: menerjemahkan HTTP menjadi pemanggilan kasus penggunaan dan menerjemahkan hasilnya kembali menjadi HTTP. Pengontrol tidak memuat aturan bisnis. Tukarkan REST dengan CLI atau pekerja antrean, dan kasus penggunaan tidak perlu diubah.
<?php
final class OrderController
{
public function __construct(private PlaceOrder $placeOrder) {}
public function store(Request $request): JsonResponse {
$total = $this->placeOrder->execute(
lineCents: $request->input('lineCents'),
vip: (bool) $request->input('vip'),
);
return new JsonResponse(['total' => $total], 201);
}
}Arsitektur yang Menyuarakan Domain
Struktur folder harus menyuarakan domain, bukan kerangka kerja. Hindari Controllers/ dan Models/ di tingkat teratas. Aturlah berdasarkan kapabilitas agar pendatang baru dapat melihat fungsi aplikasi.
src/Ordering/Domain/— entitas, objek nilaisrc/Ordering/Application/— kasus penggunaan, antarmuka portsrc/Ordering/Infrastructure/— repositori Eloquent, pengontrol HTTP
Setiap konteks terbatas menjadi folder tingkat teratas; kerangka kerja berada di bagian tepi.
Menegakkan Aturan Dependensi
Tanpa alat bantu, disiplin akan terkikis. Gunakan deptrac atau phparkitect dalam integrasi berkelanjutan untuk menggagalkan pembangunan ketika lapisan domain mengimpor lapisan infrastruktur. Aturan tersebut menjadi jaminan pada waktu kompilasi, bukan sekadar harapan saat peninjauan kode.
# deptrac.yaml
deptrac:
layers:
- name: Domain
collectors: [{ type: directory, value: src/.*/Domain/.* }]
- name: Application
collectors: [{ type: directory, value: src/.*/Application/.* }]
- name: Infrastructure
collectors: [{ type: directory, value: src/.*/Infrastructure/.* }]
ruleset:
Domain: [] # Domain may depend on nothing
Application: [Domain]
Infrastructure: [Application, Domain]Melintasi Batas dengan Objek Transfer Data
Agar entitas tidak merembes ke luar, data yang melintasi batas dikirim sebagai objek transfer data sederhana, bukan sebagai entitas atau model ORM. Kasus penggunaan mengembalikan struktur datar yang dapat diserialisasikan oleh adaptor, sehingga objek domain tidak pernah keluar dari inti dan lapisan luar tidak pernah memperoleh akses ke keadaan internal.
<?php
final class OrderSummary // boundary DTO, no behavior, no domain types
{
public function __construct(
public readonly string $orderId,
public readonly int $totalCents,
) {}
}
final class PlaceOrderV2 {
public function __construct(private OrderRepository $orders) {}
public function execute(array $lineCents, bool $vip): OrderSummary {
$order = new Order($lineCents, $vip);
$this->orders->save($order);
return new OrderSummary('ord_1', $order->total()->cents);
}
}Pemeriksaan Cepat
Arah dependensi mana yang diizinkan menurut Aturan Dependensi?
Rangkuman
Anda telah beralih dari tumpukan berlapis yang saling terikat ke Arsitektur Bersih:
- Aturan Dependensi: dependensi kode sumber hanya mengarah ke dalam.
- Entitas memuat aturan perusahaan dalam PHP tanpa kerangka kerja.
- Kasus penggunaan mengorkestrasi melalui antarmuka port, serta mengembalikan objek transfer data, bukan HTTP.
- Pengontrol dan repositori ORM adalah adaptor luar yang bergantung ke dalam (DIP).
- Struktur harus menyuarakan domain, dan alat seperti deptrac menegakkan aturan tersebut dalam integrasi berkelanjutan.
Pertanyaan yang Sering Diajukan
Apakah pelajaran “Dari Arsitektur Berlapis ke Arsitektur Bersih” gratis?
Ya — teks lengkap “Dari Arsitektur Berlapis ke Arsitektur Bersih” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus PHP Academy, upgrade ke CoddyKit PRO. Kursus PHP Academy mencakup 4 pelajaran total.
Apa yang akan aku pelajari di “Dari Arsitektur Berlapis ke Arsitektur Bersih”?
Pahami alasan dependensi seharusnya mengarah ke bagian dalam. Kamu berlatih PHP Academy dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.
Apakah aku perlu pengalaman untuk memulai PHP Academy?
Tidak diperlukan pengalaman sebelumnya. PHP Academy di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 1 dari 4.
Berapa lama pelajaran “Dari Arsitektur Berlapis ke Arsitektur Bersih” memakan waktu?
Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.
Bisakah aku menulis dan menjalankan kode dalam pelajaran PHP Academy ini?
Ya. Setiap pelajaran PHP Academy menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.
Semua pelajaran dalam kursus ini
- Dari Arsitektur Berlapis ke Arsitektur Bersih
- Port dan Adapter Dijelaskan
- Kasus Penggunaan dan Layanan Aplikasi
- Inversi Dependensi dalam Praktik