🥋
Phase 2: Deliberate Refactoring Katas

SECURE BY DESIGN DAY 15

เริ่มทำควิซสะสม ⚡
Phase 2: Deliberate Refactoring Katas | Kata 1 (Part 1)

Kata 1: Primitive Obsession Breakdown

Code Audit & Initial Domain Primitives 🥊
📌 KATA 1 MISSION & CUMULATIVE REPETITION ก้าวเข้าสู่สมรภูมิปฏิบัติการจริง (Refactoring Kata)
10 Cumulative Review Quizzes

ภารกิจประจำวัน: ยินดีต้อนรับสู่ Phase 2: Deliberate Refactoring Katas! วันนี้เราจะสวมบทบาทเป็น Security Auditor & Software Architect เพื่อสแกนค้นหารอยแตกร้าวใน Legacy Codebase ของระบบสั่งซื้อสินค้า (OrderProcessingService) วิเคราะห์รายการภัยคุกคามที่เกิดจากโรคเสพติดพริมิทิฟ (Primitive Obsession) และตีกรอบ Bounded Context เพื่อสกัด 2 โดเมนพริมิทิฟแรก: EmailAddress และ Quantity พร้อมติดตั้ง Validation Pipeline 5 ขั้น ให้ทำงานตั้งแต่หน้าประตู!

🔄 Day 1: Why Design Matters Design is the lock, not the hinge — เปลี่ยนจากการไล่แก้ Bug มาล็อกโครงสร้างคลาสให้ Valid ตั้งแต่แรก
📉 Day 2: Anti-Hamlet Effect กำจัด Primitive Obsession ที่เปิดรับค่าติดลบ (-1 เล่ม) จนระบบพากันเชื่อความเท็จเดียวกัน
🧱 Day 5: Validation Pipeline นำ Pipeline 5 ขั้น (Origin ➔ Size ➔ Lexical ➔ Syntax ➔ Semantics) ฝังใน Constructor
⚡ Day 9: Fail-Fast Contracts หยุดและ Throw Exception ทันทีที่รับขยะ เพื่อรักษาสถานะระบบให้อยู่ในสภาวะปลอดภัย (Fail-Secure)
Phase 1 (Days 1–14 Recap)

ปูพื้นฐานทฤษฎีครบ 100%: DDD Building Blocks, Immutability, Failure Handling, Cloud 3R's, และ Antifragile Culture

ตำแหน่งปัจจุบัน (Where We Are)

Day 15 จาก 35 วัน — วันแรกของ Phase 2 เริ่มต้น Kata 1 ตรวจสอบและระบุรอยแตกร้าวในโค้ดเดิม

เป้าหมายประจำวัน (Core Mission)

ชี้ชัด 4 ช่องโหว่จาก Primitive Obsession และสร้าง EmailAddress และ Quantity ให้ปลอดภัย 100%

02

การวิเคราะห์รอยแตกร้าว (Code Audit & Threat Analysis)

🔍 AUDIT TARGET • เมธอดประมวลผลคำสั่งซื้อเดิม (OrderProcessingService) Vulnerable Legacy Code
// Legacy Codebase: พึ่งพา Primitive Types กว้างๆ และเขียน Defensive Code แบบพลาสเตอร์ยา
public class OrderProcessingService {

    public void processOrder(String customerEmail, String shippingAddress, String itemCode,
                             int quantity, double unitPrice, String discountCode) {

        // 💥 Defensive Code แบบพลาสเตอร์ยา (Band-Aid): เมินเฉยข้อมูลขยะและปัดตกเงียบๆ!
        if (customerEmail == null || shippingAddress == null) {
            return; // Ignore ไม่แจ้งเตือน ไม่ Throw Exception ผู้เรียกคิดว่าสั่งซื้อสำเร็จ!
        }

        // 💥 เช็กไม่เด็ดขาด แค่พิมพ์บอก แต่ปล่อยให้คำนวณราคาติดลบต่อ!
        if (quantity <= 0) {
            System.out.println("Invalid quantity");
        }

        double totalAmount = quantity * unitPrice;
        System.out.println("Processing order for: " + customerEmail + " Total: " + totalAmount);
    }
}
FINDING #01 Confidentiality & Integrity

🔄 Parameter Swapping Attack / Error

customerEmail, shippingAddress, itemCode, และ discountCode ล้วนใช้ประเภทข้อมูล String เหมือนกันทั้งหมด!

💥 ผลกระทบ: หากผู้เรียกสลับพารามิเตอร์ processOrder(shippingAddress, customerEmail, ...) คอมไพเลอร์จะไม่เตือนแม้แต่คำเดียว แต่ระบบจะนำที่อยู่จัดส่งไปใช้เป็นอีเมล ส่งผลให้เกิด Sensitive Data Leakage ส่งใบเสร็จหรือลิงก์สำคัญไปผิดคนทันที
FINDING #02 Integrity & Business Logic

📉 Anti-Hamlet Effect (Negative Value Injection)

quantity ใช้ int และ unitPrice ใช้ double ซึ่งเปิดกว้างรับค่าติดลบได้อย่างอิสระ

💥 ผลกระทบ: หากแฮกเกอร์ส่ง quantity = -5 เข้ามา ราคารวมจะกลายเป็นติดลบ (-5 * 100 = -500) ระบบการเงินอาจทำรายการคืนเงิน (Credit Invoice) ให้แฮกเกอร์ฟรีๆ ซ้ำรอยโศกนาฏกรรม Anti-Hamlet ใน Day 2!
FINDING #03 Availability & Security

💣 Unchecked Injection Payloads & ReDoS

customerEmail ไม่มีการตรวจสอบความยาว (Size Check) และไม่ตรวจไวยากรณ์ (Syntax Check)

💥 ผลกระทบ: เปิดโอกาสให้ผู้โจมตียิงสตริงขยะยาว 1 ล้านอักขระ หรือสคริปต์อันตรายเข้ามาก่อกวน หรือยิง ReDoS Payload เข้าใส่ Regular Expression Engine จน CPU ค้างและระบบล่ม (Denial of Service)
FINDING #04 Maintainability & Traceability

🩹 Defensive Band-Aid Code (Silent Failures)

การใช้ if (customerEmail == null) return; แบบไร้คำเตือนและไม่บันทึก Audit Log

💥 ผลกระทบ: เมื่อเจอข้อมูลขยะ โค้ดเลือกที่จะ "เงียบเฉย (Silent Failure)" ทำให้ผู้เรียกใช้เข้าใจผิดว่าการสั่งซื้อสำเร็จ ทั้งๆ ที่คำสั่งซื้อถูกทิ้งไปเงียบๆ ข้อมูลขยะจึงค้างคาอยู่ในระบบโดยไม่มีใครตามสืบได้
03

การสกัด Domain Primitives สองตัวแรก (EmailAddress & Quantity)

VALIDATION PIPELINE 5 ขั้น:
1. Origin (null check) ➔ 2. Size (ขอบเขตความยาว) ➔ 3. Lexical ➔ 4. Syntax (Regex) ➔ 5. Semantics (กฎธุรกิจ)
✉️ DOMAIN PRIMITIVE 1 • EmailAddress.java (Self-Validating & Immutable) RFC 5321 Bound & Normalized
import java.util.Objects;
import java.util.regex.Pattern;

public final class EmailAddress {
    // Regex ตรวจสอบไวยากรณ์อีเมลมาตรฐาน
    private static final Pattern EMAIL_PATTERN =
        Pattern.compile("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,}$");

    private final String value;

    public EmailAddress(String rawEmail) {
        // ขั้นที่ 1: Origin Validation (ตรวจ null ทันทีที่ประตูหน้า)
        if (rawEmail == null) {
            throw new IllegalArgumentException("Email address cannot be null");
        }

        String trimmed = rawEmail.trim();

        // ขั้นที่ 2: Size Validation (ขอบเขตความยาว 1-254 ตามมาตรฐาน RFC ป้องกัน ReDoS)
        if (trimmed.isEmpty() || trimmed.length() > 254) {
            throw new IllegalArgumentException("Email length must be between 1 and 254 characters");
        }

        // ขั้นที่ 3 & 4: Lexical & Syntax Validation (ตรวจรูปแบบและโครงสร้างด้วย Regex)
        if (!EMAIL_PATTERN.matcher(trimmed).matches()) {
            throw new IllegalArgumentException("Invalid email format: " + rawEmail);
        }

        // ขั้นที่ 5: Semantic Validation (กฎธุรกิจเฉพาะ เช่น บล็อกโดเมนอันตราย)
        if (trimmed.endsWith("@malicious-domain.com")) {
            throw new IllegalArgumentException("Domain is restricted");
        }

        this.value = trimmed.toLowerCase(); // Normalization ป้องกันความคลาดเคลื่อน
    }

