FastyGO Framework Developer Guide (Phase 0)
This guide describes the current implemented architecture in English, focused on onboarding a developer quickly.
What this repository is
This project is a Phase 0 dashboard skeleton (zero domain logic), including:
- UI8Kit-powered dashboard shell (
Shell, sidebar, mobile sheet) - SSR via
a-h/templ - Theme toggle and locale switcher
- CQRS request flow
- Typed feature composition
- Embedded JSON fixtures for i18n (
en,ru)
The goal of Phase 0 is to provide a fast, stable baseline you can copy for new projects.
Implemented Architecture
1) Composition Root
cmd/server/main.go is the composition root:
- loads configuration from environment variables
- creates a CQRS dispatcher
- registers pipeline behaviors:
- validation behavior
- logging behavior
- registers handlers/features
- builds the app and starts HTTP server with graceful shutdown
This keeps bootstrapping and wiring in one place.
2) App Kernel
pkg/app provides the host application abstraction:
config.go- loads runtime settings (
APP_BIND,APP_STATIC_DIR,APP_DEFAULT_LOCALE,APP_AVAILABLE_LOCALES,APP_DATA_SOURCE) - validates locale list
- loads runtime settings (
feature.goFeatureinterface:ID() stringRoutes(*http.ServeMux)NavItems() []NavItem
NavItemmodel:Label,Path,Icon,Order
builder.goAppBuilderAPI:New(cfg).WithFeature(feature).Build().Run(ctx)
- collects nav items from all features and sorts them
- builds middleware chain + static file handler (
/static/...)
3) Web Platform Layer
pkg/web contains shared HTTP/templ utilities:
middleware/chain.go- middleware chain composition utilitymiddleware/request_id.go- assigns request id and passes via context/headermiddleware/recover.go- panic recoverymiddleware/logger.go- structured request logging and response meta (status,duration_ms,size)render.go-templrender helper to HTTP responseerror_handler.go- mapsDomainErrorto HTTP responsepage.go- shared page DTOs used by views
4) CQRS Core (lightweight)
Implemented in pkg/core/cqrs:
command.go- marker interfaces and handler interfaces:Command,Query,CommandHandler,QueryHandler
behavior.go-PipelineBehaviorinterfacedispatcher.go- registers and dispatches handlersbehaviors/validation.go- optionalValidate() errorhookbehaviors/logging.go- logs request lifecycle with duration
Important behavior:
cqrs.RegisterQuery(...)registers typed query handlerscqrs.DispatchQuery(ctx, dispatcher, query)executes pipeline and handler
This is intentionally minimal but fully wired and used in runtime.
5) View Layer (templ)
internal/site/web/views/ contains SSR templates:
layout.templ- wraps all pages into
ui8layout.Shell - provides header actions and nav
- wraps all pages into
welcome.templ- greeting page with
ui.Title,ui.Text,ui.Button
- greeting page with
nav.templ- inserts JS (
/static/js/app-shell.js) into shell head
- inserts JS (
models.go- strongly typed data models for templates
partials/language_toggle.templ- language switch control data attributes for browser script
Generated files (*_templ.go) are produced by templ generate.
6) I18N / Content layer
internal/site/web/i18n/ contains embedded content:
en/common.jsonen/welcome.jsonru/common.jsonru/welcome.json
internal/site/web/i18n/embed.go:
- uses
go:embedfor JSON files - decodes locale-specific content by section
- provides:
Load(locale)for full page payloadLocales()list
Business data (Phase 0) is currently static content, not external service-based.
7) Frontend behavior layer
internal/site/web/static/js/app-shell.js handles:
- persisted theme toggle (
dark/light) inlocalStorage - locale preference persistence in
localStorage - redirect-on-load behavior to align browser/local stored/default locale
- click-to-switch locale handler
internal/site/web/static/css/input.css imports Tailwind and UI8Kit styles.
Business logic implemented in this phase
Phase 0 has a single feature and one public interaction:
Welcome Feature (internal/infra/features/welcome, internal/application/welcome)
WelcomeQuery+WelcomeQueryHandler(internal/application/welcome/handler.go)- loads data from fixtures
- validates locale
welcome.Module(internal/infra/features/welcome/module.go)- registers route
/ - provides one nav item
Welcome - resolves effective locale (from query string or defaults)
- maps
locale->WelcomePageDataand rendersviews.Layout + views.WelcomePage
- registers route
- No external integrations, databases, or authentication logic in this phase
Current business-level capabilities:
- render welcome page from fixture content
- switch language UI + URL (
?lang=...) + persistence - theme persistence
- navigation rendering from feature nav metadata
Request and rendering flow
- HTTP request enters
AppBuilderchain - Middleware chain runs:
- request id
- recover
- logger
- Route handler (
welcome.Module) resolves locale and dispatchesWelcomeQuery Dispatcherexecutes behaviors and query handler- Fixtures are loaded for the locale
- Data is passed to
views.Layout + views.WelcomePage templrenders HTML response
Runtime sequence (important for debugging)
GET /withlangpresent -> uses that localeGET /withoutlang-> falls back toAPP_DEFAULT_LOCALE(configured default)- Locale toggle click -> updates URL via
?lang=<next>(or removes for default locale) and re-renders page - Theme button -> updates
html.darkclass and persists preference
Directory map
cmd/server/main.go– app composition and runpkg/core– domain primitives (Entity,DomainError), CQRSpkg/app– app builder/config/feature contractspkg/web– middleware, render and error helpersinternal/application/welcome– use-case/query-handler layerinternal/infra/features/welcome– HTTP/templ adapterinternal/site/web/views– templ views and partialsinternal/site/web/i18n– embedded JSON i18n contentinternal/site/web/static/css– Tailwind + UI8Kit CSS pipelineinternal/site/web/static/js/app-shell.js– theme/locale behaviorscripts/sync-ui8kit-css.sh– copies UI8Kit CSS from module cachedocs/QUICKSTART.md– quick run instructions
Environment variables
APP_BIND(default:127.0.0.1:8080)APP_STATIC_DIR(default:internal/site/web/static)APP_DEFAULT_LOCALE(default:en)APP_AVAILABLE_LOCALES(default:en,ru)APP_DATA_SOURCE(default:fixture)
Adding a new feature (recommended)
- Create
internal/application/<name>/andinternal/infra/features/<name>/ - Add:
- feature module implementing
pkg/app.Feature - query/handler pair if you need CQRS style flow
- feature module implementing
- Register feature in
cmd/server/main.go:WithFeature(newFeature)
- Add templates under
internal/site/web/views/and wire model DTOs - Add fixtures or dedicated data source integration
Because AppBuilder composes NavItems automatically, each feature contributes its own menu items.
Commands
- Install dependencies:
npm installgo mod download
- Sync UI8Kit styles:
npm run sync:ui8kit
- Dev run:
npm run build:cssgo run github.com/a-h/templ/cmd/templ@v0.3.1001 generatego run ./cmd/server- or
make dev(if available)
- Build:
make buildand run./bin/framework
- Tests:
go test ./...
- Lint + architecture checks:
make lint(runs tests and no-root import check)make ci(same as CI pipeline command)make lint-ci(same asmake ci, alias)- Windows fallback without
make:go test ./...go run ./scripts/check-no-root-imports.go
- CSS watch:
npm run dev:css
CI and lint checks
scripts/check-no-root-imports.govalidates package import boundaries:- It parses all Go files with AST and forbids imports from:
github.com/fastygo/framework/internal/featuresgithub.com/fastygo/framework/internal/site/featuresgithub.com/fastygo/framework/viewsgithub.com/fastygo/framework/fixtures
- If a forbidden import is found, the checker exits with code
1and prints file + line.
- It parses all Go files with AST and forbids imports from:
.github/workflows/no-root-imports.ymlruns this check in CI:- Triggered on
pushtomainand pull requests. - Executes
make ci, which resolves to:make lint-cimake lintgo test ./...go run ./scripts/check-no-root-imports.go
- Triggered on
- On environments without
make, run the same check locally with:
go test ./...
go run ./scripts/check-no-root-imports.go
- Docs site checks are covered by the same root targets because the same Makefile and script set is used.
Troubleshooting
- Language button does nothing:
- ensure latest JS is loaded (
Ctrl+F5) - ensure
?langquery param appears in URL after click
- ensure latest JS is loaded (
- Port in use (
bind: ... Only one usage ...):- stop old process on same port and rerun
- Need to inspect generated template output:
- run
go run github.com/a-h/templ/cmd/templ@v0.3.1001 generate
- run
Notes
This is a baseline implementation. It intentionally avoids adding business complexity in Phase 0 so teams can use this as a fast scaffold and extend with:
- real domains
- repositories / persistence
- auth
- validation layers
- richer eventing and background jobs