sentinel Integration
slush integrates with sentinel to provide type metadata for registered services.
What sentinel Provides
sentinel is a struct introspection library that extracts metadata from Go types:
- Field names, types, and tags
- Type relationships (references, collections, embeddings)
- Fully qualified domain names (FQDNs)
This metadata enables documentation generation, schema export, and ERD visualization.
How slush Uses sentinel
When you call Services(), slush looks up each implementation's metadata from sentinel's cache:
svcs := slush.Services()
for _, svc := range svcs {
fmt.Printf("Interface: %s\n", svc.Interface)
fmt.Printf("Impl: %s\n", svc.Impl)
fmt.Printf("Fields: %d\n", len(svc.Metadata.Fields))
}
The Pipeline
Your App sentinel slush
──────── ──────── ─────
1. Define types
type UserService struct {
db Database
}
2. Scan at startup ──────► sentinel.Scan[UserService]()
(caches metadata)
3. Register service ──────────────────────────────────► Register[UserAPI](svc)
(stores impl FQDN)
4. Enumerate ◄───────────────────────────────── Services()
sentinel.Lookup(fqdn) (returns ServiceInfo
(retrieves metadata) with Metadata)
Setup
1. Scan your types
Before registering services, scan your domain types with sentinel:
func main() {
// Scan root type - discovers related types
sentinel.Scan[UserService]()
sentinel.Scan[OrderService]()
// Now register services
slush.Register[UserAPI](userService)
slush.Register[OrderAPI](orderService)
}
2. Query metadata
Use Services() to get metadata for all registered services:
func handleSchemaEndpoint(w http.ResponseWriter, r *http.Request) {
svcs := slush.Services()
schema := make(map[string]any)
for _, svc := range svcs {
schema[svc.Interface] = map[string]any{
"impl": svc.Impl,
"fields": svc.Metadata.Fields,
"guards": svc.GuardCount,
}
}
json.NewEncoder(w).Encode(schema)
}
What slush Provides to sentinel
| sentinel Needs | slush Provides |
|---|---|
| Type FQDN for lookup | ServiceInfo.Impl (implementation FQDN) |
| Registration event | capitan SignalRegistered with FQDN |
ServiceInfo Fields
type ServiceInfo struct {
Interface string // Interface FQDN (lookup key)
Impl string // Implementation FQDN
Metadata sentinel.Metadata // From sentinel.Lookup()
GuardCount int // Number of guards
}
The Metadata field contains sentinel's extracted information:
type Metadata struct {
FQDN string
TypeName string
PackageName string
Fields []FieldMetadata
Relationships []TypeRelationship
}
Example: ERD Generation
Combine slush and sentinel for service relationship visualization:
func generateERD() string {
var buf strings.Builder
buf.WriteString("graph LR\n")
for _, svc := range slush.Services() {
// Node for service
buf.WriteString(fmt.Sprintf(" %s[%s]\n",
svc.Metadata.TypeName,
svc.Interface))
// Edges for relationships
for _, rel := range svc.Metadata.Relationships {
buf.WriteString(fmt.Sprintf(" %s --> %s\n",
svc.Metadata.TypeName,
rel.To))
}
}
return buf.String()
}
When Metadata Is Empty
ServiceInfo.Metadata will be empty if:
- Type wasn't scanned: Call
sentinel.Scan[T]()before registration - FQDN mismatch: sentinel and slush compute FQDNs the same way, but verify with
sentinel.Browse() - Interface type: sentinel scans structs, not interfaces. The impl FQDN must be a struct.
Debug with:
// What sentinel has cached
for _, fqdn := range sentinel.Browse() {
fmt.Println(fqdn)
}
// What slush is looking for
for _, svc := range slush.Services() {
fmt.Printf("Looking for: %s\n", svc.Impl)
}
Next Steps
- capitan Integration — Event emission
- API Reference — Services() documentation
- sentinel Documentation — Full sentinel guide