Clean Architecture เป็นแนวทางการออกแบบซอฟต์แวร์ที่ แยกความรับผิดชอบ (separation of concerns) อย่างชัดเจน เพื่อให้ระบบ:
- ทดสอบง่าย (Testable)
- ปรับเปลี่ยนง่าย (Flexible)
- ยืดอายุระบบ (Maintainable)
- ไม่ผูกติดกับ framework, database, UI
🧱 โครงสร้างของ Clean Architecture
🔄 กฎสำคัญ: Dependency Rule
โค้ดชั้นนอก สามารถเรียกใช้ ชั้นในได้
โค้ดชั้นใน ห้ามรู้จัก หรืออิงกับชั้นนอกเด็ดขาด
เช่น:
- Entity ห้ามรู้จัก Controller หรือ Database
- Use Case ห้ามรู้จัก UI หรือ Framework
Tips & Tricks การเขียน “Clean Code” ที่อ่านง่าย เข้าใจง่าย และดูแลง่าย
✨ 1. Meaningful Names – ตั้งชื่อให้รู้ว่าใช่
| ข้อควรทำ | ตัวอย่าง |
|---|
| ✅ ใช้ชื่อสื่อความหมาย (ไม่ย่อมั่ว) | calculateSalary() ดีกว่า cal() |
| ✅ ใช้ชื่อที่ตอบว่า “ทำอะไร” | isValidEmail() ดีกว่า check1() |
| ❌ หลีกเลี่ยงใช้ชื่อทั่วไปเกินไป | data, info, temp |
| ✅ ใช้ชื่อที่ consistent | ถ้าใช้ getX() ก็ใช้ setX() ด้วย |
🔧 2. Function – ฟังก์ชันควร “ทำอย่างเดียว”
| Guideline | ตัวอย่าง |
|---|
| ✅ ควรสั้น < 20 บรรทัด | ถ้าเกิน → แยกย่อย |
| ✅ ชื่อฟังก์ชันต้องบอกได้ว่าทำอะไร | sendWelcomeEmail() |
| ✅ ทำงานอย่างเดียว (Single Responsibility) | validateEmail() แยกจาก sendEmail() |
| ❌ อย่าให้มี side-effect ลับ ๆ | เช่น แก้ state global โดยไม่แจ้ง |
💬 3. Comment – อย่าใช้แทนชื่อที่ดี
| ข้อควรทำ | ตัวอย่าง |
|---|
| ✅ ใช้ comment อธิบาย “เหตุผล” ไม่ใช่ “ว่าโค้ดทำอะไร” | // ใช้ Retry เพราะ endpoint ไม่เสถียร |
| ❌ หลีกเลี่ยง comment ซ้ำกับโค้ด | i++; // เพิ่มค่า i ขึ้น 1 ❌ |
| ✅ ถ้าต้องใส่ comment → โค้ดอาจไม่ clean แล้ว | พิจารณา refactor แทน |
🧾 4. Formatting – อ่านง่าย = ดูแลง่าย
| แนวปฏิบัติ | อธิบาย |
|---|
| ✅ เว้นบรรทัดให้เห็นกลุ่มของ logic ชัดเจน | ตัวอย่าง: ระหว่าง if, loop |
| ✅ จัด indent ให้สม่ำเสมอ | ใช้ tab หรือ space ให้ทีมตกลงกัน |
| ❌ อย่ายัดทุกอย่างไว้บรรทัดเดียว | อ่านยาก |
| ✅ ใช้ linter / formatter | เช่น Prettier, ESLint, Checkstyle |
🏷️ 5. Classes – ควร “รับผิดชอบเรื่องเดียว”
| หลักการ | ตัวอย่าง |
|---|
| ✅ 1 class = 1 responsibility | ReportGenerator, EmailSender แยกกัน |
❌ ไม่ควรมี Util.java ที่ทำทุกอย่าง | |
✅ ใช้ interface เพื่อกำหนด contract | แล้ว implement แยกใน service class |
| ✅ ควร test ได้ง่าย | class ที่ดีต้อง test ได้แบบ isolated |
🧱 6. Object & Data Structures – ใช้ OOP อย่างเหมาะสม
| แนวคิด | ตัวอย่าง |
|---|
| ✅ ซ่อนการเข้าถึง field (encapsulation) | ใช้ private + getter/setter |
| ✅ พิจารณาใช้ record / DTO แยกจาก entity | |
| ❌ อย่าทำ data object ที่ไม่ฉลาดเลย (anemic model) | |
| ✅ สร้าง method ให้ object ทำงานแทนที่ logic อยู่ข้างนอก | |
⚠ 7. Error Handling – อย่าซ่อนบั๊ก
| ข้อควรทำ | ตัวอย่าง |
|---|
✅ ใช้ try/catch เฉพาะจุดที่จำเป็น | |
| ✅ ให้ข้อความ exception ชัดเจน | throw new InvalidEmailException("Email is not valid") |
| ❌ อย่าจับ exception แล้วไม่ทำอะไร | catch (Exception e) {} ❌ |
| ✅ ใช้ custom exception class แยกเฉพาะ domain | |
🧪 8. Unit Tests – โค้ดดี = test ได้ง่าย
| Guideline | อธิบาย |
|---|
| ✅ 1 test = 1 case = 1 assertion | |
| ✅ ชื่อฟังก์ชันควรบอกว่า test อะไร | shouldReturnTrueWhenEmailIsValid() |
| ✅ ใช้ Mock ให้เหมาะสม | เช่น Mockito |
| ❌ อย่า test logic ที่ framework ทำให้แล้ว | |
| ✅ Run test อัตโนมัติใน CI/CD |
🛠 วิธีปรับปรุงโปรเจคของคุณให้เข้าสู่ Clean Architecture
| สิ่งที่ควรทำ | ตัวอย่าง |
|---|
| แยก Entity ออกจาก DTO | สร้าง Employee กับ EmployeeDTO |
สร้าง Use Case class แยก เช่น CreateEmployeeUseCase | ไม่เขียน logic ใน Controller ตรง ๆ |
สร้าง interface เช่น EmployeeRepository ใน Use Case Layer | แล้วให้ Infra Layer implements มัน |
| อย่าให้ Use Case หรือ Entity ใช้ Spring, Hibernate, หรือ HttpServletRequest | ใช้ Dependency Injection เท่านั้น |
| ทดสอบ Use Case แยกเดี่ยวได้ด้วย JUnit + Mockito | ไม่ต้องโหลด Spring Context |
// Domain Layer
public class Employee {
private String id;
private String name;
}
// Use Case Layer
public interface CreateEmployeeUseCase {
void execute(Employee employee);
}
// Interface Adapter
@RestController
public class EmployeeController {
private final CreateEmployeeUseCase useCase;
@PostMapping("/employee")
public ResponseEntity<Void> create(@RequestBody EmployeeDTO dto) {
Employee employee = mapper.toEntity(dto);
useCase.execute(employee);
return ResponseEntity.ok().build();
}
}
✅ ทำไมต้องใช้ Clean Architecture?
Clean Architecture ถูกออกแบบมาเพื่อแก้ปัญหาคลาสสิกของซอฟต์แวร์ที่โตไปแล้วดูแลยาก เช่น…
- โค้ดพันกัน (Spaghetti Code)
- เปลี่ยน UI แล้วต้องแก้ Business Logic
- ย้าย Database หรือ Framework แล้วระบบพัง
- ทดสอบโค้ดยากมาก
แนวคิดนี้ แยก concerns ชัดเจน ทำให้ ทดสอบง่าย ปรับเปลี่ยนง่าย ขยายต่อได้สบาย
| ข้อดี | อธิบาย |
|---|
| ✅ ทดสอบง่าย | Unit test Use Case โดยไม่ต้องโหลด Spring หรือ DB |
| ✅ เปลี่ยน UI / DB ได้ไม่กระทบ logic | UI, DB อยู่ “วงนอก” |
| ✅ ขยายระบบได้ง่าย | เพิ่ม Use Case, Service ใหม่โดยไม่รบกวนโค้ดเดิม |
| ✅ แยกทีมทำงานได้ | แต่ละ layer รับผิดชอบชัดเจน |
| ✅ Code Maintainability สูง | แยก layer ช่วยให้เข้าใจเร็ว |
| ข้อเสีย | คำอธิบาย |
|---|
| ❌ โค้ดยาวขึ้น | ใช้หลายชั้น + interface เยอะ |
| ❌ อาจ Overkill | ถ้าใช้กับโปรเจคเล็กหรือไม่ซับซ้อน |
| ❌ เรียนรู้ยากสำหรับมือใหม่ | ต้องเข้าใจ Layering, DI, และ SOLID |
| ❌ ต้องวางแผนโครงสร้างดีตั้งแต่ต้น | ถ้าไม่วางให้ดีจะซ้ำซ้อน/เยิ่นเย้อ |
| ประเด็น | คำอธิบาย |
|---|
| 🧩 อย่า over-engineer | ถ้า logic ง่าย แค่แยก Service กับ Controller ก็พอ |
| 🧩 อย่าลืมสื่อสารกับทีม | ทุกคนต้องเข้าใจแนวทางเดียวกัน |
| 🧩 อย่าให้ Use Case รู้จัก DB หรือ Web | ใช้ Interface และ Dependency Injection เสมอ |
| 🧩 อย่าทำแต่ structure ละเลย business logic | สุดท้าย software ต้อง “ทำงานได้” ไม่ใช่แค่ “มี structure ดี” |
โค้ดที่ดี = คนอื่น (รวมถึงตัวคุณในอนาคต) อ่านแล้วเข้าใจง่าย
Clean code ไม่ใช่แค่ให้คอมไพล์ผ่าน แต่คือ “ภาษาที่ทีมสื่อสารกันได้”
ใส่ความเห็น