🧱
14-Day Mastery Roadmap

SECURE BY DESIGN DAY 05

เริ่มทำควิซ ⚡
Part 2: Fundamentals | Chapter 5

Domain Primitives

Atomic Building Blocks ⚛️
📌 TITLE ANCHOR QUESTION ทำไมบทนี้ถึงชื่อนี้? และคำว่า Domain Primitive หมายถึงอะไร?

คำว่า "Domain Primitive": เกิดจากการนำแนวคิด Primitive Type ของภาษาโปรแกรมมิ่ง (เช่น int, String) มาแทนที่ด้วย "อิฐบล็อกระดับอะตอมในมุมมองของโดเมนธุรกิจ"

คำตอบและหมุดเชื่อมโยง: หากเราเลิกใช้ชนิดข้อมูลกว้างๆ ของภาษาที่รับค่าอะไรก็ได้ แล้วเปลี่ยนมาใช้ "โดเมนพริมิทิฟ" เป็นหน่วยที่เล็กที่สุดในการสร้างระบบ ตัว Domain Primitive นั่นเองที่จะการันตีเงื่อนไขเหล็กว่า "ถ้าวัตถุนี้มีตัวตนอยู่ได้ในระบบ แปลว่ามันต้องถูกต้อง 100% เสมอ" (If it exists, it is valid) โดยบังคับตรวจ Invariants ใน Constructor ตั้งแต่วินาทีที่สร้าง ซึ่งช่วยตัดปัญหา Injection, ข้อมูลรั่วไหล (Data Leakage) และการละเมิดกฎธุรกิจออกไปโดยธรรมชาติ!

วันก่อนหน้า (Recap Days 1–4)

Day 1: Security Concern / Day 2: The Anti-Hamlet / Day 3: DDD Core Tools / Day 4: Immutability & Validation Pipeline 5 ขั้น

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

Day 5 จาก 14 วัน — รวมร่าง Value Objects + Validation สู่ "Domain Primitives" อิฐบล็อกที่แข็งแกร่งที่สุดในสถาปัตยกรรม

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

เข้าใจกฎเหล็ก 4 ข้อ, Hardening APIs ด้วย Library, ป้องกันข้อมูลลับรั่วไหลด้วย Read-Once Objects, และ Decluttering Entities

02

เรื่องราวและมโนทัศน์เชิงลึก (Core Concepts & Narrative Scenarios)

🛒 SCENARIO 2.1 • การแก้อุปาทาน ANTI-HAMLET และการ HARDENING APIS ด้วย DOMAIN PRIMITIVE LIBRARY API Hardening

📖 จาก -1 สู่ Quantity Domain Primitive

ใน Chapter 2 คดี Anti-Hamlet เกิดจากการใช้ int รับจำนวนสินค้า ทำให้ผู้ใช้ป้อน -1 เล็ดลอดเข้ามาทำลายดุลบัญชี เมื่อคุยกับ Domain Expert เราพบว่า: "จำนวนสั่งซื้อต้องเป็น 1 ถึง 200 เท่านั้น (0 ไม่ถือว่าสั่งซื้อ เกิน 200 ต้องติดต่อฝ่ายขายส่ง)"

🧱 สร้างเป็น Domain Primitive: เราสร้างคลาส Quantity ที่มี private final int value, ตรวจสอบ inclusiveBetween(1, 200, value) ใน Constructor ทันที และห่อหุ้มพฤติกรรมโดเมน เช่น add(Quantity addend) ทำให้ค่าติดลบไม่มีทางเกิดขึ้นในระบบได้อีกเลย!

🛡️ Hardening APIs ด้วย Domain Primitive Library

❌ API แบบเดิมที่เสี่ยง: void sendAuditLogs(InetAddress serverAddress);
ผู้เรียกอาจเผลอหรือตั้งใจส่ง IP สาธารณะภายนอกเข้ามาแอบดูด Audit Log ข้อมูลความลับออกไป
✅ API ที่ Hardened ด้วย Domain Primitive: void sendAuditLogs(InternalAddress serverAddress);
บังคับใช้ InternalAddress ที่การันตี IP ภายในเครือข่ายความปลอดภัยเท่านั้น เมธอดเหลือเช็กแค่ notNull() คอมไพเลอร์จะบล็อก IP นอกเครือข่ายตั้งแต่ Compile-time!
🔓 SCENARIO 2.2 • โศกนาฏกรรม TOMCAT SESSION LEAK และรูปแบบ READ-ONCE OBJECTS Sensitive Data Shield

💥 เหตุการณ์รั่วไหลบนเซิร์ฟเวอร์ Tomcat

