⚡
14-Day Mastery Roadmap

SECURE BY DESIGN DAY 07

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

Reducing Complexity of State

50% Completed Milestone 🏁
📌 TITLE ANCHOR QUESTION ทำไมบทนี้ถึงชื่อนี้? และความซับซ้อนของสถานะเกี่ยวกับ Security อย่างไร?

คำตอบและหมุดเชื่อมโยง: คำว่า "Reducing complexity of state" ตั้งขึ้นเพื่อแก้ปัญหาที่เกิดขึ้นเมื่อ Entity ในระบบเริ่มเติบโต มีหลายสถานะ (States) และมีเงื่อนไขการเปลี่ยนสถานะ (State Transitions) ที่ยุ่งเหยิงซับซ้อน

ผู้เขียนย้ำเตือนว่า "ความซับซ้อนของสถานะคือแหล่งกบดานของแฮกเกอร์" เมื่อ Entity มีสถานะยุ่งเหยิง นักพัฒนามักหลุดเขียนเงื่อนไข if-else ตรวจสอบไม่ครบทุกจุด เปิดช่องให้เกิด Logic Flaws เช่น สามารถสั่งจัดส่งสินค้าออกไปได้ทั้งที่คำสั่งซื้อยังอยู่ในสถานะไม่ได้ชำระเงิน การลดความซับซ้อนของสถานะผ่านรูปแบบการออกแบบ 4 ชนิด (Partially Immutable Entities, Entity State Objects, Entity Snapshots, และ Entity Relay) จึงเป็นการตัดสภาวะสถานะที่เป็นไปไม่ได้ออกไป ส่งผลให้ช่องโหว่ความปลอดภัยถูกกำจัดออกไปโดยปริยาย!

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

Day 1: CIA-T / Day 2: The Anti-Hamlet / Day 3: DDD Core / Day 4: Immutability / Day 5: Domain Primitives / Day 6: Ensuring Integrity of State

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

Day 7 จาก 14 วัน — วันสุดท้ายของ Part 2 Fundamentals สรุปปิดฉากรากฐานสถาปัตยกรรมระดับแกนนำ!

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

เรียนรู้ 4 อาวุธหลักสยบความซับซ้อนของสถานะ (Partially Immutable, State Objects, Snapshots, Entity Relay) เพื่อให้ Entity ปราศจากช่องโหว่

02

เรื่องราวและมโนทัศน์เชิงลึก (4 State Management Patterns & Scenarios)

TOPIC 2.0 • CONCURRENCY IMPACT Thread Safety

Single-threaded vs. Multithreaded Environments

⚡ Single-threaded Environment

การประมวลผลถูกจำกัดให้มีเพียง Thread เดียวเข้าถึง Entity ตัวนั้น ณ เวลาหนึ่ง (เช่น Actor Model หรือการโหลด Entity แยกอินสแตนซ์ต่อหนึ่ง Client Request) ทำให้จัดการความปลอดภัยของสถานะได้ง่ายผ่าน mutable objects

⚠️ Multithreaded Environment

หลาย Threads เข้าถึง Entity อินสแตนซ์เดียวกันได้พร้อมกัน (เช่น การเก็บ Entity ไว้ใน Shared Cache เพื่อเน้น Performance) การใช้ synchronized ก่อให้เกิด Thread Contention และเสี่ยงต่อ Deadlock จนระบบล่ม (Loss of Availability)

💒 SCENARIO 2.1 • สถานะการแต่งงาน (MARITAL STATUS) และอันตรายของ IF GUARDS กระจัดกระจาย Entity State Object

📖 กฎสถานะและความผิดพลาดดั้งเดิม

พิจารณาสถานภาพการแต่งงานของบุคคล มี 2 สถานะหลัก คือ Unmarried (โสด) และ Married (แต่งงานแล้ว) คนโสดเดตได้และแต่งงานได้ เมื่อแต่งงานแล้วห้ามเดตและห้ามแต่งซ้ำ แต่หย่าเพื่อกลับมาโสดได้

💥 อันตรายของ Scattered if Guards: นักพัฒนามักเอา if (!isMarried()) ไปซ่อนไว้ลึกๆ ตาม Service หรือในคลาส Person เมื่อระบบเติบโต มีกิจกรรมใหม่เพิ่มขึ้นมา เช่น party() นักพัฒนาคนอื่นจะ "ลืมเขียน if เช็กสถานะ" ทำให้คนแต่งงานแล้วแอบเดตได้! หากเปรียบกับร้านค้าออนไลน์ การลืม if เช็กสถานะชำระเงินในเมธอดจัดส่ง จะทำให้ของถูกส่งออกไปฟรีๆ โดยไม่จ่ายเงิน!

🛡️ ทางรอดด้วย Entity State Object

แยกตรรกะการควบคุมสถานะออกมาเป็นคลาสช่วยเฉพาะ เช่น MaritalStatus หรือ OrderState:

