IoT Workshop #3: ตั้ง Project & DevOps ให้พร้อมรบ

IoT Workshop #3: ตั้ง Project & DevOps ให้พร้อมรบ

Showkhun · Workshop ·

IoT Workshop #3: ตั้ง Project & DevOps ให้พร้อมรบ

Branch: workshop/plan-03-project-setup Phase: Planning (3/3) Repo: kangana1024/iot-workshop


สวัสดีน้องๆ! พี่โชว์กลับมาแล้ว ╰(°▽°)╯

สองบทที่แล้วเราวางแผน Architecture และ Database Design กันไปแล้ว วันนี้ถึงเวลา ตั้ง project จริงๆ ซะที ก่อน code ได้ เราต้องจัดบ้านให้เรียบร้อยก่อน — Monorepo, Docker, Makefile, Git Strategy ครบหมด พอตั้งเสร็จแล้ว workshop ทุก step ที่ตามมาจะ smooth มาก มาลุยกันเลย!


ทำไมต้องตั้ง Dev Environment ให้ดี? (WHY ก่อนเสมอ)

ลองนึกภาพว่าเรากำลังจะทำอาหารกับเพื่อน 10 คน แต่ครัวยังไม่ได้จัดเลย มีด กะทะ เขียง กระจายอยู่ทั่วบ้าน บางคนใช้ gas หุงข้าว บางคนใช้ไฟฟ้า แน่นอนว่า chaos ตั้งแต่ยังไม่ได้เริ่มทำ

Dev Environment คือครัวของ developer ถ้าไม่จัดก่อน:

  • น้องแต่ละคน setup นานคนละ 2-3 ชั่วโมง (เสียเวลา workshop ไปเลย)
  • “แต่มันรันได้บน machine เรานะ!” — classic 555
  • Service versions ต่างกัน → bug ที่หาไม่เจอ

เราจะแก้ปัญหานี้ด้วย Docker Compose (ทุกคน run environment เดียวกัน 100%) + Makefile (command เดียวกันทุกคน) + Monorepo (code ทุกส่วนอยู่ที่เดียว)


สิ่งที่น้องๆ จะได้เรียนรู้

  • Monorepo Structure — จัดบ้านให้ backend + 2 frontends อยู่ด้วยกันอย่างเป็นระเบียบ
  • Docker Compose — spin up 7 services พร้อมกันในคำสั่งเดียว
  • Makefile — ทำ CLI ของตัวเองให้ทีมใช้ได้โดยไม่ต้องจำ command ยาวๆ
  • Service Configs — ตั้งค่า Mosquitto, Telegraf, Kapacitor ให้คุยกันได้
  • Git Branching Strategy — วิธีจัด branch ให้ workshop ไหลลื่น
  • CI/CD Pipeline — GitHub Actions ที่ run test อัตโนมัติทุก push

ภาพรวม Dev Environment

ก่อนลงมือ มาดูภาพรวมว่า services ทั้งหมดเชื่อมกันยังไง:

Mermaid Diagram

ดูแล้วเข้าใจเลยว่า Telegraf เป็น “พนักงานเก็บข้อมูล” ที่นั่งฟัง MQTT แล้วยก data ไปเก็บใน InfluxDB ส่วน Kapacitor เป็น “นาฬิกาปลุก” ที่คอยดู pattern แล้ว alert กลับมาที่ API ของเรา


Monorepo Structure — จัดบ้านก่อนเริ่มงาน

Monorepo คือการเอา project ทั้งหมด (backend, frontend-mobile, frontend-admin, deployments) ใส่ไว้ใน repo เดียว แทนที่จะกระจาย 4 repos

เปรียบเหมือน คอนโดรวม vs บ้านแยก — ถ้าอยู่คอนโดรวม จะใช้ lift ร่วม, ใช้ไฟส่วนกลาง, ไปมาหาสู่กันง่าย แต่ถ้าบ้านแยก 4 หลัง กว่าจะไปหากันแต่ละทีก็เหนื่อยแล้ว

Project Initialization

# Clone workshop repository
git clone https://github.com/kangana1024/iot-workshop.git
cd iot-workshop

# Create directory structure
mkdir -p backend/cmd/server
mkdir -p backend/internal/{config,handler,middleware,model,repository,service,mqtt,websocket}
mkdir -p backend/pkg/utils
mkdir -p frontend-mobile/src/{screens,components,services,stores,utils}
mkdir -p frontend-admin/src/{pages,components,hooks,services,stores,types}
mkdir -p deployments/{mosquitto,telegraf,kapacitor}
mkdir -p docs scripts

โครงสร้างที่ได้จะหน้าตาแบบนี้:

iot-workshop/
├── backend/                  # Go Fiber API
│   ├── cmd/server/
│   ├── internal/
│   │   ├── config/
│   │   ├── handler/
│   │   ├── middleware/
│   │   ├── model/
│   │   ├── repository/
│   │   ├── service/
│   │   ├── mqtt/
│   │   └── websocket/
│   └── pkg/utils/
├── frontend-mobile/          # LynxJS Mobile App
│   └── src/
├── frontend-admin/           # Vite Admin Panel
│   └── src/
├── deployments/              # Docker & Configs
│   ├── docker-compose.yml
│   ├── docker-compose.dev.yml
│   ├── mosquitto/
│   ├── telegraf/
│   └── kapacitor/
├── scripts/
├── docs/
├── Makefile                  # <=== ตัวนี้สำคัญมาก!
└── .env.example

Makefile — CLI ของทีมเรา

Makefile เปรียบเหมือน รีโมทคอนโทรล ของ project แทนที่จะต้องจำ docker compose -f deployments/docker-compose.yml -f deployments/docker-compose.dev.yml up -d ทุกครั้ง แค่พิมพ์ make dev ก็จบ!

# Makefile - ใช้จัดการ commands ทั้งหมด

.PHONY: help dev dev-up dev-down build test clean seed

help: ## Show this help
	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
		awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'

# ========== Development ==========

dev: dev-up ## Start development environment
	@echo "✅ Development environment is ready!"
	@echo "📡 API:        http://localhost:3000"
	@echo "📊 Chronograf: http://localhost:8888"
	@echo "🔌 MQTT:       localhost:1883"
	@echo "🗄️  MongoDB:    localhost:27017"

dev-up: ## Start all services
	docker compose -f deployments/docker-compose.yml \
		-f deployments/docker-compose.dev.yml up -d

dev-down: ## Stop all services
	docker compose -f deployments/docker-compose.yml \
		-f deployments/docker-compose.dev.yml down

dev-logs: ## Show logs (use SERVICE=api to filter)
	docker compose -f deployments/docker-compose.yml logs -f $(SERVICE)

dev-restart: ## Restart a specific service
	docker compose -f deployments/docker-compose.yml restart $(SERVICE)

# ========== Backend ==========

api-run: ## Run Go API locally (without Docker)
	cd backend && air

api-build: ## Build Go API binary
	cd backend && go build -o bin/server ./cmd/server

api-test: ## Run Go tests
	cd backend && go test ./... -v -cover

api-lint: ## Run Go linter
	cd backend && golangci-lint run

# ========== Frontend Mobile ==========

mobile-dev: ## Start LynxJS mobile dev server
	cd frontend-mobile && npm run dev

mobile-build: ## Build LynxJS mobile app
	cd frontend-mobile && npm run build

mobile-test: ## Run mobile tests
	cd frontend-mobile && npm test

# ========== Frontend Admin ==========

admin-dev: ## Start Vite admin dev server
	cd frontend-admin && npm run dev

admin-build: ## Build Vite admin panel
	cd frontend-admin && npm run build

admin-test: ## Run admin tests
	cd frontend-admin && npm test

# ========== Database ==========

seed: ## Seed sample data
	./scripts/seed-data.sh

db-reset: ## Reset databases (WARNING: deletes all data)
	docker compose -f deployments/docker-compose.yml exec mongodb \
		mongosh iot_workshop --eval "db.dropDatabase()"
	docker compose -f deployments/docker-compose.yml exec influxdb \
		influx -execute "DROP DATABASE iot_workshop"
	$(MAKE) seed

# ========== Utilities ==========

clean: ## Clean build artifacts
	cd backend && rm -rf bin/
	cd frontend-admin && rm -rf dist/
	cd frontend-mobile && rm -rf dist/

mqtt-sub: ## Subscribe to all MQTT topics (for debugging)
	docker compose -f deployments/docker-compose.yml exec mosquitto \
		mosquitto_sub -h localhost -t '#' -v

mqtt-pub-test: ## Publish test sensor data
	docker compose -f deployments/docker-compose.yml exec mosquitto \
		mosquitto_pub -h localhost \
		-t 'devices/sensor-test-001/telemetry' \
		-m '{"temperature":25.5,"humidity":60.2,"battery":85}'