ทีมนักพัฒนานำอ็อบเจกต์ User ไปเก็บไว้ใน Web Session ของ Tomcat เพื่อแคชข้อมูล ต่อมาฝ่ายธุรกิจขอเพิ่มข้อมูลเลขบัตรประชาชน (SSN) และรหัสผ่านลงในคลาส User

⚠️ สิ่งที่เกิดขึ้น: เมื่อเซิร์ฟเวอร์ Tomcat ถูกสั่ง Restart หรือ Shutdown พฤติกรรมดั้งเดิมของมันคือ "แอบเขียนทุก Object ใน Web Session บันทึกลง Hard Disk (Persist Session to Disk)" หากเก็บข้อมูลลับเป็น String ปกติ รหัสผ่านและ SSN ของผู้ใช้ทุกคนจะถูกเขียนค้างไว้เป็น Plain Text บนดิสก์เซิร์ฟเวอร์ทันที!

🛡️ 5 กลไกคุ้มกันของ Read-Once Objects

1. Read-Once Enforcement: เรียกอ่านค่าผ่าน value() ได้เพียงครั้งเดียว หากเรียกซ้ำจะโยน Exception ทันที
2. Memory Wiping (char[]): ใช้ char[] แทน String เพื่อสั่ง Arrays.fill(value, '0') ล้างข้อมูลออกจากแรมทันทีหลังอ่าน
3. Prevent Serialization: สืบทอด Externalizable แล้วโยน UnsupportedOperationException บล็อก Tomcat จากการแอบเซฟลงดิสก์
4. Masked toString() & Final: คืนค่าเป็น "*****" ป้องกันรั่วไหลใน Log และทำคลาสเป็น final ป้องกันการแอบสืบทอด
🧹 SCENARIO 2.3 • การสะสาง ENTITY (DECLUTTERING ENTITIES) และการเทียบเคียง TAINT ANALYSIS Theory & Architecture

🧽 Entity ไม่ใช่แม่เหล็กดูดโค้ด (Decluttering Entities)

เมื่อ Entity แบกรับการเช็ก String, Regex, และ Checksum เมธอดจะมีโค้ดยาวเป็นร้อยบรรทัดจนนักพัฒนาเกิด Confirmation Bias มองข้ามจุดบกพร่องไป

⚡ หน้าที่ของ Domain Primitives: ดูดซับภาระการทำ Size, Lexical และ Syntax Validation ออกไปจัดการใน Constructor ของมันเอง
🎯 หน้าที่ของ Entity: เหลือเพียงการทำ Semantic Validation (ตรวจตรรกะทางธุรกิจระดับบริบท) เช่น "ออเดอร์นี้ปิดไปหรือยัง?" หรือ "สินค้าพอในสต็อกไหม?"

🧪 ความเชื่อมโยงกับ Taint Analysis ในเชิงทฤษฎี

ในวิทยาการคอมพิวเตอร์ Taint Analysis ใช้ติดตามข้อมูลสกปรกจากภายนอก:

📥 Taint Source: จุดที่ข้อมูลมลทินไหลเข้าสู่ระบบ (เช่น HTTP Request, ฟอร์ม)
🧼 Untainting: กระบวนการล้างมลทินผ่าน Constructor ของ Domain Primitive ทันที!
🎯 Taint Sink: จุดเป้าหมายที่มีความเสี่ยง (เช่น Database, OS Command)
🛡️ Zero Overhead: Domain Primitives ให้ความปลอดภัยเทียบเท่า Taint Analysis โดยไม่ต้องเสีย Performance ใน Runtime เลยแม้แต่น้อย!
⚛️ 4 กฎเหล็กของ DOMAIN PRIMITIVES (THE 4 PILLARS) Unbreakable Rules
1. Valid on Creation

บังคับตรวจ Invariants ทั้งหมดใน Constructor (Fail-Fast) ถ้ามีตัวตนอยู่ในระบบได้ แปลว่าต้องถูกต้อง 100% เสมอ

2. Strictly Immutable

กำหนดทุก Field เป็น private final และคลาสเป็น final ห้ามเปิด Setter ให้แก้ไขค่าหลังสร้างเด็ดขาด

3. No Generic Types

ห้ามใช้ int, String, หรือ null ลอยๆ แทนแนวคิดโดเมนที่มีกฎทางธุรกิจกำกับ

4. Encapsulated Logic

รวบรวมพฤติกรรมและการคำนวณของโดเมนไว้ในคลาสเอง (เช่น เมธอด add() ของ Quantity)

03

แบบฝึกหัดปฏิบัติตามสถานการณ์จริง (Hands-on Refactoring)