public class MaritalStatus {
    private boolean married = false;
    public void date() {
        validState(!married, "Not appropriate to date when married");
    }
    public void marry() { validState(!married, "Already married"); this.married = true; }
    public void divorce() { validState(married, "Cannot divorce if not married"); this.married = false; }
}
🎯 คลาส Person ส่งผ่านการทำงาน (Delegate) ไปให้ Helper ตรวจสอบก่อนเสมอ ตรรกะสถานะจึงรวมศูนย์ที่เดียว ทดสอบยูนิตเทสได้ 100% ไม่มีทางลืมเช็ก!
📸 SCENARIO 2.2 • ภาพถ่าย INSTAGRAM และแนวคิด ENTITY SNAPSHOTS (READ-HEAVY MULTITHREADING) CQRS Pattern

🖼️ อุปมาอุปไมย: รูปถ่ายบน Instagram

จินตนาการถึงเพื่อนสนิทที่ย้ายไปอยู่ต่างประเทศ คุณไม่ได้เจอตัวจริงของเขา (ตัวจริงเปรียบเสมือน Entity ที่แท้จริงใน Database) คุณติดตามชีวิตของเขาผ่าน "รูปถ่ายบน Instagram" รูปถ่ายแต่ละใบคือ Snapshot ณ วินาทีนั้น เป็นวัตถุเปลี่ยนค่าไม่ได้ (Immutable) คุณสามารถก๊อปปี้รูปแจกจ่ายให้คนดูพร้อมกันพันคนได้โดยที่เพื่อนของคุณไม่ต้องหยุดนิ่งหรือเดือดร้อน

⚡ สถาปัตยกรรม Entity Snapshots สำหรับระบบโหลดสูง

Read Side: ส่งออกเฉพาะ OrderSnapshot ซึ่งเป็น Immutable Value Object ให้ Threads อ่านพร้อมกันโดยปราศจาก Lock ("No locking, no blocking") ทนทานต่อ DoS
Write Side: การแก้ไขสถานะจะส่งคำสั่งไปให้ OrderService (Domain Service) เป็นผู้ยิงคำสั่งอัปเดตลง Database โดยตรง
🛡️ Security Gain: แยกอ่าน/เขียนตามหลัก CQRS ฝั่งอ่านไม่มี Mutating Methods ปลอดภัยจากการแอบแก้ไขสถานะ 100%!
🏃 SCENARIO 2.3 • เส้นทางวิบาก 15 สถานะของคำสั่งซื้อ และแนวคิด ENTITY RELAY (การวิ่งคบเพลิง) Phased Architecture

🧶 ฝันร้ายของไหมพรม 15 สถานะ

เมื่อระบบเปิดใช้หลายปี คำสั่งซื้อเติบโตจาก 2 สถานะ กลายเป็น 15 สถานะ: CONFIGURING ➔ PAID ➔ SHIPPED ➔ DELIVERED ➔ REJECTED ➔ UNDER_RETURN ➔ RETURNED ➔ REPAID ➔ MISPLACED ➔ LOST... การยัดทั้งหมดไว้ใน Order ตัวเดียวทำให้เกิดเส้นทางประหลาดที่เปิดช่องโหว่ความปลอดภัย

🏃‍♂️ การแก้ปัญหาด้วย Entity Relay (วิ่งส่งไม้คบเพลิง)

แบ่งช่วงชีวิตออกเป็นเฟสๆ และสร้าง Entity ประจำแต่ละเฟสมารับไม้ต่อ:

📦 Phase 1: PreliminaryOrder (เลือกซื้อและชำระเงิน - 4-5 สถานะ)
🚚 Phase 2: DefinitiveOrder (จัดส่งพัสดุ - 4-5 สถานะ)
↩️ Phase 3: RejectedOrder (ส่งคืนและคืนเงิน - 3-4 สถานะ)
⚡ กฎเหล็ก: No Looping Back! ต้องวิ่งไปข้างหน้าเท่านั้น ห้ามย้อนกลับเฟสเดิม เพื่อให้ State Graph เรียบง่ายและตรวจสอบได้สมบูรณ์
🏛️ 4 เสาหลักในการลดความซับซ้อนของสถานะ (THE 4 STATE MANAGEMENT PATTERNS) Foundations for Security
1. Partially Immutable Entities

ล็อคฟิลด์ที่ไม่ควรเปลี่ยน (เช่น Identity, CreationDate) เป็น private final เพื่อลดส่วนที่เคลื่อนไหวได้ (Moving Parts) และให้คอมไพเลอร์ช่วยตรวจตั้งแต่ Compile-time

Field-level Immutability
2. Entity State Objects

แยกกฎการเปลี่ยนสถานะออกมาเป็น Helper Class เฉพาะ (เช่น OrderState) แล้วให้ Entity ทำการ Delegate ตรวจสอบก่อนเสมอ รวมศูนย์การตรวจสอบความปลอดภัยไว้ที่เดียว

State Helper Delegation
3. Entity Snapshots

ส่งออกเฉพาะภาพถ่ายสถานะที่เป็น Immutable Value Object ให้ฝั่งอ่านเรียกใช้ตามแนวคิด CQRS "No locking, no blocking" ปลอดภัยจากการแอบแก้ไขสถานะ

Immutable Read Views
4. Entity Relay

