go/docs/getting-started.md
Snider 89d189dd95 docs: add human-friendly documentation for Core Go framework
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 13:02:37 +00:00

191 lines
4.5 KiB
Markdown

---
title: Getting Started
description: How to create a Core application and register services.
---
# Getting Started
This guide walks you through creating a Core application, registering services, and running the lifecycle.
## Installation
```bash
go get forge.lthn.ai/core/go
```
## Creating a Core Instance
Everything starts with `core.New()`. It accepts a variadic list of `Option` functions that configure the container before it is returned.
```go
package main
import "forge.lthn.ai/core/go/pkg/core"
func main() {
c, err := core.New()
if err != nil {
panic(err)
}
_ = c // empty container, ready for use
}
```
In practice you will pass options to register services, embed assets, or lock the registry:
```go
c, err := core.New(
core.WithService(mypackage.NewService),
core.WithAssets(embeddedFS),
core.WithServiceLock(),
)
```
See [Configuration](configuration.md) for the full list of options.
## Registering a Service
Services are registered via **factory functions**. A factory receives the `*Core` and returns `(any, error)`:
```go
package greeter
import "forge.lthn.ai/core/go/pkg/core"
type Service struct {
greeting string
}
func (s *Service) Hello(name string) string {
return s.greeting + ", " + name + "!"
}
func NewService(c *core.Core) (any, error) {
return &Service{greeting: "Hello"}, nil
}
```
Register it with `WithService`:
```go
c, err := core.New(
core.WithService(greeter.NewService),
)
```
`WithService` automatically discovers the service name from the package path. In this case, the service is registered under the name `"greeter"`.
If you need to control the name explicitly, use `WithName`:
```go
c, err := core.New(
core.WithName("greet", greeter.NewService),
)
```
See [Services](services.md) for the full registration API and the `ServiceRuntime` helper.
## Retrieving a Service
Once registered, services can be retrieved by name:
```go
// Untyped retrieval (returns any)
svc := c.Service("greeter")
// Type-safe retrieval (returns error if not found or wrong type)
greet, err := core.ServiceFor[*greeter.Service](c, "greeter")
// Panicking retrieval (for init-time wiring where failure is fatal)
greet := core.MustServiceFor[*greeter.Service](c, "greeter")
```
## Running the Lifecycle
Services that implement `Startable` and/or `Stoppable` are automatically called during startup and shutdown:
```go
import "context"
// Start all Startable services (in registration order)
err := c.ServiceStartup(context.Background(), nil)
// ... application runs ...
// Stop all Stoppable services (in reverse registration order)
err = c.ServiceShutdown(context.Background())
```
See [Lifecycle](lifecycle.md) for details on the `Startable` and `Stoppable` interfaces.
## Sending Messages
Services communicate through the message bus without needing direct imports of each other:
```go
// Broadcast to all handlers (fire-and-forget)
err := c.ACTION(MyEvent{Data: "something happened"})
// Request data from the first handler that responds
result, handled, err := c.QUERY(MyQuery{Key: "setting"})
// Ask a handler to perform work
result, handled, err := c.PERFORM(MyTask{Input: "data"})
```
See [Messaging](messaging.md) for the full message bus API.
## Putting It All Together
Here is a minimal but complete application:
```go
package main
import (
"context"
"fmt"
"forge.lthn.ai/core/go/pkg/core"
"forge.lthn.ai/core/go/pkg/log"
)
func main() {
c, err := core.New(
core.WithName("log", log.NewService(log.Options{Level: log.LevelInfo})),
core.WithServiceLock(),
)
if err != nil {
panic(err)
}
// Start lifecycle
if err := c.ServiceStartup(context.Background(), nil); err != nil {
panic(err)
}
// Use services
logger := core.MustServiceFor[*log.Service](c, "log")
fmt.Println("Logger started at level:", logger.Level())
// Query the log level through the message bus
level, handled, _ := c.QUERY(log.QueryLevel{})
if handled {
fmt.Println("Log level via QUERY:", level)
}
// Clean shutdown
if err := c.ServiceShutdown(context.Background()); err != nil {
fmt.Println("shutdown error:", err)
}
}
```
## Next Steps
- [Services](services.md) -- service registration patterns in depth
- [Lifecycle](lifecycle.md) -- startup/shutdown ordering and error handling
- [Messaging](messaging.md) -- ACTION, QUERY, and PERFORM
- [Configuration](configuration.md) -- all `With*` options
- [Errors](errors.md) -- the `E()` error helper
- [Testing](testing.md) -- test conventions and helpers