Before: Cluttered Entity (เสี่ยงหลุด Validation) Entity รับ Primitive Types
// โค้ดเดิม: Order แบกภาระสแกนตัวอักษรและ Checksum ไว้ในตัวเอง
public class Order {
    private List<OrderLine> items = new ArrayList<>();

    public void addItem(String isbn, int quantity) {
        // แบก Size, Lexical และ Syntax Checks ไว้ใน Entity
        if (isbn == null || isbn.length() != 10) {
            throw new IllegalArgumentException("Invalid ISBN length");
        }
        if (!isbn.matches("^[0-9]{9}[0-9X]$")) {
            throw new IllegalArgumentException("Invalid ISBN format");
        }
        // สุ่มเสี่ยงที่จะลืมเช็ก quantity <= 0 หรือลืมเช็ก Checksum!
        if (quantity <= 0) {
            throw new IllegalArgumentException("Quantity must be positive");
        }
        Book book = bookCatalog.findByIsbn(isbn);
        items.add(new OrderLine(book, quantity));
    }
}
⚠️ จุดพังของโค้ด: Entity เต็มไปด้วยเงื่อนไขทางไวยากรณ์ ทำให้มองข้ามกฎธุรกิจ และหากมีจุดรับ String isbn ใน Service อื่น โปรแกรมเมอร์ต้องเขียนโค้ดสแกน Regex ซ้ำอีกรอบ
After: Decluttered Entity with Domain Primitives Clean & Valid on Creation
// 1. Domain Primitive: ISBN ตรวจสอบสมบูรณ์แบบตั้งแต่ตอน new
public final class ISBN {
    private final String isbn;
    public ISBN(final String rawIsbn) {
        notNull(rawIsbn, "ISBN cannot be null");
        isTrue(rawIsbn.length() == 10, "ISBN length must be 10");
        isTrue(rawIsbn.matches("^[0-9]{9}[0-9X]$"), "Invalid syntax");
        isTrue(checksumValid(rawIsbn), "Checksum failed");
        this.isbn = rawIsbn;
    }
    public String value() { return isbn; }
}

// 2. Decluttered Order Entity: เหลือแค่ตรวจ Semantic Validation
public class Order {
    private final List<OrderLine> items = new ArrayList<>();
    private boolean paid = false;

    public void addItem(final ISBN isbn, final Quantity quantity) {
        notNull(isbn, "ISBN required");
        notNull(quantity, "Quantity required");

        // Entity เหลือเพียงตรวจกฎความหมายทางธุรกิจเท่านั้น!
        validState(!paid, "Cannot add items to a paid order");

        Book book = bookCatalog.findByIsbn(isbn);
        items.add(new OrderLine(book, quantity));
    }
}
🛡️ จุดเปลี่ยนสำคัญ: โค้ดใน Order.addItem สะอาดและปลอดภัย 100% ตัวแปร isbn และ quantity ที่ส่งเข้ามาการันตีว่าถูกต้องตามกฎธุรกิจมาแล้วตั้งแต่เกิด!
04

ชุดควิซประเมินความรู้ประจำวัน

ตอบปุ๊บ รู้ผลเฉลยทันที พร้อมคำอธิบายและหลอดพลังงาน Energy Charge

Energy Charge
0 / 10
📌 CLOSING TITLE CONNECTION WRAP-UP สรุปหมุดเชื่อมโยงกลับหาหัวข้อบท

สรุปการเชื่อมโยงกลับสู่ Chapter 5 — Domain primitives

เรื่องราวการแก้อุปาทาน Anti-Hamlet ด้วย Quantity, การ Hardening APIs ด้วย InternalAddress, การใช้ Read-Once Objects บล็อกไม่ให้ Tomcat แอบเขียนรหัสผ่านลงดิสก์, และการสะสาง Entity ในวันนี้ ล้วนสะท้อนคำตอบของหัวข้อบท "Domain primitives" อย่างสมบูรณ์แบบว่า เมื่อเราสร้างระบบขึ้นมาจากอิฐบล็อกระดับรากฐานที่เป็น Domain Primitives ตัวประเภทข้อมูลเหล่านี้นี่เองที่จะทำหน้าที่เป็นเกราะคุ้มกันความปลอดภัย ย่อยสลายความซับซ้อน และกำจัดช่องโหว่ทางซอฟต์แวร์ให้หมดไปอย่างยั่งยืนครับ!

พร้อมก้าวสู่การปกป้องสถานะของ Entity ในบทถัดไปแล้วหรือยัง? Next: Day 6 — Chapter 6: Ensuring integrity of state (Consistent on Creation & Collections) ➔