หั่นช่วงชีวิตของ Entity ที่มี 10+ สถานะออกเป็นเฟสๆ วิ่งส่งไม้ต่อกันไปข้างหน้าโดยไม่มีลูปย้อนกลับ ลดขนาด State Graph ให้เข้าใจง่ายและปราศจาก Logic Flaws

Phased Lifespan Running
03

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

Before: Complex Monolithic Order (เสี่ยงส่งของฟรี) Scattered if Guards & Confusion
// โค้ดเดิม: รวมทุกสถานะไว้ใน Order เดียว เช็ก if สับสน
public class Order {
    private String status; // "CONFIGURING", "PAID", "SHIPPED"...
    private List<OrderItem> items;

    public void ship() {
        if (this.status.equals("CONFIGURING")) {
            throw new IllegalStateException("Under configuration!");
        }
        // ช่องโหว่ร้ายแรง: ลืมเช็ก if (!this.status.equals("PAID")) !!
        this.status = "SHIPPED"; // แฮกเกอร์สั่งส่งของได้ทั้งที่ยังไม่ได้จ่ายเงิน!
    }
}
⚠️ จุดพังของโค้ด: เมื่อมีสถานะเยอะ ตรรกะ if-else พันกันเป็นไหมพรม เมธอด ship() ลืมตรวจสถานะ PAID ทำให้ส่งสินค้าออกไปได้โดยไม่จ่ายเงิน!
After: Entity Relay Architecture (การันตีสถานะ 100%) No Looping Back & Consistent on Creation
// 1. Entity เฟสแรก: PreliminaryOrder (เลือกซื้อและชำระเงิน)
public final class PreliminaryOrder {
    private final OrderId id;
    private final List<OrderItem> items;
    private boolean paid = false;

    public PreliminaryOrder(OrderId id, List<OrderItem> items) {
        this.id = notNull(id);
        this.items = Collections.unmodifiableList(new ArrayList<>(items));
    }

    // ชำระเงินเสร็จ ให้ "สร้างและส่งไม้ต่อ" ให้ DefinitiveOrder
    public DefinitiveOrder pay(PaymentReceipt payment) {
        notNull(payment, "Payment receipt required");
        validState(!paid, "Order is already paid");
        this.paid = true;
        return new DefinitiveOrder(this.id, this.items, payment);
    }
}

// 2. Entity เฟสสอง: DefinitiveOrder (จัดส่งพัสดุ - การันตี paid แล้ว 100%)
public final class DefinitiveOrder {
    private final OrderId id;
    private final List<OrderItem> items;
    private final PaymentReceipt payment;
    private ShippingStatus status = ShippingStatus.PREPARING;

    DefinitiveOrder(OrderId id, List<OrderItem> items, PaymentReceipt payment) {
        this.id = notNull(id); this.items = notNull(items);
        this.payment = notNull(payment); // มีใบเสร็จการันตีตั้งแต่กำเนิด!
    }

    public void ship() {
        validState(this.status == ShippingStatus.PREPARING, "Cannot ship");
        this.status = ShippingStatus.SHIPPED; // ปลอดภัย 100% ไม่มีทางส่งของโดยไม่จ่ายเงิน!
    }
}
🛡️ จุดเปลี่ยนสำคัญ: แยกคลาส PreliminaryOrder และ DefinitiveOrder ออกจากกันอย่างเด็ดขาด DefinitiveOrder ไม่ต้องเขียน if เช็กว่าจ่ายเงินหรือยัง เพราะมันถือกำเนิดขึ้นได้ก็ต่อเมื่อจ่ายเงินแล้วเท่านั้น (Consistent on Creation) ปิดช่องโหว่การข้ามสถานะอย่างสมบูรณ์แบบ!
04

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

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

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

สรุปการเชื่อมโยงกลับสู่ Chapter 7 — Reducing complexity of state

การศึกษาเทคนิคทั้ง 4 ในวันนี้ (Partially Immutable Entities, Entity State Objects, Entity Snapshots, และ Entity Relay) ล้วนตอบคำถามหัวข้อบท "Reducing complexity of state" อย่างสมบูรณ์แบบว่า ความซับซ้อนของสถานะคือศัตรูตัวร้ายของความปลอดภัย การลดความซับซ้อนโดยการแช่แข็งฟิลด์ที่ไม่เปลี่ยน, แยกคลาสช่วยควบคุมสถานะ, ถ่าย Snapshot สำหรับอ่านขนานกัน, และแบ่งเฟสการวิ่งส่งไม้ต่อ จะช่วยย่อยสลายความยุ่งเหยิง ตัดเส้นทางที่ผิดกฎทางธุรกิจออกไป และสร้างซอฟต์แวร์ที่ปลอดภัย แข็งแกร่ง และทรงประสิทธิภาพอย่างยั่งยืนครับ!

🎉 ยินดีด้วยครับ! คุณได้เรียนจบ Part 2: Fundamentals (Day 1 - Day 7) เรียบร้อยแล้ว!
พร้อมก้าวเข้าสู่ Part 3: Applying the Fundamentals ในวันพรุ่งนี้แล้วหรือยัง? Next: Day 8 — Chapter 8: Leveraging your delivery pipeline for security ➔