Developer Guide
Welcome! This document explains the architecture, code structure, and development patterns for WLEDger.
Core Architecture & Philosophy
This application is built with a minimal stack philosophy, prioritizing simplicity, robustness, and maintainability.
- The Stack:
- Go (Golang) Backend: Chosen for performance and single-binary deployment.
- htmx Frontend: Provides a modern, dynamic UI without a heavy JavaScript framework.
- SQLite Database: Embedded, server-less, and zero-setup.
- Pico.css: A class-less CSS framework for clean styling out of the box.
- The Architecture: Modular Monolith
- The application follows the Standard Go Project Layout. Instead of grouping code by technical layer (e.g. “controllers,” “models”), it’s grouped by Feature Domain (e.g. “parts,” “inventory,” “hardware”). This makes the codebase easier to navigate and scale.
Code Structure
cmd/server/main.go: The Entrypoint.- Initializes dependencies (Database, Templates, WLED Client).
- Wires up the Feature Modules.
- Starts the HTTP server.
internal/core/: Shared Utilities.errors.go: Centralized error logging and response helpers (ServerError,ClientError).templates.go: Shared template execution logic.
internal/models/: Data Structures.- Contains pure data structs like
Part,Bin,WLEDState. - Rule: This package contains no logic, only definitions.
- Contains pure data structs like
internal/store/: The Data Access Layer.- This is the only package that imports
database/sql. - It implements the interfaces defined by the features.
- Files are split by entity:
parts.go,bins.go,controllers.go.
- This is the only package that imports
internal/wled/: The Hardware Client.- Responsible for sending JSON payloads to WLED controllers.
internal/background/: Background Services.- Runs
time.Tickerloops to execute health checks and cleanup jobs at regular intervals.
- Runs
Feature Modules (internal/features/)
This is where the application logic lives. Each folder is a self-contained module:
parts/: Managing the Part Catalog, Images, URLs, Docs, and Categories.inventory/: Managing Bins and Stock levels.hardware/: Managing Controllers and WLED settings.dashboard/: The Stock Dashboard logic and “Locate” functionality.settings/: The composite Settings page view.system/: Backup, Restore, and Maintenance tasks.inspiration/: The LLM prompt generator.
Anatomy of a Feature Module: Each feature folder contains:
handler.go: Defines the HTTP handlers, routes, and the localStoreinterface it needs.handler_test.go: Contains unit tests, a localmockStore, and test setup helpers.
Data Flow Example: Locating a Part
- UI: User clicks “Locate”.
htmxsendsPOST /locate/part/1. - Router:
cmd/server/main.godirects the request todashboard.Handler. - Handler:
handleLocatePartcallsh.store.GetPartLocationsForLocate(1).
- Store:
internal/store/dashboard.goruns the SQL query joining parts, bins, and controllers.
- Handler:
- Receives the list of LEDs.
- Groups them by Controller IP.
- Calls
h.wled.SendCommand(...).
- WLED Client:
internal/wled/wled.gosends the JSON payload to the Controller.
- Response:
- The handler renders the
_locate-stop-button.htmltemplate partial. - htmx swaps the button in the browser.
- The handler renders the
Testing
The app is designed for high testability using Dependency Injection and Interface Segregation.
Note: Tests are a work in progress. If you’re a testing guru and want to contribute, send a pull request :)
- How to Run Tests:
# Run all tests (Unit + Integration) go test -v ./... - Testing Philosophy:
internal/store/*_test.go: Integration Tests. These use a real, in-memory SQLite database (:memory:). They verify that the SQL is correct and that database constraints (Foreign Keys, Unique) work as expected.internal/features/*/*_test.go: Unit Tests. These use Local Mocks defined inside the test file. They verify the HTTP logic (status codes, template rendering, error handling) without touching the database or network.