ลองพิมพ์ make help แล้วจะเห็น menu สวยงามแบบนี้:

dev                  Start development environment
dev-up               Start all services
dev-down             Stop all services
dev-logs             Show logs (use SERVICE=api to filter)
api-run              Run Go API locally (without Docker)
api-test             Run Go tests
seed                 Seed sample data
mqtt-pub-test        Publish test sensor data
...

เปรียบเหมือนร้านอาหารที่มีเมนู ดีกว่าต้องไปถามพ่อครัวทุกครั้งว่า “วันนี้มีอะไรบ้าง” 555


Docker Compose — ทีมงาน 7 คน ทำงานพร้อมกัน

Docker Compose เปรียบเหมือน ผู้จัดการทีม ที่รู้ว่าต้องเรียก service ไหนก่อน-หลัง service ไหนต้องรอ service ไหน ใครต้องคุยกับใครผ่าน network อะไร

Main Compose File

# deployments/docker-compose.yml
version: '3.8'

services:
  # ========== Backend API ==========
  api:
    build:
      context: ../backend
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      - APP_ENV=development
      - APP_PORT=3000
      - MONGO_URI=mongodb://mongodb:27017/iot_workshop
      - INFLUX_URL=http://influxdb:8086
      - INFLUX_DB=iot_workshop
      - MQTT_BROKER=tcp://mosquitto:1883
      - JWT_SECRET=workshop-dev-secret-change-in-prod
    depends_on:
      mongodb:
        condition: service_healthy
      mosquitto:
        condition: service_started
      influxdb:
        condition: service_healthy
    networks:
      - iot-network
    restart: unless-stopped

  # ========== MongoDB ==========
  mongodb:
    image: mongo:7
    ports:
      - "27017:27017"
    volumes:
      - mongodb_data:/data/db
      - ../scripts/init-mongo.js:/docker-entrypoint-initdb.d/init.js:ro
    healthcheck:
      test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - iot-network

  # ========== MQTT Broker ==========
  mosquitto:
    image: eclipse-mosquitto:2
    ports:
      - "1883:1883"
      - "9001:9001"
    volumes:
      - ./mosquitto/mosquitto.conf:/mosquitto/config/mosquitto.conf:ro
      - mosquitto_data:/mosquitto/data
      - mosquitto_log:/mosquitto/log
    networks:
      - iot-network

  # ========== InfluxDB ==========
  influxdb:
    image: influxdb:1.8
    ports:
      - "8086:8086"
    environment:
      - INFLUXDB_DB=iot_workshop
      - INFLUXDB_HTTP_AUTH_ENABLED=false
      - INFLUXDB_REPORTING_DISABLED=true
    volumes:
      - influxdb_data:/var/lib/influxdb
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8086/ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - iot-network

  # ========== Telegraf ==========
  telegraf:
    image: telegraf:1.30
    volumes:
      - ./telegraf/telegraf.conf:/etc/telegraf/telegraf.conf:ro
    depends_on:
      - mosquitto
      - influxdb
    networks:
      - iot-network

  # ========== Chronograf ==========
  chronograf:
    image: chronograf:1.10
    ports:
      - "8888:8888"
    environment:
      - INFLUXDB_URL=http://influxdb:8086
      - KAPACITOR_URL=http://kapacitor:9092
    volumes:
      - chronograf_data:/var/lib/chronograf
    depends_on:
      - influxdb
      - kapacitor
    networks:
      - iot-network

  # ========== Kapacitor ==========
  kapacitor:
    image: kapacitor:1.7
    ports:
      - "9092:9092"
    environment:
      - KAPACITOR_HOSTNAME=kapacitor
      - KAPACITOR_INFLUXDB_0_URLS_0=http://influxdb:8086
    volumes:
      - ./kapacitor/kapacitor.conf:/etc/kapacitor/kapacitor.conf:ro
      - kapacitor_data:/var/lib/kapacitor
    depends_on:
      - influxdb
    networks:
      - iot-network

volumes:
  mongodb_data:
  mosquitto_data:
  mosquitto_log:
  influxdb_data:
  chronograf_data:
  kapacitor_data:

networks:
  iot-network:
    driver: bridge

สังเกตว่าเราใช้ depends_on + condition: service_healthy — นี่คือการบอกว่า “API จะเริ่มได้ก็ต่อเมื่อ MongoDB พร้อมแล้วจริงๆ นะ ไม่ใช่แค่ container ขึ้นมา” เหมือนบอกน้องใหม่ว่า “รอ senior deploy เสร็จก่อนนะ ค่อยเริ่มทำงาน”

Development Overrides

# deployments/docker-compose.dev.yml
version: '3.8'

services:
  api:
    build:
      target: development
    volumes:
      - ../backend:/app
    command: air -c .air.toml
    environment:
      - APP_ENV=development
      - APP_DEBUG=true

  frontend-admin:
    build:
      context: ../frontend-admin
      target: development
    ports:
      - "5173:5173"
    volumes:
      - ../frontend-admin:/app
      - /app/node_modules
    command: npm run dev -- --host 0.0.0.0
    networks:
      - iot-network

Dev override file นี้สำคัญมาก มันทำให้ air (Go hot reload) ทำงานได้ และ mount source code เข้า container ตรงๆ — แก้ code ปุ๊บ reload ปั๊บ ไม่ต้อง rebuild image ทุกครั้ง


Service Configurations — ตั้งค่าให้แต่ละ service คุยกันได้

Mosquitto MQTT Broker

MQTT Broker เปรียบเหมือน ไปรษณีย์กลาง — sensor ส่ง message มา broker รับ แล้ว forward ไปให้ subscriber ทุกคนที่สนใจ topic นั้น

# deployments/mosquitto/mosquitto.conf
listener 1883
protocol mqtt

listener 9001
protocol websockets

allow_anonymous true
persistence true
persistence_location /mosquitto/data/

log_dest stdout
log_type all
connection_messages true

Port 1883 ใช้สำหรับ native MQTT ส่วน port 9001 ใช้ WebSocket เพราะ browser ไม่สามารถ connect MQTT ตรงๆ ได้ ต้องผ่าน WebSocket ก่อน

Telegraf — ตัวรับ-ส่งข้อมูล

Telegraf คือ พนักงานสายพาน ที่คอยหยิบ message จาก MQTT แล้วแปลง JSON ไปเก็บใน InfluxDB ในรูปแบบ time-series ที่ query ได้ง่าย:

# deployments/telegraf/telegraf.conf
[global_tags]
  environment = "development"

[agent]
  interval = "10s"
  round_interval = true
  flush_interval = "10s"
  flush_jitter = "0s"

# ========== Input: MQTT Consumer ==========
[[inputs.mqtt_consumer]]
  servers = ["tcp://mosquitto:1883"]
  topics = [
    "devices/+/telemetry"
  ]
  qos = 1
  data_format = "json"
  topic_tag = "topic"

  # Extract device_id from topic
  [[inputs.mqtt_consumer.topic_parsing]]
    topic = "devices/+/telemetry"
    measurement = "measurement/_/measurement"
    tags = "_/device_id/_"

  [inputs.mqtt_consumer.json_v2]
    measurement_name = "sensor_data"
    [[inputs.mqtt_consumer.json_v2.field]]
      path = "temperature"
      type = "float"
    [[inputs.mqtt_consumer.json_v2.field]]
      path = "humidity"
      type = "float"
    [[inputs.mqtt_consumer.json_v2.field]]
      path = "pressure"
      type = "float"
    [[inputs.mqtt_consumer.json_v2.field]]
      path = "battery"
      type = "int"

# ========== Output: InfluxDB ==========
[[outputs.influxdb]]
  urls = ["http://influxdb:8086"]
  database = "iot_workshop"
  retention_policy = "raw"
  write_consistency = "any"
  timeout = "5s"

ส่วน topic_parsing เจ๋งมาก มันดึง device_id ออกจาก topic path devices/{device_id}/telemetry โดยอัตโนมัติ แล้วเก็บเป็น tag ใน InfluxDB ทำให้ query per device ได้ง่ายมาก

Kapacitor — นาฬิกาปลุก Alert

# deployments/kapacitor/kapacitor.conf
hostname = "kapacitor"
data_dir = "/var/lib/kapacitor"

[http]
  bind-address = ":9092"

[[influxdb]]
  enabled = true
  name = "default"
  default = true
  urls = ["http://influxdb:8086"]

[smtp]
  enabled = false

[[httppost]]
  endpoint = "api-webhook"
  url = "http://api:3000/api/v1/alerts/webhook"
  headers = { "Content-Type" = "application/json" }

Kapacitor เชื่อม webhook กลับมาที่ API ของเรา — เมื่อ temperature สูงเกิน threshold มันจะ POST ไปที่ /api/v1/alerts/webhook แล้ว API จะส่ง notification ต่อให้ user ผ่าน WebSocket


Environment Variables

# .env.example (root level)

# ========== App ==========
APP_ENV=development
APP_PORT=3000
APP_DEBUG=true

# ========== MongoDB ==========
MONGO_URI=mongodb://localhost:27017/iot_workshop

# ========== InfluxDB ==========
INFLUX_URL=http://localhost:8086
INFLUX_DB=iot_workshop

# ========== MQTT ==========
MQTT_BROKER=tcp://localhost:1883
MQTT_CLIENT_ID=iot-workshop-api
MQTT_QOS=1

# ========== JWT ==========
JWT_SECRET=your-secret-key-change-in-production
JWT_EXPIRY=24h
JWT_REFRESH_EXPIRY=168h

# ========== Frontend ==========
VITE_API_URL=http://localhost:3000
VITE_WS_URL=ws://localhost:3000/ws
VITE_CHRONOGRAF_URL=http://localhost:8888

อย่าลืม copy .env.example ไปเป็น .env ก่อน run — script setup ด้านล่างจะทำให้อัตโนมัติ แต่ถ้าทำเองก็แค่ cp .env.example .env


Git Branching Strategy — ถนนของ workshop

พี่โชว์ออกแบบ branch structure ให้ workshop ไหลเป็นเส้นตรง น้องๆ สามารถ checkout ทีละ step แล้วดู diff ระหว่าง step ได้เลย เหมือน ถนนที่มี milestone ชัดเจน:

main
├── workshop/plan-01-architecture       # Planning: System Architecture
├── workshop/plan-02-database-design    # Planning: Database Design
├── workshop/plan-03-project-setup      # Planning: Project Setup  <-- เราอยู่ตรงนี้
├── workshop/dev-01-fiber-bootstrap     # Backend: Go Fiber Setup
├── workshop/dev-02-mongodb-models      # Backend: MongoDB Models
├── workshop/dev-03-device-api          # Backend: Device API
├── workshop/dev-04-sensor-ingestion    # Backend: Sensor Ingestion
├── workshop/dev-05-mqtt-broker         # Backend: MQTT Integration
├── workshop/dev-06-websocket           # Backend: WebSocket
├── workshop/dev-07-tick-setup          # TICK: Stack Setup
├── workshop/dev-08-telegraf-pipeline   # TICK: Telegraf Pipeline
├── workshop/dev-09-alerting            # TICK: Kapacitor Alerting
├── workshop/dev-10-lynxjs-setup        # Mobile: LynxJS Setup
├── workshop/dev-11-lynxjs-dashboard    # Mobile: Dashboard
├── workshop/dev-12-lynxjs-control      # Mobile: Device Control
├── workshop/dev-13-lynxjs-charts       # Mobile: Charts
├── workshop/dev-14-lynxjs-alerts       # Mobile: Notifications
├── workshop/dev-15-vite-setup          # Admin: Vite Setup
├── workshop/dev-16-admin-crud          # Admin: CRUD
├── workshop/dev-17-admin-monitoring    # Admin: Monitoring
└── workshop/dev-18-admin-auth          # Admin: Auth & RBAC

Workshop Flow สำหรับน้องๆ

  1. Clone repository
  2. Checkout branch ของแต่ละ content ตามลำดับ
  3. เขียน code ตาม tutorial
  4. Compare กับ solution ใน branch
  5. Merge เข้า main เมื่อเสร็จแต่ละ step
# เริ่มต้น
git clone https://github.com/kangana1024/iot-workshop.git
cd iot-workshop

# ดู branch ทั้งหมด
git branch -a

# เริ่ม Workshop Step 1
git checkout workshop/dev-01-fiber-bootstrap

# ดู diff ระหว่าง steps — เจ๋งมาก ดูได้เลยว่า step นี้เพิ่มอะไรบ้าง
git diff workshop/dev-01-fiber-bootstrap..workshop/dev-02-mongodb-models

One-Click Setup Script

สิ่งที่เราชอบมากในฐานะ developer — script ที่ทำให้ทุกอย่าง setup ได้ด้วยคำสั่งเดียว เหมือน สูตรอาหารสำเร็จรูป แค่กด start รอนิดนึง ทุกอย่างพร้อม!

#!/bin/bash
# scripts/setup.sh - One-click development environment setup

set -e

echo "🚀 Setting up IoT Workshop Development Environment..."
echo ""

# Check prerequisites
echo "📋 Checking prerequisites..."
command -v docker >/dev/null 2>&1 || { echo "❌ Docker is required. Install: https://docs.docker.com/get-docker/"; exit 1; }
command -v docker compose >/dev/null 2>&1 || { echo "❌ Docker Compose v2 is required."; exit 1; }
command -v go >/dev/null 2>&1 || { echo "⚠️  Go is optional for local development (Docker handles it)."; }
command -v node >/dev/null 2>&1 || { echo "⚠️  Node.js is optional for local development (Docker handles it)."; }
echo "✅ Prerequisites OK"
echo ""

# Copy environment file
if [ ! -f .env ]; then
    echo "📝 Creating .env from template..."
    cp .env.example .env
    echo "✅ .env created"
fi

# Start services
echo "🐳 Starting Docker services..."
make dev-up
echo ""

# Wait for services
echo "⏳ Waiting for services to be healthy..."
sleep 10

# Seed data
echo "🌱 Seeding sample data..."
make seed
echo ""

# Summary
echo ""
echo "╔══════════════════════════════════════════════════╗"
echo "║    🎉 IoT Workshop Environment Ready!            ║"
echo "╠══════════════════════════════════════════════════╣"
echo "║                                                  ║"
echo "║  📡 API Server:    http://localhost:3000          ║"
echo "║  📊 Chronograf:    http://localhost:8888          ║"
echo "║  🔌 MQTT Broker:   localhost:1883                 ║"
echo "║  🗄️  MongoDB:       localhost:27017               ║"
echo "║  📈 InfluxDB:      localhost:8086                 ║"
echo "║                                                  ║"
echo "║  Run 'make help' to see all available commands    ║"
echo "╚══════════════════════════════════════════════════╝"

Script นี้ฉลาดตรงที่ check ก่อนว่า .env มีอยู่แล้วหรือเปล่า ถ้ายังไม่มีค่อย copy มา — ไม่ทับ config ที่น้องๆ แก้ไปแล้ว


CI/CD Pipeline — GitHub Actions ยามเฝ้าค่ายอัตโนมัติ

CI/CD คือ ยามเฝ้าค่าย ที่ทำงาน 24/7 ทุกครั้งที่ push code มา มันจะ run test อัตโนมัติ ถ้า test fail ก็จะ block merge ทันที ไม่มีใครแอบ push code พัง production ได้:

# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main, 'workshop/**']
  pull_request:
    branches: [main]

jobs:
  backend-test:
    runs-on: ubuntu-latest
    services:
      mongodb:
        image: mongo:7
        ports: ['27017:27017']
      mosquitto:
        image: eclipse-mosquitto:2
        ports: ['1883:1883']
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version: '1.22'
      - name: Run tests
        working-directory: backend
        run: go test ./... -v -cover -race

  admin-build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - name: Install & Build
        working-directory: frontend-admin
        run: |
          npm ci
          npm run build
          npm test

สังเกตว่า GitHub Actions สามารถ spin up MongoDB และ Mosquitto เป็น service ได้เลยระหว่าง test — ไม่ต้อง mock database จริงๆ test coverage ก็ดีขึ้นด้วย


สรุปสิ่งที่เราทำในบทนี้

น้องๆ เก่งมากที่อ่านมาถึงตรงนี้! (ノ◕ヮ◕)ノ*:・゚✧

ในบทนี้เราได้วางรากฐาน dev environment ครบ:

สิ่งที่ทำประโยชน์
Monorepo Structurecode ทุกส่วนอยู่ที่เดียว ง่ายต่อการ cross-reference
Docker Compose 7 servicesทุกคน environment เดียวกัน 100%
Makefilecommand กลางที่ทุกคนใช้ได้โดยไม่ต้องจำ
Service ConfigsMosquitto, Telegraf, Kapacitor คุยกันได้ตั้งแต่แรก
Git Branching Strategyworkshop flow ชัดเจน เรียนทีละ step
CI/CD PipelineGitHub Actions test อัตโนมัติทุก push

Next Step — เริ่ม Phase 2: Development!

Phase 1: Planning เสร็จแล้ว! เราวางแผนครบทั้ง Architecture, Database Design และ Project Setup

ตอนนี้ถึงเวลาลงมือ code จริงๆ แล้ว! บทต่อไปเราจะ bootstrap Go Fiber API ตั้งแต่ศูนย์ มี middleware, health check, router structure ครบถ้วน มาลุยกัน!

Navigation: