จากสถาปัตยกรรมแบบเลเยอร์สู่สถาปัตยกรรมสะอาด
ทำความเข้าใจว่าเหตุใดการพึ่งพาควรชี้เข้าด้านใน
จากสถาปัตยกรรมแบบเลเยอร์สู่สถาปัตยกรรมสะอาด เป็นบทเรียน PHP Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน PHP Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส PHP Academy มีบทเรียนทั้งหมด 4 บทเรียน
เหตุใดจึงใช้สถาปัตยกรรมสะอาด
คุณคงคุ้นเคยกับโครงสร้าง PHP แบบสามชั้นดั้งเดิมแล้ว: ตัวควบคุม → บริการ → คลังข้อมูล → ฐานข้อมูล โครงสร้างนี้ใช้งานได้ แต่ตรรกะทางธุรกิจจะถูกผูกเข้ากับอีโลเควนต์ ด็อกทริน คำขอเอชทีทีพี และวงจรชีวิตของกรอบงาน สถาปัตยกรรมสะอาดกลับทิศทางการพึ่งพา เพื่อให้โดเมนไม่รู้อะไรเกี่ยวกับโครงสร้างพื้นฐาน ผลลัพธ์คือกรณีใช้งานที่ทดสอบได้ อะแดปเตอร์ที่สับเปลี่ยนได้ และฐานโค้ดที่รองรับการอัปเกรดกรอบงาน
กฎการพึ่งพา
กฎข้อเดียวของสถาปัตยกรรมสะอาดคือ การพึ่งพาของซอร์สโค้ดต้องชี้เข้าด้านในเท่านั้น วงกลมด้านใน (เอนทิตี กรณีใช้งาน) ต้องไม่อ้างอิงวงกลมด้านนอก (ตัวควบคุม ตัวแมปวัตถุสัมพันธ์ กรอบงาน) เมื่อโปรแกรมทำงาน การควบคุมจะไหลออกด้านนอกผ่านส่วนติดต่อ แต่ในเวลาคอมไพล์หรือนำเข้า ไม่มีสิ่งใดจากด้านในนำเข้าสิ่งใดจากด้านนอก
- เอนทิตี: กฎระดับองค์กร
- กรณีใช้งาน: กฎระดับแอปพลิเคชัน
- อะแดปเตอร์: ตัวควบคุม ตัวนำเสนอ ช่องทางเชื่อมต่อ
- กรอบงานและตัวขับ: ฐานข้อมูล เอชทีทีพี เว็บ
ตัวอย่างสถาปัตยกรรมแบบชั้นที่ผูกโยงกัน
นี่คือลักษณะของบริการที่แอป PHP ส่วนใหญ่นำไปใช้งาน โปรดสังเกตว่าตรรกะโดเมนพันกันอยู่กับอีโลเควนต์และการตอบกลับเอชทีทีพี คุณไม่สามารถทดสอบหน่วยกฎส่วนลดได้โดยไม่ใช้ฐานข้อมูลและกรอบงาน
<?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]);
}
}เอนทิตี: โดเมนที่ไม่ขึ้นกับกรอบงาน
เอนทิตีเข้ารหัสกฎที่ใช้ทั่วทั้งองค์กรและไม่พึ่งพาสิ่งใด ใช้ PHP ธรรมดา ไม่มีคำกำกับ และไม่มีคลาสฐานจากตัวแมปวัตถุสัมพันธ์ จึงสร้างขึ้นได้อย่างสมบูรณ์ในการทดสอบ
<?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; // 2700กรณีใช้งานเป็นเจ้าของลำดับงาน
กรณีใช้งาน (ตัวประสานงาน) ทำหน้าที่ประสานเอนทิตีและติดต่อโลกภายนอกผ่านส่วนต่อประสาน (พอร์ต) เท่านั้น โดยรับวัตถุถ่ายโอนข้อมูลคำขอและส่งคืนวัตถุถ่ายโอนข้อมูลการตอบกลับ — ไม่ส่งคืนออบเจ็กต์เอชทีทีพี
<?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;
}
}ขอบเขตคือส่วนต่อประสาน
กรณีใช้งานประกาศส่วนต่อประสาน OrderRepository ที่ต้องการ ส่วนต่อประสานนี้อยู่ในวงกลมด้านใน ขณะที่การใช้งานจริงของอีโลเควนต์หรือด็อกทรินอยู่ด้านนอกและพึ่งพาเข้าด้านใน นี่คือการประยุกต์ใช้หลักการกลับทิศการพึ่งพาที่ขอบเขตสถาปัตยกรรม
ทิศทางการพึ่งพาของซอร์สโค้ด: EloquentOrderRepository → OrderRepository (ส่วนต่อประสาน) ไม่ใช่ทิศทางกลับกัน
<?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]);
}
}การทดสอบโดยไม่ใช้โครงสร้างพื้นฐาน
เนื่องจากกรณีใช้งานพึ่งพาส่วนต่อประสาน การทดสอบจึงฉีดตัวจำลองเข้าไปได้ ไม่ต้องใช้ฐานข้อมูล ไม่ต้องเริ่มระบบกรอบงาน — การทดสอบหน่วยที่เร็วระดับไมโครวินาทีและตรวจสอบพฤติกรรมทางธุรกิจล้วน
<?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;ตัวควบคุมกลายเป็นอะแดปเตอร์แบบบาง
ตอนนี้ตัวควบคุมเป็นอะแดปเตอร์: แปลงเอชทีทีพีเป็นการเรียกกรณีใช้งาน และแปลงผลลัพธ์กลับเป็นเอชทีทีพี ตัวควบคุมไม่มีกฎธุรกิจ หากเปลี่ยนจาก REST เป็น CLI หรือผู้ปฏิบัติงานคิว กรณีใช้งานก็ยังไม่ต้องแก้ไข
<?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);
}
}สถาปัตยกรรมที่บอกโดเมนอย่างชัดเจน
โครงสร้างโฟลเดอร์ควรสื่อถึงโดเมนอย่างชัดเจน ไม่ใช่กรอบงาน หลีกเลี่ยง Controllers/ และ Models/ ในระดับบนสุด จัดระเบียบตามความสามารถ เพื่อให้ผู้มาใหม่มองเห็นได้ทันทีว่าแอปทำอะไร
src/Ordering/Domain/— เอนทิตี ออบเจ็กต์ค่าsrc/Ordering/Application/— กรณีใช้งาน ส่วนต่อประสานพอร์ตsrc/Ordering/Infrastructure/— คลังข้อมูลอีโลเควนต์ ตัวควบคุมเอชทีทีพี
แต่ละบริบทที่มีขอบเขตชัดเจนเป็นโฟลเดอร์ระดับบนสุด ส่วนกรอบงานอยู่ตรงขอบ
การบังคับใช้กฎการพึ่งพา
หากไม่มีเครื่องมือ วินัยจะค่อย ๆ เสื่อมลง ใช้ ดีพแทรก หรือ พีเอชพีอาร์คิเทกต์ใน CI เพื่อให้การประกอบล้มเหลวเมื่อโดเมนนำเข้าโครงสร้างพื้นฐาน กฎนี้จึงกลายเป็นหลักประกันขณะคอมไพล์ แทนที่จะเป็นเพียงความหวังจากการตรวจทานโค้ด
# 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]การข้ามขอบเขตด้วยวัตถุถ่ายโอนข้อมูล
เพื่อไม่ให้เอนทิตีเล็ดลอดออกไป ข้อมูลที่ข้ามขอบเขตจะเดินทางในรูปวัตถุถ่ายโอนข้อมูลแบบเรียบง่าย ไม่ใช่เอนทิตีหรือแบบจำลองของตัวแมปวัตถุสัมพันธ์ กรณีใช้งานส่งคืนโครงสร้างแบนที่อะแดปเตอร์ทำให้เป็นอนุกรมได้ ดังนั้นออบเจ็กต์โดเมนจะไม่หลุดออกจากแกนกลาง และชั้นด้านนอกก็ไม่สามารถเข้าถึงสถานะภายในได้
<?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);
}
}ตรวจสอบอย่างรวดเร็ว
ภายใต้กฎการพึ่งพา อนุญาตให้พึ่งพาไปในทิศทางใด
สรุปทบทวน
คุณได้เปลี่ยนจากโครงสร้างแบบชั้นที่ผูกโยงกันมาเป็นสถาปัตยกรรมสะอาด:
- กฎการพึ่งพา: การพึ่งพาของซอร์สโค้ดชี้เข้าด้านในเท่านั้น
- เอนทิตีเก็บกฎระดับองค์กรไว้ใน PHP ที่ไม่ขึ้นกับกรอบงาน
- กรณีใช้งานประสานงานผ่านส่วนต่อประสานพอร์ต และส่งคืนวัตถุถ่ายโอนข้อมูล ไม่ใช่เอชทีทีพี
- ตัวควบคุมและคลังข้อมูลของตัวแมปวัตถุสัมพันธ์เป็นอะแดปเตอร์ด้านนอกที่พึ่งพาเข้าด้านใน (DIP)
- โครงสร้างควรสื่อถึงโดเมนอย่างชัดเจน และเครื่องมืออย่างดีพแทรกจะบังคับใช้กฎนี้ใน CI
คำถามที่พบบ่อย
บทเรียน “จากสถาปัตยกรรมแบบเลเยอร์สู่สถาปัตยกรรมสะอาด” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “จากสถาปัตยกรรมแบบเลเยอร์สู่สถาปัตยกรรมสะอาด” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส PHP Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส PHP Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “จากสถาปัตยกรรมแบบเลเยอร์สู่สถาปัตยกรรมสะอาด”
ทำความเข้าใจว่าเหตุใดการพึ่งพาควรชี้เข้าด้านใน คุณปฏิบัติ PHP Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน PHP Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน PHP Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน
บทเรียน “จากสถาปัตยกรรมแบบเลเยอร์สู่สถาปัตยกรรมสะอาด” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน PHP Academy นี้ได้ไหม
ได้ บทเรียน PHP Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- จากสถาปัตยกรรมแบบเลเยอร์สู่สถาปัตยกรรมสะอาด
- อธิบายพอร์ตและอะแดปเตอร์
- กรณีใช้งานและบริการแอปพลิเคชัน
- การกลับทิศทางการพึ่งพาในการใช้งานจริง