IoT Workshop #1: ออกแบบ Architecture ก่อนลงมือจริง

IoT Workshop #1: ออกแบบ Architecture ก่อนลงมือจริง

ShowkhunWorkshop

IoT Workshop #1: ออกแบบ Architecture ก่อนลงมือจริง

Branch: workshop/plan-01-architecture Phase: Planning (1/3) Repo: kangana1024/iot-workshop


มีโปรเจกต์หลายอันที่ล้มเหลวไม่ใช่เพราะโค้ดแย่ แต่เพราะ ไม่เคยคิดก่อนสร้าง เลย (╥_╥)

เราเลยอยากให้ Workshop ซีรีส์นี้เริ่มต้นด้วยการ “คิดก่อน code” — ออกแบบ Architecture ให้แน่น ก่อนที่น้องๆ จะเปิด IDE แม้แต่บรรทัดเดียว มาลุยกันเลย!


สิ่งที่น้องๆ จะได้จากบทความนี้

  • เข้าใจภาพรวม System Architecture แบบ Event-Driven สำหรับ IoT
  • เห็น Data Flow ทั้ง 3 แบบ: รับข้อมูล, ส่งคำสั่ง, ดึง History
  • รู้จัก Communication Patterns — REST, MQTT, WebSocket ใช้ตอนไหน
  • เข้าใจ ว่าทำไม ถึงเลือก Tech Stack แต่ละตัว (ไม่ใช่แค่ “มันดี”)
  • เห็น Deployment ทั้ง Dev และ Production

ก่อนอื่น — ทำไมต้อง Architecture First?

ลองนึกภาพว่าน้องๆ กำลังจะสร้างบ้าน แล้วเริ่มสั่งอิฐก้อนแรกโดยยังไม่มีแบบแปลน…

นั่นแหละปัญหา เพราะพอผนังขึ้นไปแล้วค่อยรู้ว่าประตูอยู่ผิดที่ มันรื้อยากมาก IoT System ก็เหมือนกัน — ถ้าไม่คิด Data Flow ไว้ก่อน พอ Sensor ส่งข้อมูลมา 1,000 เครื่องพร้อมกัน ระบบ crash แน่นอน

Architecture คือ แบบแปลน ที่บอกว่าแต่ละส่วนอยู่ที่ไหน คุยกันยังไง และมีทางออกฉุกเฉินไหม


ภาพรวมของระบบ — 6 ส่วนที่ทำงานด้วยกัน

ระบบ IoT Platform ที่เราจะสร้างในซีรีส์นี้ประกอบด้วย 6 ส่วนหลัก ทำงานร่วมกันแบบ Event-Driven Architecture:

┌──────────────────────────────────────────────────────────────────┐
│                        IoT Platform                              │
│                                                                  │
│  ┌──────────┐     ┌──────────┐     ┌──────────────────────┐     │
│  │  Devices  │────▶│  MQTT    │────▶│  Backend (Go+Fiber)  │     │
│  │ (Sensors) │     │ Broker   │     │  - REST API          │     │
│  └──────────┘     │(Mosquitto)│     │  - MQTT Handler      │     │
│       ▲           └──────────┘     │  - WebSocket Hub     │     │
│       │                │           └────────┬─────────────┘     │
│       │                │                    │                    │
│       │                ▼                    ▼                    │
│       │           ┌──────────┐     ┌──────────────────┐         │
│       │           │ Telegraf │────▶│    MongoDB        │         │
│       │           └────┬─────┘     │ (Device Registry) │         │
│       │                │           └──────────────────┘         │
│       │                ▼                    │                    │
│       │           ┌──────────┐              │                    │
│       │           │ InfluxDB │              │                    │
│       │           │(TimeSeries)│             │                    │
│       │           └────┬─────┘              │                    │
│       │                │                    │                    │
│       │                ▼                    ▼                    │
│       │           ┌──────────┐     ┌──────────────────┐         │
│       │           │Kapacitor │     │   WebSocket       │         │
│       │           │(Alerting)│     │   (Real-time)     │         │
│       │           └────┬─────┘     └──────┬───────────┘         │
│       │                │                  │                      │
│       │                ▼                  ▼                      │
│       │           ┌──────────┐     ┌──────────────────┐         │
│       └───────────│Chronograf│     │  Frontend Apps    │         │
│      (commands)   │(Dashboard)│     │  - LynxJS Mobile  │         │
│                   └──────────┘     │  - Vite Admin     │         │
│                                    └──────────────────┘         │
└──────────────────────────────────────────────────────────────────┘

ดูแล้วอาจรู้สึกว่าเยอะ แต่ไม่ต้องตกใจ — เราจะ breakdown ทีละส่วนเลย (ง่ายกว่าที่คิด!)


Data Flow: จาก Sensor ถึง Screen

อยากให้น้องๆ เข้าใจว่าข้อมูลมันเดินทางยังไง ก่อนจะมาดู code ทีหลัง

เปรียบเทียบกับชีวิตจริง: ลองนึกถึงระบบน้ำในบ้าน — น้ำออกจากประปา (Sensor) ผ่านท่อใหญ่ (MQTT) ไปถึงมิเตอร์ (Backend) แล้วค่อยกระจายไปห้องครัว ห้องน้ำ (Frontend) แต่ละจุดมีวาล์วควบคุม และมีถังเก็บน้ำสำรอง (Database) ไว้เผื่อต้องย้อนดูว่าเมื่อกี้ใช้น้ำไปเท่าไหร่

Mermaid Diagram

Flow 1: Sensor Data Ingestion (เส้นทางหลัก)

นี่คือเส้นทางที่ข้อมูล sensor วิ่งจาก device มาถึงหน้าจอ user

Sensor Device

    ▼ (MQTT Publish)
    │ Topic: devices/{device_id}/telemetry
    │ Payload: {"temp": 28.5, "humidity": 65, "ts": 1711440000}

    ├──▶ Mosquitto MQTT Broker
    │        │
    │        ├──▶ Telegraf (MQTT Consumer)
    │        │        │
    │        │        └──▶ InfluxDB (Time-Series Storage)
    │        │                  │
    │        │                  └──▶ Kapacitor (Alert Check)
    │        │                           │
    │        │                           └──▶ Webhook ──▶ Go API ──▶ Push Notification
    │        │
    │        └──▶ Go Backend (MQTT Subscriber)
    │                 │
    │                 ├──▶ Validate & Enrich Data
    │                 ├──▶ Update Device Status (MongoDB)
    │                 └──▶ Broadcast via WebSocket
    │                          │
    │                          ├──▶ LynxJS Mobile App (Real-time Dashboard)
    │                          └──▶ Vite Admin Panel (Monitoring)

สังเกตว่า Mosquitto เป็นตัวกลางที่รับข้อมูลแล้ว broadcast ไปหลายทางพร้อมกัน นี่แหละคือพลังของ Pub-Sub!

Flow 2: Device Command (เส้นทางย้อนกลับ)

น้องๆ อยากสั่งให้อุปกรณ์ทำอะไร เช่น เปิด/ปิดไฟ ก็ใช้ Flow นี้

Admin/User Action (Mobile App / Admin Panel)

    ▼ (REST API Call)
    │ POST /api/v1/devices/{id}/command
    │ Body: {"action": "toggle", "payload": {"state": "on"}}

    └──▶ Go Backend

             ├──▶ Validate Permission
             ├──▶ Log Command (MongoDB)
             └──▶ MQTT Publish
                      │ Topic: devices/{device_id}/command
                      │ Payload: {"action": "toggle", "state": "on"}

                      └──▶ Device receives command
                               └──▶ Execute action (turn on relay, etc.)

Flow 3: Historical Data Query

ดูกราฟย้อนหลัง “เมื่อคืนอุณหภูมิสูงสุดเท่าไหร่” — ใช้ Flow นี้

User requests chart data

    ▼ (REST API Call)
    │ GET /api/v1/sensors/data?device_id=xxx&range=24h

    └──▶ Go Backend

             └──▶ Query InfluxDB

                      └──▶ Return aggregated data

                               └──▶ Render chart on Mobile/Admin

Component Diagram: ข้างในแต่ละส่วนมีอะไร?

Backend Components (Go + Fiber)

Backend เราแบ่งเป็น 3 ชั้น เหมือนกับแผนก HR ในบริษัท — มี Receptionist รับงาน (Handler), มี Manager วางแผน (Service), และมี Worker ลงมือทำ (Repository)

┌──────────────────────────────────────────────┐
│              Go Fiber Application             │
│                                              │
│  ┌──────────┐  ┌──────────┐  ┌───────────┐  │
│  │  Router   │  │Middleware │  │   Config   │  │
│  │          │  │- Auth    │  │- Viper    │  │
│  │ /api/v1  │  │- CORS    │  │- .env     │  │
│  │ /ws      │  │- Logger  │  │           │  │
│  └────┬─────┘  └──────────┘  └───────────┘  │
│       │                                      │
│  ┌────▼─────────────────────────────────┐    │
│  │           Handler Layer               │    │
│  │  DeviceHandler  SensorHandler         │    │
│  │  UserHandler    AlertHandler          │    │
│  │  AuthHandler    WebSocketHandler      │    │
│  └────┬─────────────────────────────────┘    │
│       │                                      │
│  ┌────▼─────────────────────────────────┐    │
│  │           Service Layer               │    │
│  │  DeviceService   SensorService        │    │
│  │  UserService     AlertService         │    │
│  │  AuthService     MQTTService          │    │
│  └────┬─────────────────────────────────┘    │
│       │                                      │
│  ┌────▼─────────────────────────────────┐    │
│  │         Repository Layer              │    │
│  │  MongoRepo      InfluxRepo            │    │
│  │  (Devices,      (SensorData,          │    │
│  │   Users)         Metrics)             │    │
│  └──────────────────────────────────────┘    │
└──────────────────────────────────────────────┘

Service Communication Matrix

แต่ละส่วนคุยกันยังไง ใช้ Port อะไร — นี่คือ “แผนที่ท่อสื่อสาร” ของทั้งระบบ:

Source Target Protocol Port Purpose
Devices Mosquitto MQTT 1883 Sensor telemetry
Mosquitto Telegraf MQTT 1883 Data pipeline
Telegraf InfluxDB HTTP 8086 Write time-series
InfluxDB Kapacitor HTTP 9092 Alert evaluation
Kapacitor Go API HTTP 3000 Alert webhook
Go API MongoDB TCP 27017 CRUD operations
Go API InfluxDB HTTP 8086 Query sensor data
Go API Mosquitto MQTT 1883 Send commands
Mobile App Go API HTTP/WS 3000 API + Real-time
Admin Panel Go API HTTP/WS 3000 API + Real-time
Admin Panel Chronograf HTTP 8888 Embedded dashboards

Communication Patterns: เลือกอะไรใช้ตอนไหน?

นี่คือส่วนที่หลายคนสับสนมากที่สุด — REST vs MQTT vs WebSocket ต่างกันยังไง?

เปรียบเทียบกับการสื่อสารในชีวิตจริง:

  • REST = โทรศัพท์ ถามตอบทันที รอ response ก่อนแล้วค่อยทำต่อ
  • MQTT = วิทยุสื่อสาร ส่งออกอากาศ ใครสนใจ subscribe ไว้ก็รับเอง
  • WebSocket = Line Group ที่เปิดค้างไว้ แล้วใครก็ส่งข้อความเข้ามาได้ตลอด

1. Request-Response (REST API)

ใช้สำหรับ CRUD operations, queries, commands ที่ต้องการ response ทันที

Client ──── GET /api/v1/devices ────▶ Go API
Client ◀── 200 OK [{...}, {...}] ──── Go API

Endpoints หลัก:

  • GET/POST/PUT/DELETE /api/v1/devices — Device management
  • GET/POST /api/v1/sensors/data — Sensor data
  • POST /api/v1/auth/login — Authentication
  • GET /api/v1/alerts — Alert queries

2. Publish-Subscribe (MQTT)

ใช้สำหรับ device communication — lightweight, low bandwidth, reliable เหมาะกับ sensor ที่ต้องส่งข้อมูลต่อเนื่อง

Device ──── PUBLISH "devices/sensor-01/telemetry" ────▶ Mosquitto

Telegraf ◀── SUBSCRIBE "devices/+/telemetry" ──────────────┤
Go API  ◀── SUBSCRIBE "devices/+/telemetry" ───────────────┘

Topic Structure:

devices/{device_id}/telemetry    # Sensor data (device -> cloud)
devices/{device_id}/command      # Commands (cloud -> device)
devices/{device_id}/status       # Online/offline status
devices/{device_id}/config       # Configuration updates
system/alerts                    # System-wide alerts

3. WebSocket (Real-time Push)

ใช้สำหรับ push real-time data ไปยัง frontend โดยที่ client ไม่ต้อง poll ถามซ้ำๆ

Client ──── WS Upgrade /ws ──────────▶ Go API
Client ◀──── {"type":"sensor","data":{...}} ──── Go API (continuous)

WebSocket Events:

// Subscribe to device updates
{"type": "subscribe", "room": "device:sensor-01"}

// Sensor data broadcast
{"type": "sensor_data", "device_id": "sensor-01", "data": {"temp": 28.5}}

// Alert notification
{"type": "alert", "severity": "critical", "message": "Temperature exceeded 40°C"}

// Device status change
{"type": "device_status", "device_id": "sensor-01", "status": "offline"}

Tech Stack Justification: ทำไมถึงเลือกอันนี้?

เรารู้ว่าน้องๆ หลายคนอยากถาม “ใช้ Node.js ได้เหมือนกันมั้ย?” หรือ “MongoDB แทน PostgreSQL ได้มั้ย?” — ดังนั้นขอตอบด้วยข้อมูล ไม่ใช่ความรู้สึก

ทำไมเลือก Go + Fiber?

Criteria Go + Fiber Alternatives
Performance ใกล้เคียง C/C++ สำหรับ I/O intensive Node.js ช้ากว่า 3-5x สำหรับ concurrent connections
Concurrency Goroutines - lightweight (2KB stack) Thread-based models ใช้ memory มากกว่า
MQTT Eclipse Paho Go client - mature & stable หลายภาษามี MQTT client แต่ Go ecosystem ดีมาก
Binary Single binary, no runtime dependency Node.js/Python ต้อง runtime
Fiber Express-like API, fastest Go framework Gin/Echo ก็ดี แต่ Fiber ง่ายกว่าสำหรับ Express devs

IoT Backend ต้องรับ connection จาก sensor พร้อมกันนับพัน Goroutines จัดการเรื่องนี้ได้ดีมาก เพราะแต่ละ goroutine ใช้ memory แค่ 2KB (เทียบกับ thread ที่กิน 1-2MB ต่อตัว)

ทำไมเลือก MongoDB?

  • Flexible Schema: IoT devices มี metadata หลากหลาย ไม่ต้อง migrate schema ทุกครั้งที่เพิ่ม sensor รูปแบบใหม่
  • Document Model: Device config เป็น nested JSON พอดีกับ document structure
  • Aggregation Pipeline: ใช้ aggregate device statistics ได้ดี
  • Change Streams: Real-time notifications เมื่อ data เปลี่ยน

ทำไมเลือก TICK Stack?

TICK ย่อมาจาก Telegraf, InfluxDB, Chronograf, Kapacitor — ทีมเดียวกัน ออกแบบมาทำงานด้วยกัน เหมือนซื้อชุดเครื่องครัวครบเซต ไม่ต้องหาตัวต่อจากหลายยี่ห้อ

  • Time-Series Optimized: InfluxDB ออกแบบมาสำหรับ time-series data โดยเฉพาะ query เร็วกว่า MongoDB มากสำหรับข้อมูลที่มี timestamp
  • Integrated Pipeline: Telegraf → InfluxDB → Kapacitor → Chronograf ทำงานร่วมกันได้เลย
  • IoT Proven: ใช้กันแพร่หลายใน IoT industry
  • Alerting Built-in: Kapacitor มี alerting engine ในตัว ไม่ต้องเขียนเอง

ทำไมเลือก LynxJS?

  • IoT-First: ออกแบบมาสำหรับ IoT applications
  • Lightweight: เหมาะกับ real-time data display
  • Cross-Platform: Build iOS/Android จาก codebase เดียว

ทำไมเลือก Vite + React?

  • Fast DX: Vite HMR เร็วมาก feedback loop สั้นลงเยอะ
  • TypeScript: Type safety สำหรับ complex admin UI ช่วยจับ bug ก่อน runtime
  • Ecosystem: React มี component libraries ครบ (Shadcn/ui, Recharts, etc.)

Deployment Architecture

Development (Docker Compose)

ทุก service รันใน container แยก แต่คุยกันได้ผ่าน Docker network — เหมือนอพาร์ทเมนต์หลายห้องในตึกเดียว ต่างคนต่างห้อง แต่ใช้ทางเดินร่วมกัน

# docker-compose.yml overview
services:
  api:        # Go Fiber API (port 3000)
  mongodb:    # MongoDB (port 27017)
  mosquitto:  # MQTT Broker (port 1883)
  influxdb:   # InfluxDB (port 8086)
  telegraf:   # Telegraf (no exposed port)
  chronograf: # Chronograf UI (port 8888)
  kapacitor:  # Kapacitor (port 9092)

Production Considerations

พอ traffic เยอะขึ้น เราสามารถ scale Go API ออกเป็นหลาย instance โดยมี Load Balancer คอยแบ่งงาน

                    ┌─── Load Balancer (nginx/traefik) ───┐
                    │                                      │
              ┌─────▼──────┐                    ┌─────────▼───────┐
              │  Go API #1  │                    │   Go API #2      │
              │  (instance) │                    │   (instance)     │
              └──────┬──────┘                    └────────┬────────┘
                     │                                    │
              ┌──────▼────────────────────────────────────▼──────┐
              │              Shared Infrastructure                │
              │  MongoDB Replica Set | InfluxDB | Mosquitto      │
              └──────────────────────────────────────────────────┘

สรุป — เราได้อะไรจากบทความนี้?

ก่อนลงมือ code เราได้วางรากฐานไว้แล้ว:

  • System Architecture แบบ Event-Driven — รู้ว่าแต่ละส่วนอยู่ที่ไหน
  • Data Flow 3 แบบ: Ingestion (sensor → screen), Command (user → device), Query (user → history)
  • Communication Patterns: รู้ว่า REST, MQTT, WebSocket แต่ละอันใช้ตอนไหน
  • Tech Stack พร้อมเหตุผลที่เลือก ไม่ใช่แค่ “มันฮิต”
  • Deployment สำหรับทั้ง Development และ Production

เรียนรู้ WHY ก่อน HOW เสมอ แล้วตอน HOW จะง่ายขึ้นมาก (ง่าวง่าว)


Next Step

บทความหน้าเราจะลงลึกเรื่อง Database & Data Model Design — ออกแบบ Schema ของทั้ง MongoDB และ InfluxDB ให้รองรับ IoT use case จริงๆ

Navigation: