Master Plan: สร้าง IoT Platform ครบวงจร 18 step + E2E

Master Plan: สร้าง IoT Platform ครบวงจร 18 step + E2E

ShowkhunWorkshop

เคยมั้ยที่อยากสร้าง IoT system เองตั้งแต่ต้นจนจบ แต่ไม่รู้จะเริ่มจากตรงไหน? เรามาแก้ปัญหานั้นด้วยกันเลยดีกว่า workshop นี้เราจะสร้าง IoT Platform จริงๆ ครบทุก layer ตั้งแต่ sensor ส่งข้อมูลขึ้นมา ผ่าน MQTT ไปจนถึง mobile app และ admin panel — ใช่ ครบจริงๆ ไม่ใช่แค่ hello world นะ ^^ และทั้งหมดเป็น โค้ดที่รันได้จริง ที่อยู่ใน repo เดียวกัน

Repo: github.com/kangana1024/showkhun-workshop โครงสร้าง branch: ทุก step มี branch ของตัวเอง (step-01-... ถึง step-18-...) และ step-19-e2e คือ branch ที่รวมทุกอย่างไว้ครบ (รวม Playwright E2E test ของ admin)


สิ่งที่น้องๆ จะได้จาก Workshop นี้

ก่อนจะลุยกัน ขอ spoil ก่อนว่าพอจบ workshop นี้น้องๆ จะทำอะไรได้บ้าง:

  • ออกแบบ IoT architecture แบบ event-driven ได้อย่างมั่นใจ รู้ว่า service ไหนคุยกับใคร ผ่านอะไร
  • เขียน Go + Fiber backend รับ sensor data ผ่านทั้ง REST และ MQTT (ingestion path เดียวกัน)
  • เก็บ device registry/users ใน MongoDB 8 และเก็บ time-series ใน InfluxDB 2.7 อ่านกลับด้วย Flux
  • ต่อ pipeline แบบ config ล้วนด้วย Telegraf (MQTT → InfluxDB)
  • เขียน alerting engine เป็น Go เอง (threshold / offline / anomaly) — ไม่พึ่ง Kapacitor
  • สร้าง mobile app ด้วย LynxJS ดู real-time dashboard บนมือถือได้เลย
  • สร้าง admin panel ด้วย Vite + React + TypeScript จัดการ device/user/alert ทั้งหมด
  • ใส่ Authentication + RBAC ด้วย JWT (argon2id + HS256) บังคับสิทธิ์ที่ฝั่ง server
  • Run infra ทั้งหมดด้วย Docker Compose คำสั่งเดียวขึ้นหมด (make up)

Tech Stack ที่เราจะใช้ (ของจริง pin version หมด)

ลองนึกภาพ IoT system เหมือน “ระบบไปรษณีย์อัจฉริยะ” นะ:

  • อุปกรณ์ IoT = คนส่งจดหมาย (sensor ส่งข้อมูล)
  • MQTT Broker (Mosquitto) = ที่ทำการไปรษณีย์ (รับและกระจายข้อความ)
  • Go + Fiber Backend = ผู้จัดการไปรษณีย์ (validate, เก็บ, ส่งต่อ, alert)
  • MongoDB + InfluxDB = คลังเก็บจดหมาย (ต่างประเภทเก็บต่างที่)
  • LynxJS Mobile = แอปติดตามพัสดุ (ดู status real-time)
  • Vite + React Admin = dashboard ของ supervisor (ควบคุมทั้งระบบ)
Layer Technology เวอร์ชัน หน้าที่
Backend API Go + Fiber Go 1.26 / Fiber v2.52 REST API, WebSocket hub, MQTT handler
Registry DB MongoDB mongo:8.0.16 Device registry, users, groups, alert rules/history
Time-Series InfluxDB 2.7 + Flux influxdb:2.7.12 เก็บ sensor telemetry, query ด้วย Flux
Ingestion agent Telegraf telegraf:1.39.0 MQTT consumer (json_v2) → InfluxDB
Message Broker Mosquitto (MQTT) eclipse-mosquitto:2.0.22 Device ↔ cloud (pub/sub)
Alerting Go engine (ในตัว backend) threshold / offline / anomaly + webhook
Mobile App LynxJS (Rspeedy) Real-time dashboard + device control
Admin Panel Vite + React + TypeScript Device/user/alert management + monitoring
Auth JWT (HS256) + argon2id RBAC: admin / operator / viewer
Container Docker + Docker Compose Development infrastructure

เกร็ดสำคัญ: เดิมแพลนวางไว้ว่าจะใช้ TICK Stack เต็มชุด (Telegraf + InfluxDB + Chronograf + Kapacitor) แต่พอลงมือทำจริง เราเลือก เขียน alerting engine เป็น Go เอง แทน Kapacitor และไม่ได้รัน Chronograf เป็น service — เพราะ engine ที่เขียนเองประเมินได้ทันที ตอน ingest, unit-test ได้ละเอียด, และจัดการ rule ผ่าน REST + MongoDB ได้เหมือน resource อื่น. infra จริงจึงมีแค่ 4 service: MongoDB, Mosquitto, InfluxDB, Telegraf

ทำไม InfluxDB 2.7 (ไม่ใช่ 1.8 หรือ 3)

  • 2.7 มี UI / Org / Bucket / Token ในตัว provision ผ่าน DOCKER_INFLUXDB_INIT_* ได้คำสั่งเดียว เหมาะกับการสอน และยังใช้ Flux ได้เต็มที่
  • 1.8 เป็นยุค InfluxQL + retention policy + continuous query แบบเก่า — เราไม่ใช้แล้ว (workshop นี้ออกแบบรอบใหม่ทั้งหมดบน 2.7)
  • 3.x rewrite ใหม่ (Apache Arrow/DataFusion), ตัด Flux ออก, ไม่มี Chronograf/Kapacitor — เหมาะ edge/production บางเคสมากกว่าการสอนพื้นฐาน

ภาพรวม Architecture ของทั้งระบบ

มาดูกันก่อนว่าข้อมูลจาก sensor มันเดินทางยังไง ตั้งแต่วัดอุณหภูมิจนถึงโชว์บนหน้าจอมือถือ — สังเกตว่า telemetry เข้ามาได้ 2 ทาง (REST และ MQTT) แล้ววิ่งผ่าน ingestion path เดียวกัน:

graph LR
    A[🌡️ Sensors] -->|MQTT 1883| B[📡 Mosquitto]
    A -->|REST 3000| C[🖥️ Go + Fiber]
    B -->|subscribe| C
    B -->|subscribe| T[🔁 Telegraf]
    C -->|registry CRUD| M[(🍃 MongoDB 8)]
    C -->|write sensor_data| I[(📈 InfluxDB 2.7)]
    T -->|write telegraf_sensor_data| I
    C -->|Flux query| I
    C -->|WebSocket fan-out| F[📱 Mobile + 🖥️ Admin]
    C -->|alert engine| W[🔔 Webhook]

ข้อมูลไหลแบบนี้แหละ — sensor ยิงขึ้นมาทาง MQTT หรือ REST แล้ว backend รับไป validate กับ registry (MongoDB), เขียน time-series ลง InfluxDB, fan-out ไป frontend แบบ real-time ผ่าน WebSocket และ run alert engine ไปพร้อมกัน ส่วน Telegraf ก็เป็น pipeline แบบ config ล้วนที่วิ่งคู่ขนาน เขียน measurement ของตัวเองแยกไว้ (กัน double-write) สวยงามมาก (งง) ฮ่าๆ แต่ไม่ต้องกังวล เราจะค่อยๆ build ทีละ step กัน


โครงสร้าง Monorepo (ของจริง)

showkhun-workshop/
├── backend/                      # Go + Fiber API server
│   ├── cmd/server/               # main entry point + graceful shutdown
│   ├── internal/
│   │   ├── config/               # โหลด config จาก env ด้วย Viper
│   │   ├── logger/               # zap structured logger
│   │   ├── middleware/           # request logger
│   │   ├── health/               # /healthz (dependency checks) + /api/v1/ping
│   │   ├── database/             # MongoDB + InfluxDB connection/setup/query
│   │   ├── model/                # domain entities (Device, User, Group, Alert)
│   │   ├── validate/             # shared validator
│   │   ├── repository/           # interfaces + MongoDB impl (mongo/)
│   │   ├── service/              # business logic + alert engine + tests
│   │   ├── handler/              # thin HTTP handlers
│   │   ├── auth/                 # JWT, argon2id, RBAC middleware
│   │   ├── httpx/                # JSON error/list envelope helpers
│   │   ├── ratelimit/            # per-device token-bucket rate limiter
│   │   ├── tsquery/              # bounded, parameterised Flux query builder
│   │   ├── notify/               # webhook notifier (Slack-compatible)
│   │   ├── mqtt/                 # Paho client: telemetry sub + command pub
│   │   ├── ws/                   # WebSocket hub (rooms, fan-out, heartbeat)
│   │   └── server/               # ประกอบ Fiber app + middleware + routes
│   ├── Dockerfile                # multi-stage build → distroless
│   ├── .env.example
│   ├── go.mod
│   └── go.sum
├── frontend-mobile/              # LynxJS mobile app (Rspeedy)
│   └── src/
│       ├── screens/              # Dashboard, Devices, Alerts, Settings
│       ├── navigation/           # tab bar
│       ├── ui/                   # Card, Button, Badge, Dialog ...
│       ├── api/                  # client + realtime (WebSocket)
│       └── theme/
├── frontend-admin/               # Vite + React + TypeScript admin panel
│   └── src/
│       ├── pages/                # Login, Overview, Devices, AlertRules, ...
│       ├── features/             # devices, alertRules, monitoring, users
│       ├── components/           # DataTable, layout, ui
│       ├── api/                  # client, endpoints, realtime, types
│       ├── stores/               # auth, toast, confirm
│       └── routes/               # router + ProtectedRoute
├── infra/                        # Infrastructure (development)
│   ├── docker-compose.yml        # MongoDB, Mosquitto, InfluxDB, Telegraf
│   ├── mosquitto/mosquitto.conf  # broker config (anonymous, dev only)
│   ├── telegraf/telegraf.conf    # MQTT consumer (json_v2) → InfluxDB 2.x
│   └── .env.example
├── e2e/                          # Playwright E2E tests (admin)
│   ├── tests/admin.spec.ts
│   └── playwright.config.ts
├── Makefile                      # คำสั่งสำหรับงานที่ใช้บ่อย
├── .gitignore
└── README.md

เรื่องโครงสร้าง เราเลือก Monorepo เพราะ workshop นี้ต้องการให้น้องๆ เห็นภาพรวมทั้งหมดในที่เดียว เปิด editor ครั้งเดียวเห็นทุก service เลย — backend, mobile, admin, infra อยู่ครบ ไม่ต้อง clone หลาย repo


Roadmap: 18 Step + E2E (4 ช่วงงาน)

( •_•)
( •_•)>⌐■-■
(⌐■-■)  ← เตรียมตัวลุยกันได้เลย!

ก่อนเริ่ม แอบบอกว่าโรดแมปนี้ map กับ branch จริงในรีโป — แต่ละ post จะอ้างถึง branch ของมัน (step-NN-...) และ branch step-19-e2e คือตัวรวมสุดท้ายที่มีทุกอย่างครบ

Phase 1: Planning (3 บท) — วางรากฐานให้แน่น

ก่อนจะเขียน code สักบรรทัด เราต้องออกแบบก่อน เหมือนสร้างบ้านต้องมีแปลนก่อนสร้าง ไม่งั้นสร้างไปก็รื้อทิ้งทีหลัง

# Post เนื้อหา
1 System Architecture event-driven architecture, data flow (ingest/command/query), communication patterns
2 Database & Data Model MongoDB collections + InfluxDB 2.7 buckets/measurements
3 Project Setup & DevOps monorepo, Docker Compose, Makefile, env, git strategy

Phase 2: Backend — Go + Fiber + Data Layer + Alerting (9 step)

Backend ก็เหมือนครัวของร้านอาหาร ลูกค้า (frontend) ไม่เห็น แต่ถ้าครัวพัง ทุกอย่างพัง เราจะสร้างครัวที่แน่นมากๆ

# Branch Post เนื้อหา
4 step-01-fiber-bootstrap Go Fiber Bootstrap project structure, middleware, Viper config, health check, graceful shutdown
5 step-02-mongodb-models MongoDB Models & Repository domain models, repository pattern, BSON, indexes, pooling
6 step-03-device-api Device Management API CRUD + bulk + filter/sort/pagination, ingestion token
7 step-04-sensor-ingestion Sensor Ingestion REST ingest → InfluxDB, validate, registry tags, rate limit
8 step-05-mqtt-broker MQTT Integration Paho client, topic structure, QoS, auto-reconnect, commands
9 step-06-websocket WebSocket Real-time Fiber WS, hub pattern, rooms, fan-out, heartbeat
10 step-07-influx-setup InfluxDB 2.7 + Telegraf Setup org/bucket/token, retention, downsample bucket
11 step-08-telegraf-pipeline Telegraf Pipeline + Flux mqtt_consumer + json_v2 + processors → influxdb_v2, Flux read
12 step-09-alerting Go Alerting Engine threshold/offline/anomaly, cooldown, history (TTL), webhook

Phase 3: Mobile Frontend — LynxJS (5 step)

นี่คือส่วนที่น้องๆ จะโชว์คนอื่นได้ “เฮ้ ดูนี่สิ app IoT ที่ฉันสร้างเอง!” บน mobile จริงๆ เลย

# Branch Post เนื้อหา
13 step-10-lynxjs-setup LynxJS App Setup tab navigation, base components, theme, API layer
14 step-11-lynxjs-dashboard Real-time Dashboard live sensor cards, WebSocket manager, pull-to-refresh
15 step-12-lynxjs-control Device Control UI device list, search/filter, command controls
16 step-13-lynxjs-charts Data Visualization line chart, range selector, CSV export
17 step-14-lynxjs-alerts Notifications & Alerts alert list, severity badges, notification preferences

Phase 4: Admin Panel — Vite + React (4 step)

Admin panel คือ “ห้องควบคุม” ที่ผู้ดูแลระบบใช้ manage ทุกอย่าง ตั้งแต่ device ยันสิทธิ์การเข้าถึง

# Branch Post เนื้อหา
18 step-15-vite-setup Vite Admin Setup Vite + React + TS, Tailwind, React Router, Zustand, Axios
19 step-16-admin-crud Admin CRUD DataTable, React Hook Form + Zod, toast, confirm, bulk
20 step-17-admin-monitoring Monitoring Dashboard overview cards, device status grid, alert overview, real-time
21 step-18-admin-auth Authentication & RBAC JWT (Go + React), protected routes, admin/operator/viewer

แล้ว step-19-e2e ล่ะ? เป็น branch สุดท้ายที่รวมทุก step เข้าด้วยกัน แล้วเพิ่ม Playwright E2E test ของ admin (login → overview → device CRUD → alert rule → monitoring) เพื่อ verify ว่าทั้งระบบทำงานต่อกันได้จริง ไม่ใช่แค่แยกชิ้นผ่าน — นี่คือ branch ที่เราใช้อ้างอิงโค้ดจริงในทุก post


Checklist ติดตามความคืบหน้า

Phase 1: Planning

  • System Architecture Design
  • Database & Data Model Design
  • Project Setup & DevOps

Phase 2: Backend (Go + Fiber + Data + Alerting)

  • step-01 Go Fiber Bootstrap
  • step-02 MongoDB Models & Repository
  • step-03 Device Management API
  • step-04 Sensor Data Ingestion
  • step-05 MQTT Integration
  • step-06 WebSocket Real-time
  • step-07 InfluxDB 2.7 + Telegraf Setup
  • step-08 Telegraf Pipeline + Flux Query
  • step-09 Go Alerting Engine

Phase 3: Mobile (LynxJS)

  • step-10 LynxJS App Setup
  • step-11 Real-time Dashboard
  • step-12 Device Control UI
  • step-13 Data Visualization
  • step-14 Notifications & Alerts

Phase 4: Admin (Vite + React)

  • step-15 Vite Admin Setup
  • step-16 Admin CRUD Operations
  • step-17 Monitoring Dashboard
  • step-18 Authentication & RBAC

Integration

  • step-19 End-to-End (Playwright) — ทั้งระบบทำงานต่อกันได้จริง

พอร์ตที่ใช้ (ของจริง)

Service Port หมายเหตุ
Backend API 3000 REST + WebSocket ที่ /api/v1/ws
MongoDB 27017
Mosquitto (MQTT) 1883 + WebSocket ที่ 9001
InfluxDB 8086 UI + HTTP API

⚠️ ค่า default ทั้งหมด (รหัสผ่าน Mongo/InfluxDB, InfluxDB token, anonymous MQTT) ตั้งไว้เพื่อความสะดวกใน local เท่านั้น ห้ามนำไป production และ API auth (JWT + RBAC) เปิดเป็น default — ต้องตั้ง APP_AUTH_JWT_SECRET (อย่างน้อย 32 bytes) มิฉะนั้น backend จะไม่ยอม start


สรุปภาพรวม

Workshop นี้ครอบคลุม 18 step + E2E ใน monorepo เดียว พอเดินครบ น้องๆ จะมี IoT Platform ที่ทำงานได้จริง — รับ telemetry ได้ทั้ง REST/MQTT, เก็บ time-series ใน InfluxDB 2.7, แจ้งเตือนอัตโนมัติด้วย Go engine, มี mobile app ดู sensor real-time และ admin panel ที่ล็อกสิทธิ์ด้วย JWT/RBAC

สิ่งสำคัญที่สุดที่เราอยากให้น้องๆ เข้าใจจาก workshop นี้ไม่ใช่แค่ code แต่คือ วิธีคิดการออกแบบระบบ ว่า service ต่างๆ ควรแบ่งหน้าที่ยังไง ข้อมูลควรไหลผ่านตรงไหน และเลือก technology ตามโจทย์ ไม่ใช่ตาม trend (อย่างที่เราเลือกเขียน alert engine เองแทน Kapacitor นั่นแหละ)


Next: #1 System Architecture Design