← Back to PostgreSQL course
PHASE 3: DEVOPS, MAINTAINABILITY & ARCHITECTURAL BOUNDARIES PILLAR 02 DEPLOYMENT 🚀

Day 11: Database-as-Code with Flyway / Liquibase Migration Mechanics (การบริหารจัดการ Stored Units ในรูปแบบ Database-as-Code, กลไก Versioned vs. Repeatable Migrations, การตรวจจับ Schema Drift, และการรวมระบบเข้ากับ CI/CD Pipeline)

ยกระดับโค้ดฐานข้อมูลจากสคริปต์แก้ไขด้วยมือหลังบ้าน (Ad-hoc Manual Fix) สู่สถาปัตยกรรม Database-as-Code (DbaC) ที่สามารถเก็บใน Git, ทำ Peer Review, และรันผ่าน CI/CD Pipeline แบบอัตโนมัติ 100% พร้อมไขความลับกลไก Dual-Migration Strategy: V__*.sql (Versioned สำหรับตาราง) vs R__*.sql (Repeatable สำหรับ Stored Units) ขจัดปัญหา Schema Drift อย่างเด็ดขาด!

📍 ตำแหน่งปัจจุบันของคุณบน Roadmap (18-Day Masterclass) เริ่มต้น Phase 3: DevOps & Architectural Boundaries
Phase 1: Architecture Synthesis (Days 1–4) ✓
➔
Phase 2: 4 Pillars & Capstone (Days 5–10) ✓
➔
Day 11: Database-as-Code & Flyway
➔ ➔
เปิดบท

ถอดรหัสคำสำคัญของวันนี้

Phase 3 เริ่มที่คำถามว่าโค้ดในฐานข้อมูลจะอยู่กับทีมได้อย่างไร — เก็บใน Git รีวิวได้ และ deploy ผ่าน pipeline ไม่ใช่ให้คนแอบเข้าไปแก้บน production ด้วยมือ

Database-as-Code

แนวคิดการบริหารโครงสร้างและโปรแกรมยูนิตในฐานข้อมูลให้เหมือนซอร์สโค้ดซอฟต์แวร์ เก็บใน Git ทำ code review และสั่ง deploy ผ่าน CI/CD

Flyway / Liquibase

เครื่องมือที่อ่านไฟล์สคริปต์ SQL ตามลำดับ แล้วไปรันอัปเดตบนฐานข้อมูลเป้าหมายให้อัตโนมัติ พร้อมบันทึกประวัติไว้

Versioned (V__) vs Repeatable (R__)

สคริปต์แบบ Versioned ใช้กับโครงสร้างตาราง — รันครั้งเดียว ห้ามแก้ไฟล์เดิม ส่วน Repeatable ใช้กับ stored code — รันใหม่ให้อัตโนมัติทุกครั้งที่ไฟล์ถูกแก้

โดยรวมวันนี้คือการจัดการสคริปต์ฐานข้อมูลด้วย Flyway แยกโครงสร้างตารางกับ stored code ออกจากกัน แล้วนำเข้าสู่ CI/CD เพื่อขจัดปัญหาคนแอบไปแก้ฐานข้อมูลด้วยมือ (schema drift)

เปรียบกับชีวิตจริง

เหมือนสมุดบัญชีที่รันเลขหน้าถาวร (Versioned V__) ห้ามฉีกห้ามลบข้อความเดิม กับคู่มือการทำงานที่พิมพ์ฉบับใหม่มาเปลี่ยนแทนเล่มเก่าได้เรื่อยๆ (Repeatable R__)

ช่วยให้ทีมโปรแกรมเมอร์หลายคนทำงานบนฐานข้อมูลเดียวกันได้อย่างเป็นระบบ โดยไม่ต้องกลัวว่าโค้ดจะทับกันหรือหายไป

1

🏛️ มโนทัศน์ สถาปัตยกรรมองค์กร และสถานการณ์จริง (Concept & Architecture)

หลังจากที่เราได้สร้าง Stored Units ประสิทธิภาพสูงทั้ง 4 เสาหลัก (UDFs, Procedures, Triggers, Materialized Views) สำเร็จแล้ว คำถามคลาสสิกที่สถาปนิกและผู้บริหารมักจะถามเสมอคือ "เราจะดูแลโค้ดพวกนี้อย่างไรเมื่อมีนักพัฒนา 20 คน? จะควบคุมไม่ให้คนแอบเข้าไปแก้ฟังก์ชันบน Production ได้อย่างไร? และจะกู้คืนระบบกลับสู่ State เดิมได้เร็วแค่ไหนหาก Deploy พัง?"

💥 บทเรียนจริงจากสมรภูมิระดับโลก: Monzo Bank & Deliveroo กับหายนะ Schema Drift
ในระบบ Core Banking ยุคใหม่ของ Monzo Bank และแพลตฟอร์มสั่งอาหารระดับโลก Deliveroo มีทีมนักพัฒนาซอฟต์แวร์กระจายอยู่หลายสิบ Microservices ในช่วงแรกของการเติบโต มีวิศวกรใช้สิทธิ DB Admin เชื่อมต่อตรงเข้าไปแก้ Stored Procedure และตารางบน Production เพื่อ Hotfix บั๊กฉุกเฉินตอนดึกโดยไม่ได้บันทึกลง Git Repository

ผลที่ตามมาคือ "Production Database หลุดลอยออกจากความเป็นจริง (Schema Drift)" เมื่อ CI/CD Pipeline อัตโนมัติรันในเช้าวันจันทร์ มันได้ดึงเอาสคริปต์เก่าจาก Git ไปรันทับตัว Hotfix ส่งผลให้ระบบธุรกรรมตัดบัตรเครดิตพังทลายยาวนานหลายชั่วโมง! ทั้งสององค์กรจึงได้ตั้งกฎเหล็กปฏิวัติสถาปัตยกรรมเป็น Database-as-Code (DbaC) บังคับว่า "หากสคริปต์ DDL ไม่ได้ผ่าน Pull Request ใน Git และไม่ผ่าน Flyway Pipeline จะไม่มีใคร แม้กระทั่ง Principal Engineer มีสิทธิแตะต้อง Production Database ได้เลย!"
🎯 บทเรียนสำคัญ: ความเร็วของระบบจะไร้ความหมายทันที หากปราศจากความสามารถในการทำ Reproducible Deployment และ Version Control ที่เชื่อถือได้ 100%
🚨 วิกฤตแก้ Bug สดบน Production: เมื่อการลืม Commit โค้ดกลับ Git ทำระบบภาษีพัง 8 ชั่วโมง!

ทีมพัฒนาระบบการเงินมีนักพัฒนา 15 คน และมี Environment 4 ชั้น (Dev, SIT, UAT, Production) ในคืนวันศุกร์ เกิด Bug ในการปัดเศษทศนิยมของฟังก์ชัน fn_calculate_tax() บน Production นักพัฒนาอาวุโสใช้สิทธิ DB Admin แอบเปิด DBeaver เข้าไปแก้โค้ดบน Production โดยตรงจนหายดี แต่ ลืมนำโค้ดที่แก้กลับมาบันทึกลง Git Repository:

Schema Drift
Production ฉีกออกจาก Git (Dev ≠ UAT ≠ Prod)
CI/CD พังทลาย
Deployment รอบถัดไปดึงโค้ดเก่าจาก Git มาทับตัวแก้สด
8 ชั่วโมงกู้ระบบ
ทีมต้องเสียเวลาไล่กู้คืนฟังก์ชันด้วยมือกลางดึก

ต้นตอของวิกฤตนี้คือ "การปล่อยให้ฐานข้อมูลเป็น State ลึกลับที่ไม่ผ่าน Version Control" ทางออกเดียวในระดับ Enterprise คือการเปลี่ยนกระบวนทัศน์มาเป็น Database-as-Code (DbaC) ด้วยเครื่องมือ Migration อย่าง Flyway หรือ Liquibase ครับ!

🏗️ อุปมาอุปไมย: โครงสร้างเสาตึกที่ทุบทิ้งไม่ได้ (Versioned) vs การจัดวางเฟอร์นิเจอร์ใหม่ (Repeatable)

🧱 Versioned Migration (V__*.sql: โครงสร้างเสาและคานคอนกรีต)

เปรียบเสมือนการหล่อเสาเข็มและเทคานคอนกรีตของอาคาร (เช่น การสร้างตาราง CREATE TABLE หรือเพิ่มคอลัมน์)

  • ทำได้ครั้งเดียวตามลำดับ: ห้ามทุบเสาเข็มต้นเดิมทิ้ง ต้องต่อเติมตามลำดับ V1 ➔ V2 ➔ V3 เสมอ
  • ห้ามแก้ไขไฟล์เดิม: หากเทเสาผิด ห้ามแอบไปแก้สคริปต์ V1 แต่ต้องออกสคริปต์ V2 เพื่อแก้ไข (Immutable Sequence)
  • ตรวจสอบ Checksum เข้มงวด: หากใครแอบแก้ไฟล์เดิม Flyway จะตรวจพบและหยุดทันที!
🛋️ Repeatable Migration (R__*.sql: การตกแต่งและทาสีห้องใหม่)

เปรียบเสมือนการทาสีผนังหรือจัดวางเฟอร์นิเจอร์ใหม่ (เช่น Stored Functions, Procedures, Views, Triggers)

  • ทำซ้ำกี่ครั้งก็ได้ (Idempotent): ทาสีทับของเดิมได้เสมอด้วยคำสั่ง CREATE OR REPLACE
  • รันใหม่เฉพาะเมื่อไฟล์เปลี่ยน: Flyway จะคำนวณ Checksum หากไฟล์นี้ใน Git มีการแก้ มันจะถูกหยิบมารันใหม่อัตโนมัติ
  • เก็บ History ในไฟล์เดียว: ประวัติการแก้ฟังก์ชันถูกเก็บใน Git commit history ของไฟล์นั้น ไม่ต้องสร้างไฟล์ใหม่เกะกะ

⚖️ การเปรียบเทียบเชิงสถาปัตยกรรม: Versioned Migration vs. Repeatable Migration ใน Flyway

มิติเปรียบเทียบสถาปัตยกรรม Versioned Migration (V__*.sql) Repeatable Migration (R__*.sql)
ลักษณะการตั้งชื่อไฟล์ V1__create_tables.sql, V2__add_idx.sql R__01_functions.sql, R__02_procedures.sql
เป้าหมายวัตถุในฐานข้อมูล Stateful Objects (ตาราง, คอลัมน์, Foreign Keys) Stateless/Code Objects (UDFs, Procedures, Triggers, Views)
ความถี่ในการรัน (Execution Frequency) รันเพียง 1 ครั้งถ้วน ตามลำดับเวอร์ชัน รันใหม่ ทุกครั้งที่สคริปต์ใน Git มีการเปลี่ยนแปลง Checksum
ลำดับการทำงาน (Execution Order) รันก่อนเสมอ เรียงตามหมายเลขเวอร์ชัน (V1 ➔ V2 ➔ V3) รันทีหลังสุด หลังจาก Versioned Migrations สำเร็จทั้งหมดแล้ว
ข้อกำหนดทางไวยากรณ์ (Syntax Rule) คำสั่ง DDL ปกติ (ห้ามรันซ้ำ) ต้องเป็น Idempotent (เช่น CREATE OR REPLACE)
ผลเมื่อแก้เนื้อหาในไฟล์เดิม 🚨 Validation Failed: Checksum Mismatch! ✅ คำนวณ Checksum ใหม่และ Deploy อัตโนมัติ
สถาปัตยกรรมระดับลึก: การไหลของโค้ดฐานข้อมูลจาก Git สู่ Multi-Environment ผ่าน Flyway CI/CD Pipeline Database-as-Code Pipeline
❌ แย่: Ad-hoc Manual Fix (แอบแก้สดบน Prod เกิด Schema Drift) ✅ ปัง: Git-Centric Flyway CI/CD Pipeline (Dual-Migration Strategy) 1. Developer ใช้สิทธิ Admin แอบแก้สดบน Production • เปิด GUI (DBeaver / pgAdmin) ต่อตรงเข้า Production DB • แก้โค้ด Stored Procedure fn_calculate_tax() ด่วนกลางดึก ⚠️ โค้ดที่แก้ไม่ได้ถูกบันทึกลง Git Repository! 2. หายนะ Schema Drift & Blind Deployment • ฐานข้อมูล Dev, UAT และ Production มีโค้ดไม่เหมือนกัน • เมื่อทำ Deployment รอบใหม่ CI/CD ดึงโค้ดเก่าจาก Git มาทับ • บั๊กเก่าฟื้นคืนชีพ ระบบคิดภาษีผิดพลาดกระทบลูกค้านับแสนราย 💥 ทีมงานตาบอด ไม่รู้ว่าใครแก้โค้ดอะไรไปเมื่อไหร่! ผลกระทบต่อองค์กร: Deployment Rollback ล้มเหลว • ไม่สามารถ Rollback ฐานข้อมูลกลับสู่จุดเดิมได้ • ทีมงานต้องเสียเวลา Manual Fix และไล่เปรียบเทียบสคริปต์ 8 ชม. ผิดกฎบัตรความปลอดภัย และขัดต่อมาตรฐาน ISO 27001 / PCI-DSS 1. Git Repository: Single Source of Truth • V__*.sql: โครงสร้างตาราง (Immutable Versioned) • R__*.sql: Stored Functions, Procedures, Triggers (Repeatable) • ผ่าน Pull Request / Peer Code Review และตรวจสอบไวยากรณ์ ⚡ บันทึกประวัติการเปลี่ยนแปลงครบถ้วน 100% ใน Git Commits 2. Automated CI/CD Engine (Flyway Validate & Migrate) flyway_schema_history (Tracking Table ใน PostgreSQL) คำนวณ Hashing Checksum ➔ เช็คความถูกต้อง ➔ รันเฉพาะสิ่งที่เปลี่ยน • หากไฟล์ V__ ถูกแอบแก้ ➔ Checksum Mismatch Abort ทันที • หากไฟล์ R__ ถูกแก้ไข ➔ ตรวจพบ Checksum ใหม่ สั่ง Deploy อัตโนมัติ! Deploy ไร้รอยต่อ Dev = SIT = UAT = Production 100% ขจัด Schema Drift, กู้คืนระบบสู่ State ใดๆ ได้ใน 3 วินาที, ไร้ Downtime
🎙️ สคริปต์คำพูดสำหรับพรีเซนท์บนเวที (Presentation Pitch Script)

"ทุกท่านครับ การแอบเปิด GUI เข้าไปแก้ Stored Procedure บน Production DB คือฝันร้ายที่ทำลายระบบ CI/CD และเป็นสาเหตุอันดับ 1 ของ Deployment Failures!

ทางออกระดับสถาปัตยกรรมคือการใช้ Database-as-Code ผ่าน Flyway โดยแยกยุทธศาสตร์ออกเป็น 2 ส่วน: Versioned Migrations (V__) สำหรับโครงสร้างตารางที่ไม่เปลี่ยนบ่อย และ Repeatable Migrations (R__) สำหรับ Stored Units ซึ่งรันใหม่ให้อัตโนมัติทุกครั้งที่มีการแก้โค้ดใน Git!

ผลลัพธ์คือการขจัดปัญหา Schema Drift อย่างเด็ดขาด สามารถกู้คืนระบบกลับสู่ State ใดๆ ในประวัติศาสตร์ได้ภายใน 3 วินาที และทำให้การปล่อยโค้ดฐานข้อมูลเป็นเรื่องอัตโนมัติ 100% บน CI/CD Pipeline ครับ"

🧮 วิดเจ็ตทดลอง: ตัวจำลองการทำงานของ Flyway Migration & Checksum Validation

ลองจำลองสถานการณ์การแก้ไขไฟล์ใน Git Repository หรือการแอบแก้ไขฐานข้อมูลหลังบ้าน เพื่อดูว่า Flyway Engine จะตอบสนองอย่างไร และจะเกิด Schema Drift หรือไม่

ผลลัพธ์การทำงานของ Flyway (Flyway Engine Response):
• ตรวจพบ Checksum ใหม่ในไฟล์ Repeatable ➔ สั่ง Deploy ทับของเดิมด้วย CREATE OR REPLACE อัตโนมัติ
• สถานะความปลอดภัย: ปลอดภัย 100% ไร้ Schema Drift
✅ Migration Success (Rerun R__)
2

💻 ปูพื้นฐานไวยากรณ์ & แบบฝึกหัดไต่ระดับ (Database-as-Code with Flyway)

การทำ Database-as-Code ต้องเริ่มตั้งแต่การเข้าใจโครงสร้างไดเรกทอรีของสคริปต์, การตั้งชื่อไฟล์ที่ถูกต้องตามมาตรฐาน Flyway, และการเขียนสคริปต์ Stored Units ใน Repeatable Migration ให้มีคุณสมบัติ Idempotent (รันซ้ำกี่ครั้งก็ได้ผลลัพธ์ถูกต้องเสมอ):

🧱 แกะโครงสร้างไดเรกทอรีและรูปแบบการตั้งชื่อไฟล์สคริปต์ตามมาตรฐาน Flyway

โครงสร้างไดเรกทอรี: Flyway Database-as-Code Blueprint Flyway Folder Structure
db/migration/
├── V1__create_core_tables.sql       <-- [1] Versioned: รันครั้งแรกครั้งเดียว ห้ามแก้ไฟล์นี้ตลอดกาล
├── V2__add_orders_indexes.sql       <-- [2] Versioned: รันต่อจาก V1 ตามลำดับหมายเลขเวอร์ชัน
├── R__01_udf_calculations.sql       <-- [3] Repeatable: รันใหม่ทุกครั้งที่มีการแก้โค้ด UDF ใน Git
├── R__02_stored_procedures.sql     <-- [4] Repeatable: รันใหม่ทุกครั้งที่มีการแก้ Procedure ใน Git
└── R__03_audit_triggers.sql        <-- [5] Repeatable: รันใหม่ทุกครั้งที่มีการแก้ Trigger ใน Git
💡 กฎเหล็กของ Flyway: เครื่องหมาย Underscore สองตัว (__) คือตัวคั่นระหว่างประเภท/เวอร์ชันกับคำอธิบายสคริปต์ ห้ามใช้ขีดเดียวเด็ดขาด!

🏋️‍♂️ แบบฝึกหัดไต่ระดับการเขียนจริง (จากระดับเริ่มต้น ➔ Enterprise CI/CD Migration Pattern)

🟢 Level 1: การเขียน Versioned Migration (V1__init_schema.sql) สำหรับโครงสร้างตาราง ระดับเริ่มต้น
-- db/migration/V1__init_schema.sql
-- 🎯 กฎเหล็ก: Versioned Migration ต้องขึ้นต้นด้วย V__ และรันเพียงครั้งเดียวตามลำดับถาวร

-- [1] สร้างตารางบัญชีเงินฝาก
CREATE TABLE accounts (
    account_id   BIGSERIAL PRIMARY KEY,
    account_no   VARCHAR(20) UNIQUE NOT NULL,
    balance      NUMERIC(15,2) NOT NULL DEFAULT 0.00,
    status       VARCHAR(10) NOT NULL DEFAULT 'ACTIVE',
    created_at   TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- [2] สร้างตารางบันทึกประวัติธุรกรรม
CREATE TABLE transaction_logs (
    log_id       BIGSERIAL PRIMARY KEY,
    account_id   BIGINT REFERENCES accounts(account_id),
    amount       NUMERIC(15,2) NOT NULL,
    type         VARCHAR(20) NOT NULL,
    created_at   TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
จุดที่ต้องสังเกต: เมื่อสคริปต์นี้ถูกรันบนฐานข้อมูลแล้ว Flyway จะบันทึก Checksum ลงตาราง flyway_schema_history ห้ามใครกลับมาแก้ไขไฟล์นี้อีกเด็ดขาด!
🟡 Level 2: การเขียน Repeatable Migration (R__01_stored_units.sql) สำหรับ Stored Units ระดับปานกลาง
-- db/migration/R__01_stored_units.sql
-- 🎯 กฎเหล็ก: Repeatable Migration ต้องขึ้นต้นด้วย R__ และทุกคำสั่งต้องเป็น Idempotent (รันซ้ำได้ไม่พัง)

-- [1] Stored Function แบบ Idempotent (ใช้ CREATE OR REPLACE)
CREATE OR REPLACE FUNCTION fn_get_account_balance(p_account_id BIGINT)
RETURNS NUMERIC
LANGUAGE sql
STABLE
AS $$
    SELECT balance FROM accounts WHERE account_id = p_account_id;
$$;

-- [2] Stored Procedure แบบ Idempotent พร้อม Explicit Transaction Control
CREATE OR REPLACE PROCEDURE pr_deposit_funds(
    p_account_id BIGINT,
    p_amount     NUMERIC
)
LANGUAGE plpgsql
AS $$
BEGIN
    UPDATE accounts SET balance = balance + p_amount WHERE account_id = p_account_id;
    INSERT INTO transaction_logs (account_id, amount, type) VALUES (p_account_id, p_amount, 'DEPOSIT');
    COMMIT;
END $$;
จุดที่ต้องสังเกต: เมื่อนักพัฒนาแก้ Bug ภายในฟังก์ชันนี้ใน Git แล้วสั่ง Commit ➔ Flyway จะตรวจพบว่า Checksum ของไฟล์ R__01 เปลี่ยนไป และจะรันไฟล์นี้ใหม่อัตโนมัติบนทุก Environment ทันที!

🔴 Level 3: Refactoring CI/CD Database Deployment Pipeline (เปรียบเทียบ Manual Ad-hoc vs Repeatable Pattern)

เปรียบเทียบโค้ดเดิมที่สร้างไฟล์ Versioned ใหม่ไปเรื่อยๆ จนซอร์สโค้ดกระจัดกระจาย กับโค้ดใหม่ที่จัดการผ่าน Repeatable Migration รวมศูนย์:

❌ โค้ดเดิมแบบ Flawed: สร้างไฟล์ V3__, V4__, V5__ สะสมจนกระจัดกระจาย ไฟล์ขยะสะสม & ตามหา Logic ยาก
-- ❌ หายนะของการแก้ Stored Procedure ด้วย Versioned Migration:
-- V3__update_proc_v1.sql
-- V4__update_proc_v2.sql
-- V5__fix_bug_proc.sql
-- V6__add_param_proc.sql

-- ผลลัพธ์: มีไฟล์สคริปต์สะสมเป็นร้อยๆ ไฟล์ใน Git!
-- นักพัฒนาคนใหม่เข้ามา ไม่รู้ว่าไฟล์ไหนคือ Logic ล่าสุดที่รันอยู่บน Production
-- และประวัติ Git History ของแต่ละฟังก์ชันกระจัดกระจายข้ามหลายไฟล์จน Review ไม่ได้!
วิเคราะห์ข้อเสีย: การใช้ Versioned Migration กับ Stored Code ทำให้เกิดไฟล์ DDL ขยะสะสม ทำลายหลักการ Single Source of Truth
✅ โค้ดใหม่ระดับ Production: Repeatable Migration Architecture รวมศูนย์ในไฟล์เดียว & CI/CD 100%
-- db/migration/R__02_core_financial_procedures.sql
-- 🎯 ใช้ Repeatable Migration รวม Stored Units ที่เกี่ยวข้องไว้ในไฟล์เดียว
-- เมื่อแก้ Bug ให้แก้ในไฟล์นี้ตรงๆ Flyway จะคำนวณ Checksum ใหม่และ Deploy ให้อัตโนมัติ!

-- [Statement 1] จัดการกรณีที่มีการเปลี่ยน Parameter Signature ด้วย DROP IF EXISTS
DROP PROCEDURE IF EXISTS pr_transfer_funds_enterprise(BIGINT, BIGINT, NUMERIC);

-- [Statement 2] ประกาศ Stored Procedure ฉบับล่าสุด (Idempotent 100%)
CREATE OR REPLACE PROCEDURE pr_transfer_funds_enterprise(
    p_src_acc   BIGINT,
    p_dst_acc   BIGINT,
    p_amount    NUMERIC
)
LANGUAGE plpgsql
AS $$
DECLARE
    v_src_bal NUMERIC;
BEGIN
    -- [บรรทัดที่ 1-3] ตรวจสอบยอดเงินต้นทางพร้อมล็อกแถว
    SELECT balance INTO v_src_bal FROM accounts WHERE account_id = p_src_acc FOR UPDATE;

    IF v_src_bal < p_amount THEN
        RAISE EXCEPTION 'ยอดเงินในบัญชีต้นทางไม่เพียงพอ (คงเหลือ: %, ต้องการ: %)', v_src_bal, p_amount;
    END IF;

    -- [บรรทัดที่ 4-6] ทำการหักเงินและเพิ่มเงินเชิงเซต
    UPDATE accounts SET balance = balance - p_amount WHERE account_id = p_src_acc;
    UPDATE accounts SET balance = balance + p_amount WHERE account_id = p_dst_acc;

    -- [บรรทัดที่ 7-8] บันทึก Transaction Logs
    INSERT INTO transaction_logs(account_id, amount, type) VALUES (p_src_acc, -p_amount, 'TRANSFER_OUT');
    INSERT INTO transaction_logs(account_id, amount, type) VALUES (p_dst_acc, p_amount, 'TRANSFER_IN');

    -- 🎯 [บรรทัดที่ 9] สั่ง COMMIT ปิด Transaction ภายใน Procedure
    COMMIT;
END $$;
วิเคราะห์ข้อได้เปรียบ: ซอร์สโค้ด Stored Procedure มีเพียงไฟล์เดียวใน Git ดู Git Diff และ History ได้ชัดเจน และ Flyway จัดการ Deploy ลงทุก Environment ให้อัตโนมัติเมื่อไฟล์เปลี่ยน
3

📝 คลังข้อสอบและแบบประเมินความรู้ (18 ข้อ Multiple Choices พร้อมเฉลยยิบ)

แบบทดสอบวัดระดับความเข้าใจ Day 11: Database-as-Code with Flyway / Liquibase Migration Mechanics (Versioned vs Repeatable, Schema Drift, Checksum Validation, Idempotency) และข้อสอบสะสมทบทวนครบทั้ง Days 1–10 คลิกเลือกคำตอบ ก, ข, ค, หรือ ง เพื่อดูผลลัพธ์และคำอธิบายอย่างละเอียดทันที

📊 สรุปผลการประเมิน Database-as-Code (Day 11) 0 / 18
ความคืบหน้า ตอบแล้ว 0 จาก 18 ข้อ

🎉 ยินดีด้วยครับ! คุณได้เริ่มต้นก้าวแรกของ Phase 3 (DevOps, Maintainability & Architectural Boundaries) เรียบร้อยแล้ว

บทเรียน Day 11: Database-as-Code with Flyway / Liquibase Migration Mechanics ได้ยกระดับการจัดการซอร์สโค้ดฐานข้อมูลเข้าสู่ยุค Database-as-Code โดยการแบ่งสคริปต์ออกเป็น Versioned Migrations (V__*.sql) สำหรับโครงสร้าง DDL และ Repeatable Migrations (R__*.sql) สำหรับ Stored Units (UDFs, Procedures, Triggers) ซึ่งช่วยขจัดปัญหา Schema Drift และทำให้การ Deploy สคริปต์ฐานข้อมูลเข้าสู่ระบบ CI/CD เป็นเรื่องอัตโนมัติ 100%

🚩 ก้าวต่อไปสู่ Day 12 (Automated Testing with Testcontainers & pgTAP):

ในวันถัดไป เราจะไปเจาะลึกการสร้างระบบทดสอบอัตโนมัติ (Automated Database Unit Testing) โดยการเขียน Unit Test สำหรับ Stored Units ด้วย pgTAP และการจำลอง Ephemeral Database Container สำหรับรัน Integration Tests ใน CI/CD Pipeline ด้วย Testcontainers เพื่อการันตีว่า Stored Units ทุกตัวปราศจาก Bug ก่อน Deploy ลง Production ครับ!

เปิดบทเรียน Day 12 →