# CasaOS Service Layer Architecture: How the Repository Pattern Works and How to Extend It

> Explore the CasaOS service layer architecture and the repository pattern. Learn how to extend services with its flexible, six-step registration process for loose coupling and easy integration.

- Repository: [IceWhale/CasaOS](https://github.com/IceWhaleTech/CasaOS)
- Tags: architecture
- Published: 2026-06-27

---

**CasaOS uses a repository-style service layer where a global `Repository` interface exposes feature-specific services through a centralized `store` struct, enabling loose coupling and easy extension via a six-step registration process.**

The CasaOS service layer organizes all core business logic behind a clean abstraction that separates interface definitions from concrete implementations. This architecture, defined primarily in [`service/service.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/service.go), allows developers to add new capabilities by implementing small, focused interfaces and wiring them into the global `MyService` accessor. Understanding this pattern is essential for anyone contributing to the CasaOS codebase or building extensions that integrate with its system, storage, or notification features.

## Global Service Accessor

CasaOS exposes all services through a single global variable declared in [`service/service.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/service.go).

```go
var MyService Repository

```

This global acts as the single entry point for the entire application. Any package can retrieve a service using `MyService.<Feature>()`, such as `MyService.System().GetDeviceInfo()` (see [`service/system.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/system.go) lines 72-78) or `MyService.Notify().SendNotification()`. The pattern eliminates circular dependencies while maintaining discoverability.

## Repository Interface Design

The `Repository` interface defines the contract for accessing all domain services. Located in [`service/service.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/service.go), it exposes methods that return service-specific interfaces rather than concrete types.

```go
type Repository interface {
    Casa() CasaService
    Connections() ConnectionsService
    Gateway() external.ManagementService
    Health() HealthService
    Notify() NotifyServer
    Rely() RelyService
    Shares() SharesService
    System() SystemService
    Storage() StorageService
    MessageBus() *message_bus.ClientWithResponses
    Peer() PeerService
    Other() OtherService
}

```

Each method returns an interface that abstracts the underlying implementation, allowing the concrete `store` to change without affecting consumers.

## Concrete Store Implementation

The private `store` struct in [`service/service.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/service.go) holds the actual service implementations. It aggregates dependencies like the database connection and individual service instances.

```go
type store struct {
    peer        PeerService
    db          *gorm.DB
    casa        CasaService
    notify      NotifyServer
    rely        RelyService
    system      SystemService
    shares      SharesService
    connections ConnectionsService
    gateway     external.ManagementService
    storage     StorageService
    health      HealthService
    other       OtherService
}

```

The `NewService(db *gorm.DB, RuntimePath string)` factory function initializes this struct (lines 48-66), injecting dependencies into each service constructor before returning the populated `store` as a `Repository` interface.

## Service Implementation Patterns

Each feature lives in its own file and implements a minimal interface. **CasaService** ([`service/casa.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/casa.go), lines 13-15) provides simple read-only version information. **SystemService** ([`service/system.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/system.go), lines 37-69) demonstrates DB-free logic with hardware queries and shell helpers. **NotifyServer** ([`service/notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/notify.go), lines 25-39) shows a DB-backed service with WebSocket broadcasting capabilities.

Services access shared resources like the global cache (`Cache`) or configuration (`config.*`) without exposing these internals through their public interfaces.

## Message Bus Integration

The `MessageBus()` method lazily initializes a client using CasaOS-Common utilities ([`service/service.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/service.go) lines 29-46). Services publish events like `casaos:file:operate` (see [`service/notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/notify.go) lines 60-70) without direct coupling to the bus implementation. This allows asynchronous communication between features while maintaining the service layer's clean boundaries.

## How to Extend the Service Layer

Adding new functionality to CasaOS follows a six-step registration process that maintains the architectural integrity of the repository pattern.

### Step 1: Define the Interface

Create a new file like [`service/myfeature.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/myfeature.go) and define the public API.

```go
type MyFeatureService interface {
    Ping() string
    DoSomething(arg string) error
}

```

### Step 2: Implement the Service

Create a struct that implements the interface, accepting only required dependencies.

```go
type myFeature struct {
    db *gorm.DB
}

func (m *myFeature) Ping() string { return "pong" }

func (m *myFeature) DoSomething(arg string) error {
    // Business logic here
    return nil
}

func NewMyFeatureService(db *gorm.DB) MyFeatureService {
    return &myFeature{db: db}
}

```

### Step 3: Update the Repository Interface

Add a getter method to the `Repository` interface in [`service/service.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/service.go).

```go
type Repository interface {
    // ... existing methods ...
    MyFeature() MyFeatureService
}

```

### Step 4: Add Field to the Store

Add a field to the private `store` struct to hold the implementation.

```go
type store struct {
    // ... existing fields ...
    myFeature MyFeatureService
}

```

### Step 5: Initialize in NewService

Wire the constructor call inside the `NewService` factory function.

```go
return &store{
    // ... existing initializations ...
    myFeature: NewMyFeatureService(db),
}

```

### Step 6: Expose the Getter

Add the accessor method to the `store` struct.

```go
func (s *store) MyFeature() MyFeatureService { return s.myFeature }

```

After these changes, any code can call `MyService.MyFeature().Ping()` to access the new functionality.

## Testing Custom Services

Because services are accessed via interfaces, you can inject mocks for unit testing without modifying production code.

```go
type mockFeature struct{}

func (m *mockFeature) Ping() string                  { return "mock-pong" }
func (m *mockFeature) DoSomething(arg string) error   { return nil }

// In test setup:
MyService = &store{myFeature: &mockFeature{}}

```

This approach allows testing controllers and handlers in isolation from database or hardware dependencies.

## Key Source Files

| File | Purpose |
|------|---------|
| [`service/service.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/service.go) | Defines `Repository`, `MyService` global, and `NewService` factory |
| [`service/casa.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/casa.go) | Simple read-only service example |
| [`service/system.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/system.go) | Complex service with hardware and OS utilities |
| [`service/notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/notify.go) | DB-backed service with message bus integration |
| [`service/storage.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/storage.go) | Service delegating to external HTTP utilities |

## Summary

- **CasaOS uses a repository pattern** where the `Repository` interface exposes domain services through a centralized `store` struct.
- **Global access** occurs via the `MyService` variable, initialized once by `NewService` in [`service/service.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/service.go).
- **Extension requires six steps**: define interface, implement struct, add to `Repository`, add to `store`, initialize in `NewService`, and expose getter method.
- **Loose coupling** allows services to depend only on the database, message bus, or configuration items they specifically need.
- **Testability** is built-in through interface-based design, enabling mock substitution for any service.

## Frequently Asked Questions

### What is the purpose of the `MyService` global variable in CasaOS?

The `MyService` global variable provides a centralized access point to all domain services defined in the `Repository` interface. Rather than passing service instances through function parameters or maintaining complex dependency injection containers, any package can call `MyService.System()` or `MyService.Storage()` to access the singleton service instances initialized at startup.

### How does CasaOS handle database connections in the service layer?

The `NewService` factory accepts a `*gorm.DB` parameter and injects it into services that require persistence, such as `NotifyServer` and `SharesService`. Services that don't need database access, like `CasaService` or `SystemService`, receive only the parameters they require, maintaining clean separation between data access and business logic.

### Can I add a service to CasaOS without modifying the core repository files?

No, extending CasaOS requires modifying [`service/service.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/service.go) to add your service to the `Repository` interface, the `store` struct, and the `NewService` factory. However, you can isolate your implementation in a separate file (e.g., [`service/myfeature.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/myfeature.go)) and follow the existing pattern to minimize merge conflicts when updating from upstream.

### How does the message bus fit into the service architecture?

The `MessageBus()` method returns a `*message_bus.ClientWithResponses` that services use to publish events without direct coupling to other services. Implemented in [`service/service.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/service.go) lines 29-46, this client allows asynchronous communication—for example, the notify service publishes `casaos:file:operate` events that other services can subscribe to, maintaining loose coupling while enabling event-driven workflows.