    public String value() { return value; }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        EmailAddress that = (EmailAddress) o;
        return Objects.equals(value, that.value);
    }

    @Override
    public int hashCode() { return Objects.hash(value); }

    @Override
    public String toString() { return value; }
}
📦 DOMAIN PRIMITIVE 2 • Quantity.java (Invariants & Encapsulated Operations) Anti-Hamlet Shield
public final class Quantity {
    private static final int MAX_ORDER_QUANTITY = 10000; // Invariant ขีดจำกัดทางธุรกิจ
    private final int value;

    public Quantity(int rawQuantity) {
        // ขั้นที่ 1 & 5: Origin & Semantic Fail-Fast Validation (ตัดค่าศูนย์และติดลบทันที!)
        if (rawQuantity <= 0) {
            throw new IllegalArgumentException("Quantity must be greater than zero. Provided: " + rawQuantity);
        }

        // ขั้นที่ 2: Size / Bound Validation
        if (rawQuantity > MAX_ORDER_QUANTITY) {
            throw new IllegalArgumentException("Quantity exceeds maximum limit of " + MAX_ORDER_QUANTITY);
        }

        this.value = rawQuantity;
    }

    public int value() { return value; }

    // Domain Operation: รวมศูนย์การคำนวณไว้ในตัวเอง คืนค่าเป็น Quantity ตัวใหม่ที่เป็น Valid เสมอ
    public Quantity add(Quantity other) {
        return new Quantity(this.value + other.value);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Quantity quantity = (Quantity) o;
        return value == quantity.value;
    }

    @Override
    public int hashCode() { return Integer.hashCode(value); }

    @Override
    public String toString() { return String.valueOf(value); }
}
🛡️ REFACTORED SIGNATURE • OrderProcessingService.java (Compile-Time Checked) No More Defensive Band-Aids
public class OrderProcessingService {

    // ✅ ปลอดภัยในระดับ Compile-Time 100%!
    // ไม่สามารถส่ง String สลับตำแหน่ง หรือส่ง Quantity ติดลบเข้ามาได้อีกต่อไป
    public void processOrder(EmailAddress customerEmail, String shippingAddress, String itemCode,
                             Quantity quantity, double unitPrice, String discountCode) {

        // โค้ดภายในเชื่อใจ EmailAddress และ Quantity ได้ทันที 100% ไม่ต้องเขียน Defensive Checks ซ้ำซ้อน!
        double totalAmount = quantity.value() * unitPrice;

        System.out.println("Processing order for: " + customerEmail.value() +
                           " Quantity: " + quantity.value() +
                           " Total: " + totalAmount);
    }
}
04

ชุดควิซประเมินความรู้สะสมประจำวัน (Day 15 Cumulative Quiz - 10 ข้อ)

ทบทวน Spaced Repetition (Days 1–14) และเจาะลึก Invariants Day 15 ตอบปุ๊บ รู้ผลเฉลยทันทีพร้อมคำอธิบาย

Kata Energy
0 / 10
📌 CLOSING SUMMARY & DAY 16 PREVIEW สรุปผลการปฏิบัติตาม Kata 1 (Part 1)

สรุปปิดท้าย Day 15 — Kata 1: Primitive Obsession Breakdown (Part 1)

ในวันนี้เราได้ทำ Code Audit ผ่าวิเคราะห์รอยแตกร้าวของ Primitive Obsession ใน Legacy Codebase ชี้ชัด 4 มิติภัยคุกคาม และลงมือสกัด Domain Primitives 2 ตัวแรก (EmailAddress และ Quantity) พร้อมฝัง Validation Pipeline 5 ขั้น สำเร็จเรียบร้อย! ควิซทั้ง 10 ข้อได้ดึงทฤษฎีสำคัญจาก Phase 1 (Hinge vs Lock, Anti-Hamlet, และ Fail-Fast) กลับมาเชื่อมต่อได้อย่างเหนียวแน่น

พรุ่งนี้ใน Day 16 เราจะสกัด Domain Primitives ส่วนที่เหลือให้ครบทั้งชุด (PostalAddress, ItemCode, Money, DiscountCode) และใช้แนวทาง New API Approach ล้างแค้น Legacy Code ทั้งหมด! Next: Day 16 — Kata 1 (Part 2: Completing Domain Primitives & Legacy Decoupling) ➔