Architecture Overview
The repository is structured as a small service, not as a one-file bot script. That choice matters because it gives you explicit boundaries for runtime composition, transport logic, and state persistence.
Package map
cmd/server process entrypoint
internal/config env loading, normalization, validation
internal/app composition root and runtime lifecycle
internal/bot Telegram transport, handlers, menu sync
internal/handler HTTP endpoints and webhook wiring
internal/session session store abstraction and backends
Startup sequence
At startup the process follows this order:
- Load
.envand.env.local - Parse environment variables into
config.Config - Validate semantic constraints such as mode, webhook URL, and TTLs
- Initialize the session store
- Initialize the Telegram bot service
- Build the HTTP handler and server
- Prepare the bot transport
- Start the bot transport and the HTTP server under a shared
errgroup
That ordering keeps configuration failures cheap and prevents the application from opening the HTTP listener before the bot layer is ready.
Composition root
internal/app is the composition root. It is responsible for:
- Choosing the session backend
- Building the bot service
- Building the HTTP handler
- Running the HTTP server and bot service together
- Coordinating graceful shutdown
It does not contain Telegram command logic or HTTP response shaping.
Telegram transport layer
internal/bot owns the Telegram client lifecycle and update routing. Its responsibilities include:
- Creating the
go-telegram/botclient with the correct options - Registering handlers for text commands, callback data, and inline queries
- Syncing Telegram commands via
setMyCommands - Preparing polling or webhook transport before runtime starts
- Providing a readiness probe through
GetMe()
The package also contains the current demo interaction flows. As your application grows, this package should increasingly delegate business logic outward instead of absorbing it.
HTTP surface
internal/handler exposes four concerns:
- A root endpoint with service metadata
- A liveness endpoint
- A readiness endpoint that checks Telegram connectivity
- The Telegram webhook endpoint in webhook mode
The HTTP server exists even in polling mode because operational probes still matter.
Session persistence
internal/session hides whether chat-scoped state lives in memory or Redis. The interface is intentionally narrow so the rest of the application only relies on the operations it actually uses today.
That tradeoff keeps the template simple while still allowing:
- Local development without extra infrastructure
- Durable session data when Redis is available
- Clear backend reporting in
/sessionresponses and root metadata
Shutdown model
The bot transport and HTTP server run under a shared context. When the context is canceled:
- The HTTP server receives a bounded shutdown window
- The session backend is closed
- The process exits through the shared
errgroup
This keeps shutdown logic centralized instead of scattering cleanup in multiple packages.