12/04/2026 18:16น.

JS2GO EP.46 การสร้าง Middleware และ Modular Architecture ใน Go และ Node.js
#Node.js
#Go
#Modular Architecture
#Middleware
เขียน API ให้รันได้ = ง่าย
เขียน API ให้ดูแลง่าย ขยายง่าย และรอดใน Production = ต้องใช้สถาปัตยกรรมที่ดี
บทความนี้จะสรุปตั้งแต่พื้นฐาน → ระดับองค์กร (Enterprise)
สิ่งที่คุณจะได้เรียนรู้
✔ การสร้าง Middleware (Auth, Logging, Rate Limit)
✔ Service / Repository Pattern
✔ Modular Architecture แบบ Production-ready
✔ ตัวอย่างโค้ด Express + Fiber ที่ถูกต้อง
✔ Best Practices ที่ใช้ในบริษัทจริง
⭐ 1) Middleware คืออะไร?
Middleware = ฟังก์ชันที่ถูกเรียกก่อน (หรือหลัง) Handler
มีไว้สำหรับงานประเภท:
- ตรวจสอบ Authorization / Token
- Logging request
- Rate Limiting
- Request Transform (เช่น normalize headers)
- Validation
- ตรวจสอบสิทธิ์ของผู้ใช้
Flow (เหมือนกันทั้ง Go และ Node.js)
Request → Middleware A → Middleware B → Handler → Response
Handler ควร “บางที่สุด” เพราะงานหนักควรถูกดันไป Service Layer หรือ Middleware
⭐ 2) ตัวอย่าง Middleware ที่ถูกต้องและใช้งานได้จริง
🔹 Logging Middleware
Go (Fiber)
func Logging() fiber.Handler {
return func(c *fiber.Ctx) error {
start := time.Now()
err := c.Next()
fmt.Printf("[%s] %s - %d (%v)\n",
c.Method(),
c.Path(),
c.Response().StatusCode(),
time.Since(start),
)
return err
}
}
Register
app.Use(Logging())
Node.js (Express)
function logger(req, res, next) {
const start = Date.now();
res.on("finish", () => {
console.log(`[${req.method}] ${req.url} - ${res.statusCode} (${Date.now() - start}ms)`);
});
next();
}
app.use(logger);
🔹 Authentication Middleware
Go (Fiber)
func Auth() fiber.Handler {
return func(c *fiber.Ctx) error {
token := c.Get("Authorization")
if token == "" {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"error": "Missing Authorization header",
})
}
return c.Next()
}
}
Node.js (Express)
function auth(req, res, next) {
const token = req.headers.authorization;
if (!token) return res.status(401).json({ error: "Unauthorized" });
next();
}
app.use(auth);
🔹 Rate Limit (Basic Example สำหรับสาธิต ไม่เหมาะกับ Production)
Go
func RateLimit() fiber.Handler {
var mu sync.Mutex
var count = 0
return func(c *fiber.Ctx) error {
mu.Lock()
if count >= 100 {
mu.Unlock()
return c.Status(429).SendString("Too Many Requests")
}
count++
mu.Unlock()
return c.Next()
}
}
Node.js
let count = 0;
function rateLimit(req, res, next) {
if (count >= 100) {
return res.status(429).send("Too Many Requests");
}
count++;
next();
}
app.use(rateLimit);
➡ ใน Production ควรใช้ Redis + Token Bucket / Sliding Window
⭐ 3) Modular Architecture คืออะไร?
โครงสร้างแบบ Modular = ทุกโมดูลดูแลตัวเอง (self-contained)
ช่วยให้ระบบ:
- แก้ไขง่าย
- เพิ่มฟีเจอร์ได้โดยไม่แตะส่วนอื่น
- แยก test ได้ง่าย
- เหมาะกับระบบขนาดกลาง–ใหญ่
📌 โครงสร้างใน Go (Production-ready)
/cmd/server/main.go
/internal
/user
handler.go
service.go
repository.go
model.go
/product
handler.go
service.go
repository.go
/pkg
/database
📌 โครงสร้างใน Node.js (Express)
src/
├─ modules/
│ ├─ user/
│ │ ├─ user.controller.js
│ │ ├─ user.service.js
│ │ ├─ user.repository.js
│ │ ├─ user.model.js
│ ├─ product/
├─ middlewares/
├─ routes/
├─ database/
├─ app.js
├─ server.js
⭐ 4) Service / Repository Pattern คืออะไร?
แยกความรับผิดชอบตามลำดับชั้น:
| Layer | หน้าที่ |
|---|---|
| Handler / Controller | รับ Request → เรียก Service → ส่ง Response |
| Service | Logic ธุรกิจ เช่น Validation, Rule, Workflow |
| Repository | คุยกับ Database เท่านั้น |
ตัวอย่างใน Go (Fiber)
Handler
func (h *UserHandler) GetUser(c *fiber.Ctx) error {
id := c.Params("id")
user, err := h.Service.GetUser(c.Context(), id)
if err != nil {
return c.Status(404).JSON(fiber.Map{"error": "User not found"})
}
return c.JSON(user)
}
Service
func (s *UserService) GetUser(ctx context.Context, id string) (*User, error) {
return s.Repo.FindByID(ctx, id)
}
Repository
func (r *UserRepository) FindByID(ctx context.Context, id string) (*User, error) {
row := r.DB.QueryRow(ctx, "SELECT id, name FROM users WHERE id=$1", id)
var u User
if err := row.Scan(&u.ID, &u.Name); err != nil {
return nil, err
}
return &u, nil
}
ตัวอย่าง Node.js (Express)
Controller
export const getUser = async (req, res) => {
const user = await userService.getUser(req.params.id);
if (!user) return res.status(404).send("User not found");
res.json(user);
};
Service
export const userService = {
getUser: async (id) => userRepository.findById(id),
};
Repository
export const userRepository = {
findById: async (id) => {
const result = await pool.query(
"SELECT id, name FROM users WHERE id=$1",
[id]
);
return result.rows[0];
},
};
⭐ 5) Project Structure ที่ใช้จริงใน Production
ทั้ง Go และ Node.js ใช้ pattern แบบแยก module + service + repository เพื่อให้รองรับทีมใหญ่และระบบที่เติบโตต่อเนื่อง
⭐ 6) Best Practices สำหรับองค์กร
✔ Controller ต้อง “บาง” ไม่มี business logic
✔ Service ไม่แตะ database
✔ Repository ไม่รู้กฎของธุรกิจ
✔ อย่าทำ middleware ทำงานหนัก (เพิ่ม latency)
✔ ใช้ Dependency Injection (จะสอนใน EP.47)
✔ ตั้งชื่อไฟล์ตามหน้าที่ เช่น user.service.js ไม่ใช่ service.js
✔ Mock service/repository เพื่อเขียน unit test
📌 สรุป
เมื่อคุณใช้:
- Middleware ที่ออกแบบดี
- Modular Architecture
- Service/Repository Pattern
ระบบของคุณจะ:
🚀 ขยายง่าย
🧱 เสถียร
🔍 เทสง่าย
💼 พร้อมใช้งาน Production จริง
นี่คือเหตุผลว่าทำไมองค์กรใหญ่และซอฟต์แวร์เฮาส์มืออาชีพถึงใช้สถาปัตยกรรมแนวนี้
🔵 ตอนต่อไป: EP.47 Dependency Injection ใน Go และ Node.js
คุณจะได้เรียนรู้ว่า:
- DI ทำให้ระบบ testable ขนาดไหน
- Constructor-based Injection ใน Go
- DI Container ใน Node.js
- Mock Service/Repository อย่างไรให้เทสง่าย
- ออกแบบ Dependency Graph ของระบบอย่างถูกต้อง