CasaOS Service Layer Architecture: How Services Communicate in the Repository Pattern
CasaOS implements a repository-style service layer where a global singleton service.MyService exposes all domain services via the Repository interface, enabling direct Go method calls for synchronous operations and the CasaOS Message Bus for asynchronous event distribution.
The CasaOS service layer architecture follows a clean, modular design that separates business logic from transport concerns. At its core lies a singleton repository pattern that aggregates all services—such as system management, notifications, and peer discovery—into a single access point. This architecture supports multiple communication pathways, from simple in-process method calls to distributed event broadcasting, making it straightforward for HTTP handlers and background jobs to interact with domain logic.
Core Service Layer Design
The Repository Interface
The foundation of the service layer resides in service/service.go, which defines the Repository interface and constructs concrete service implementations. This interface acts as a service locator, exposing methods like Casa(), Notify(), System(), and Gateway() to retrieve specific service instances.
type Repository interface {
Casa() CasaService
Notify() NotifyServer
System() SystemService
Gateway() external.ManagementService
MessageBus() *codegen.ClientWithResponses
// ... additional service accessors
}
func NewService(db *gorm.DB, RuntimePath string) Repository {
return &store{
casa: NewCasaService(),
notify: NewNotifyService(db),
system: NewSystemService(),
// ... other initializations
}
}
The repository is instantiated once during application bootstrap in main/main.go:
service.MyService = service.NewService(sqliteDB, config.CommonInfo.RuntimePath)
service.Cache = cache.Init()
Concrete Service Implementations
Each domain service lives in its own file under the service/ directory and implements a thin interface:
service/system.go: ImplementsSystemServicefor hardware introspection and system control (e.g.,GetCpuPercent(), reboot/shutdown operations).service/notify.go: ImplementsNotifyServerfor managing notification logs and publishing events to the Message Bus viaSendNotify().service/casa.go: ImplementsCasaServicefor remote version checking with built-in caching viago-cache.service/connections.go: ImplementsConnectionsServicefor DB-driven CIFS mount management.
Service Communication Patterns
Direct Method Calls via Singleton
The primary communication mechanism involves direct Go method calls through the global service.MyService variable. HTTP handlers and background tasks obtain service references and invoke methods synchronously:
// Example from route/v1/system.go
need, version := version.IsNeedUpdate(service.MyService.Casa().GetCasaosVersion())
cpuPercent := service.MyService.System().GetCpuPercent()
This pattern provides type-safe, compile-time checked communication with minimal overhead.
External Gateway Integration
For operations requiring interaction with the CasaOS-Common management component, the repository exposes Gateway(), which returns an external.ManagementService. This service handles route creation, port configuration, and other system-level networking tasks:
response, err := service.MyService.Gateway().CreateRoute(&model.Route{
Path: "/v2/app",
Target: "localhost:8080",
})
Asynchronous Event Distribution
For decoupled, asynchronous communication, services utilize the CasaOS Message Bus. The NotifyServer publishes events through a generated OpenAPI client (codegen/message_bus/api.go), allowing cross-service and cross-process event distribution:
func (i *notifyServer) SendNotify(name string, message map[string]interface{}) {
response, err := MyService.MessageBus().PublishEventWithResponse(
context.Background(),
common.SERVICENAME,
name,
message,
)
// ... handle response
}
Simultaneously, the notify service maintains active WebSocket connections (WebSocketConns) to push real-time notifications to connected clients.
Shared In-Memory Caching
Services share a global cache instance (service.Cache) backed by go-cache. This enables cheap, in-process data sharing for frequently accessed, ephemeral data such as thermal zone paths and remote version information:
// In service/casa.go
Cache.Set(keyName, version, time.Minute*20)
Practical Implementation Example
The following example demonstrates initializing the service layer and utilizing multiple communication patterns:
package main
import (
"github.com/IceWhaleTech/CasaOS/service"
"github.com/IceWhaleTech/CasaOS/pkg/config"
)
func main() {
// Initialize the global repository (normally performed in main.go)
db := /* gorm DB */ nil
service.MyService = service.NewService(db, config.CommonInfo.RuntimePath)
// Direct method call: Fetch CPU utilization
cpuPct := service.MyService.System().GetCpuPercent()
println("Current CPU %:", cpuPct)
// Asynchronous event: Broadcast notification via Message Bus
msg := map[string]interface{}{
"title": "System Status",
"body": "CasaOS is operational",
}
service.MyService.Notify().SendNotify("casaos:info", msg)
}
This code performs a synchronous hardware query through SystemService and publishes an asynchronous event through NotifyServer, illustrating the dual communication models available within the architecture.
Key Source Files to Explore
Understanding the CasaOS service layer architecture requires examining these critical files:
service/service.go: Central repository definition,Repositoryinterface, and service wiring logic.service/system.go: Core system operations including hardware statistics and power management.service/notify.go: Notification handling, Message Bus publishing, and WebSocket connection management.service/casa.go: Remote version checking with caching implementation.service/connections.go: Database-backed service example for managing CIFS mounts.main/main.go: Application bootstrap that instantiatesMyServiceand initializes dependencies.route/v1/system.go: REST handlers demonstrating service consumption patterns.codegen/message_bus/api.go: Generated OpenAPI client for Message Bus communication.
Summary
- Repository Pattern: CasaOS uses a singleton
Repositoryinterface (service.MyService) to aggregate all domain services, providing a unified access point throughout the application. - Synchronous Communication: Direct Go method calls via the singleton offer type-safe, high-performance interaction between HTTP handlers and business logic.
- Asynchronous Messaging: The Message Bus client enables decoupled event distribution across services and processes, while WebSocket connections provide real-time client notifications.
- External Integration: The
Gatewayservice abstracts communication with the CasaOS-Common management component for network and routing operations. - Shared Resources: A global
go-cacheinstance (service.Cache) provides in-process caching accessible to all services.
Frequently Asked Questions
What architectural pattern does CasaOS use for its service layer?
CasaOS implements a repository-style service layer combined with the singleton pattern. The Repository interface defined in service/service.go acts as a service locator, exposing accessor methods for all concrete services. A global variable service.MyService holds the single instance initialized at startup, ensuring consistent state and resource sharing across the application.
How do HTTP handlers access business logic in CasaOS?
HTTP handlers access business logic by importing the service package and invoking methods through the global service.MyService singleton. For example, a handler might call service.MyService.System().GetCpuPercent() to retrieve hardware statistics or service.MyService.Casa().GetCasaosVersion() to check for updates. This approach keeps handlers thin and delegates all domain logic to the service layer.
What is the role of the Message Bus in CasaOS architecture?
The Message Bus provides asynchronous, decoupled communication between services and external components. Implemented via a generated OpenAPI client in codegen/message_bus/api.go, it allows services like NotifyServer to publish events using PublishEventWithResponse(). This enables cross-service notifications without direct method coupling, supporting event-driven workflows and integration with external CasaOS components.
How does CasaOS deliver real-time notifications to web clients?
Real-time notifications flow through two pathways: first, the NotifyServer publishes events to the Message Bus for system-wide distribution; second, it maintains a slice of active WebSocket connections (WebSocketConns) and broadcasts messages directly to connected clients. This dual approach ensures both internal service awareness and immediate user interface updates.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →