Aesthetic study notes about Micro-frontends and Module Federation in Vue.js on an iPad screen

1. 🎯 ตอนที่ 42: อนาคตของเว็บระดับ Enterprise ทลายข้อจำกัดด้วย Micro-frontends

2. 📖 เปิดฉาก (The Hook)

น้องๆ ครับ ลองจินตนาการถึงโปรเจกต์เว็บแอปพลิเคชันที่บริษัทของเราพัฒนามาเป็นเวลา 3 ปีดูสิครับ จากแอปเล็กๆ น่ารัก วันนี้มันกลายเป็นอสูรกาย (Monolith) ที่มีโค้ดนับแสนบรรทัด! มีนักพัฒนา 5 ทีม (ทีมสินค้า, ทีมตะกร้า, ทีมผู้ใช้, ทีมแอดมิน, ทีมโปรโมชั่น) ต้องมาเขียนโค้ดรุมอยู่ใน Repository เดียวกัน

ผลที่ตามมาคืออะไรครับ? เวลาเรากด npm run build ทีหนึ่ง ต้องไปชงกาแฟรอ 20 นาที! เวลาจะ Merge โค้ดก็เกิด Conflict กันวุ่นวาย ทีม A อัปเดตไลบรารีตัวนึง ดันไปทำให้หน้าเว็บของทีม B พังซะงั้น นี่มันฝันร้ายของการทำโปรเจกต์สเกลใหญ่ชัดๆ!

เพื่อแก้ปัญหานี้ โลกของ Frontend Architecture จึงได้หยิบยืมคอนเซปต์ของ “Microservices” จากฝั่ง Backend มาใช้ และก่อกำเนิดสิ่งที่เรียกว่า “Micro-frontends” ครับ วันนี้พี่จะพามาจิบกาแฟ และเรียนรู้วิธีการหั่นอสูรกายตัวนี้ออกเป็นชิ้นเล็กๆ ให้แต่ละทีมดูแลโปรเจกต์ของตัวเอง แล้วจับมันมาประกอบร่างกันกลางอากาศด้วยเวทมนตร์ของ Webpack Module Federation เตรียมตัวเปิดโลกใบใหม่กันได้เลยครับ!

3. 🧠 แก่นวิชา (Core Concepts)

🧩 1. Micro-frontends คืออะไร?

มาร์ติน ฟาวเลอร์ (Martin Fowler) ปรมาจารย์ด้านสถาปัตยกรรมซอฟต์แวร์ได้นิยามไว้ว่า “Micro Frontend คือรูปแบบสถาปัตยกรรมที่แอปพลิเคชัน Frontend ถูกแบ่งออกเป็นส่วนๆ ที่สามารถพัฒนาและส่งมอบ (Deliver) ได้อย่างอิสระ จากนั้นจึงนำมาประกอบเข้าด้วยกันเป็นภาพใหญ่ภาพเดียว”

เปรียบเทียบง่ายๆ เหมือนการต่อเลโก้ครับ แทนที่เราจะให้คน 50 คนมารุมต่อเลโก้ปราสาทบนฐานเดียวกัน (Monolith) เราให้แต่ละทีมไปต่อชิ้นส่วนของตัวเอง (เช่น ทีม A ต่อประตู, ทีม B ต่อหอคอย) แล้วค่อยเอามาเสียบรวมกันในตอนจบ

🚀 2. ทำไมองค์กรใหญ่ๆ ถึงต้องใช้? (Benefits)

การใช้ Micro-frontends ในระดับ Enterprise มีข้อดีมหาศาลครับ:

  • Decoupled & Autonomous Teams: แต่ละทีมเป็นอิสระต่อกัน (Autonomous) ทีมตะกร้าสินค้าอยากใช้ Vue 3 แต่ทีมแอดมินอยากใช้ Vue 2 ก็ทำได้!
  • Smaller, Maintainable Codebases: โค้ดเล็กลง ดูแลรักษาง่าย ไม่พันกันเป็นสปาเกตตี
  • Incremental Upgrades: สามารถทยอยอัปเกรดเทคโนโลยีทีละส่วนได้ โดยไม่ต้องรื้อเขียนใหม่ทั้งระบบ (ตัวอย่างเช่น โปรเจกต์ Pinterest Clone เราสามารถแยกแอปออกเป็น Board App, Pins App, Photos App และ Users App ให้แต่ละทีมดูแลแยกกันไปเลย!)

🌐 3. Module Federation (เวทมนตร์แห่งการประกอบร่าง)

ในอดีต การทำ Micro-frontends ทำได้ยากมาก (ต้องใช้วิธี iFrame หรือโหลดผ่าน Script Tag มั่วไปหมด) จนกระทั่ง Webpack 5 ได้เปิดตัวฟีเจอร์ Module Federation มันคือระบบที่อนุญาตให้โปรเจกต์ Vue 2 โปรเจกต์ที่รันอยู่คนละ Server สามารถ “แชร์ Component” ให้กันและกันได้แบบ Real-time (ตอน Runtime) โดยที่เราไม่ต้องเอาโค้ดไป Publish ลง NPM เลยครับ!

Diagram showing Micro-frontend architecture with Module Federation in Vue.js

4. 💻 ร่ายมนต์โค้ด (Show me the Code)

ในโลกของ Module Federation เราจะมีคำศัพท์ 2 คำคือ:

  1. Remote (ผู้ให้บริการ): โปรเจกต์ที่สร้าง Component แล้วประกาศว่า “ใครอยากใช้ เอาไปเลย!”
  2. Host (ผู้บริโภค): โปรเจกต์หลักที่ทำหน้าที่ดูด Component จาก Remote มาแสดงผล

สมมติว่าเรามี 2 โปรเจกต์ Vue 3 แยกกันคนละโฟลเดอร์ คนละ Repository ครับ

Step 1: ฝั่ง Remote App (สมมติว่าเป็นทีม Header)

เราเข้าไปแก้ไฟล์ webpack.config.js (หรือ vite.config.js ถ้าใช้ Vite plugin) เพื่อประกาศส่งออก (Expose) ตัว Header.vue ครับ:

// ฝั่ง Remote (รันอยู่ที่พอร์ต 3001)
const { ModuleFederationPlugin } = require("webpack").container;

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: "header_app", // ตั้งชื่อโปรเจกต์นี้
      filename: "remoteEntry.js", // ไฟล์ลายแทงที่จะให้คนอื่นมาดึงไป
      exposes: {
        // ประกาศชิ้นส่วนที่จะเปิดให้ชาวโลกดึงไปใช้
        "./SiteHeader": "./src/components/Header.vue", 
      },
      shared: ["vue"] // 🌟 Pro-Tip: แชร์ไลบรารี Vue ร่วมกัน จะได้ไม่ต้องโหลดซ้ำซ้อน!
    })
  ]
}

Step 2: ฝั่ง Host App (สมมติว่าเป็นทีมหน้า Home หลัก)

เราตั้งค่าให้ Host รู้จักกับ Remote ก่อนครับ:

// ฝั่ง Host (รันอยู่ที่พอร์ต 3000)
const { ModuleFederationPlugin } = require("webpack").container;

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: "host_app",
      remotes: {
        // บอกว่าถ้าเจอคำว่า 'header_app' ให้วิ่งไปดึงโค้ดมาจาก URL นี้นะ!
        header_app: "header_app@http://localhost:3001/remoteEntry.js",
      },
      shared: ["vue"]
    })
  ]
}

Step 3: การเรียกใช้งานใน Component ฝั่ง Host

คราวนี้ในไฟล์ App.vue ของทีมหน้า Home เราสามารถดึง Header.vue ของอีกทีมมาใช้ได้เลยครับ ราวกับว่ามันอยู่ในเครื่องเราเอง!

<template>
  <div class="home-page">
    <!-- 2. นำ Component จากทมอื่นมาใช โดยใช Suspense ครอบไวเพราะมนตองดงขามเนตเว -->
    <Suspense>
      <template #default>
        <RemoteHeader />
      </template>
      <template #fallback>
        <div>กำลงโหลด Header จากท B... </div>
      </template>
    </Suspense>

    <main>
      <h1>นดอนรบสู่หนาหล</h1>
      <p>เนื้อหาสวนนี้ควบคมโดยท Home (Host)</p>
    </main>
  </div>
</template>

<script setup>
import { defineAsyncComponent } from 'vue'

// 1. ดึง Component ข้ามโปรเจกต์! (สังเกตชื่อ 'header_app/SiteHeader' ที่เราตั้งไว้)
const RemoteHeader = defineAsyncComponent(() => 
  import('header_app/SiteHeader')
)
</script>

ว้าว! นี่แหละครับความเจ๋ง โปรเจกต์ 2 ตัวรันแยกกัน พัฒนาแยกกัน แต่สามารถดึง Component มารวมกันได้บนหน้าจอเดียว!

5. 🛡️ เคล็ดลับจากคัมภีร์ลับ (Under the Hood / Pro-Tips)

การทำ Micro-frontends ไม่ได้มีแต่ข้อดีนะครับ มันมาพร้อมกับความท้าทายระดับโหดหินที่ Senior Dev ต้องวางแผนให้ดี:

🚨 1. ปัญหา State Management (ใครถือข้อมูล?) ถ้าทีม Header มีปุ่ม Login แล้วทีม Home จะรู้ได้ยังไงว่า Login แล้ว? กฎเหล็กของ Micro-frontends คือ “พยายามแชร์ State ระหว่างแอปให้น้อยที่สุด (Loose Coupling)” ครับ ถ้าจำเป็นจริงๆ ให้ใช้ท่า Custom Events (window.dispatchEvent) หรือส่งข้อมูลผ่าน LocalStorage/SessionStorage แทนการพยายามแชร์ Vuex/Pinia ข้ามโปรเจกต์โดยตรง เพราะมันจะทำให้โปรเจกต์ผูกติดกันจนเกินไป

🎨 2. สงคราม CSS (CSS Conflicts) ลองนึกภาพทีม A เขียน button { background: red; } ส่วนทีม B เขียน button { background: blue; } พอเอามาประกอบร่างกัน หน้าเว็บพังแน่นอน! ทางแก้: ต้องตกลงกันในองค์กรให้เข้มงวดครับ เช่น บังคับใช้ Scoped CSS ใน Vue เสมอ, ใช้ CSS Modules, หรือการตั้ง Prefix ให้ Class Name ตามชื่อทีม (เช่น .teamA-button)

📦 3. จัดการ Shared Dependencies ให้ดี ใน Module Federation เรามีคำสั่ง shared: ["vue", "pinia"] ซึ่งสำคัญมาก! สมมติทั้ง 5 ทีมใช้ Vue.js เหมือนกัน เราคงไม่อยากให้เบราว์เซอร์ของลูกค้าต้องโหลดไลบรารี Vue.js ซ้ำกัน 5 รอบจริงไหมครับ? การประกาศ Shared จะเป็นการบอก Webpack ว่า “ถ้า Host โหลด Vue มาแล้ว Remote ไม่ต้องโหลดซ้ำนะ ให้ใช้ร่วมกันเลย” ช่วยประหยัดแบนด์วิดท์ไปได้มหาศาลครับ

6. 🏁 บทสรุป (To be continued…)

การก้าวเข้าสู่สถาปัตยกรรม Micro-frontends ด้วย Module Federation ถือเป็นไม้ตายสุดท้ายสำหรับองค์กรที่กำลังเติบโตอย่างรวดเร็ว มันช่วยปลดล็อกคอขวดในการพัฒนา ลดความขัดแย้งระหว่างทีม และทำให้อายุขัยของซอฟต์แวร์ยาวนานขึ้น

อย่างไรก็ตาม สถาปัตยกรรมนี้ไม่ได้เหมาะกับทุกคนครับ (Over-engineering) หากโปรเจกต์น้องๆ ยังมีนักพัฒนาแค่ 2-3 คน การทำ Monolith ธรรมดาก็ยังคงเป็นทางเลือกที่ดีและรวดเร็วกว่า แต่ถ้าวันหนึ่งที่โค้ดเริ่มใหญ่จนเกินเยียวยา วิชา Micro-frontends นี้แหละครับที่จะช่วยกอบกู้สถานการณ์ไว้ได้!

จบกันไปแล้วครับสำหรับซีรีส์การผจญภัยในโลกของ Vue.js ตั้งแต่พื้นฐานไปจนถึงระดับ Enterprise Architecture หวังว่าคัมภีร์เหล่านี้จะช่วยติดปีกให้น้องๆ ก้าวขึ้นเป็น Senior Frontend Developer ที่แข็งแกร่งได้อย่างเต็มภาคนะครับ ลุยกันเลย!


ต้องการที่ปรึกษาและพัฒนาระบบ Web Application หรือ Frontend Architecture ให้กับธุรกิจของคุณ? ทีมงาน WP Solution พร้อมให้บริการออกแบบและพัฒนาซอฟต์แวร์แบบครบวงจร ดูรายละเอียดบริการของเราได้ที่: www.wpsolution2017.com หรือพูดคุยปรึกษาเบื้องต้นได้ที่ Line: wisit.p