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

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

Showkhun · Workshop ·

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 อะไร — นี่คือ “แผนที่ท่อสื่อสาร” ของทั้งระบบ:

SourceTargetProtocolPortPurpose
DevicesMosquittoMQTT1883Sensor telemetry
MosquittoTelegrafMQTT1883Data pipeline
TelegrafInfluxDBHTTP8086Write time-series
InfluxDBKapacitorHTTP9092Alert evaluation
KapacitorGo APIHTTP3000Alert webhook
Go APIMongoDBTCP27017CRUD operations
Go APIInfluxDBHTTP8086Query sensor data
Go APIMosquittoMQTT1883Send commands
Mobile AppGo APIHTTP/WS3000API + Real-time
Admin PanelGo APIHTTP/WS3000API + Real-time
Admin PanelChronografHTTP8888Embedded 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?

CriteriaGo + FiberAlternatives
Performanceใกล้เคียง C/C++ สำหรับ I/O intensiveNode.js ช้ากว่า 3-5x สำหรับ concurrent connections
ConcurrencyGoroutines - lightweight (2KB stack)Thread-based models ใช้ memory มากกว่า
MQTTEclipse Paho Go client - mature & stableหลายภาษามี MQTT client แต่ Go ecosystem ดีมาก
BinarySingle binary, no runtime dependencyNode.js/Python ต้อง runtime
FiberExpress-like API, fastest Go frameworkGin/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: