How Chorus Works

Laravel Chorus creates a bidirectional synchronization system between your Laravel backend and client applications. This page explains the core architecture and data flow patterns that make real-time synchronization possible.

Architecture Overview

Core Components

Server-Side Components

Client-Side Components

Change Detection Adapters

Chorus supports multiple methods for detecting model changes:

1. Eloquent (Default)

// Automatically triggers when models are saved/deleted
public static function bootHarmonics()
{
    static::created(fn($model) => self::createHarmonic($model, 'create'));
    static::updated(fn($model) => self::createHarmonic($model, 'update'));
    static::deleted(fn($model) => self::createHarmonic($model, 'delete'));
}

2. Postgres: WAL Logical Replication Stream (Future Enhancement)

It is planned to add support for Postgres Logical Replication stream. For Postgres databases with logical replication enabled, it is possible to listen to the database’s logical changes (create, update, delete) directly. This means that we can mutate the database directly without going through the Eloquent ORM. This approach is ideal for applications that have databases that can change outside Laravel in some way.

Synchronization Strategies

Selective Field Sync

Only specified fields are synchronized, reducing bandwidth and storage requirements.
protected $syncFields = [
    'id',           // Always include primary key
    'title',        // User-visible content
    'status',       // Business logic fields
    'updated_at'    // Timestamp for conflict resolution
    // 'password' excluded for security
    // 'internal_notes' excluded to reduce size
];

Filtered Synchronization

Apply custom logic to determine which records each user should receive.
protected function syncFilter(): Builder
{
    $user = auth()->user();
    
    return $this->where(function($query) use ($user) {
        $query->where('user_id', $user->id)              // Own records
              ->orWhere('assigned_to', $user->id)         // Assigned records
              ->orWhere('team_id', $user->team_id);       // Team records
    });
}

Channel-based Broadcasting

Different WebSocket channels for different data scopes and user permissions.
// User-specific channel
"chorus.user.{$userId}"

// Tenant-specific channel  
"chorus.tenant.{$tenantId}.user.{$userId}"

// Table-wide updates
"chorus.table.messages"

// Record-specific updates
"chorus.record.messages.{$messageId}"

Conflict Resolution

When multiple clients modify the same data simultaneously, Chorus handles conflicts using:

Last-Write-Wins Strategy

  • Server timestamps determine the “winning” version
  • Client changes are overwritten by server updates
  • Simple but may lose user edits in rare cases

Optimistic Locking

  • Version numbers or timestamps prevent conflicting updates
  • Failed updates trigger user notifications
  • Allows manual conflict resolution

Field-Level Merging

  • Different fields can be updated by different users
  • Complex but preserves maximum user input
  • Requires careful schema design

Performance Optimizations

Delta Compression

Only changed fields are transmitted, not entire records

Batch Processing

Multiple changes combined into single operations

Connection Pooling

Efficient WebSocket connection management

Local Caching

IndexedDB provides instant query performance

Security Considerations

  • Data Filtering: syncFilter() ensures users only receive authorized data
  • Write Validation: All client operations validated server-side
  • Channel Authorization: WebSocket channels require proper authentication
  • Field Selection: Sensitive fields excluded from synchronization
  • Tenant Isolation: Automatic multi-tenant data separation

Next: Learn how to implement Chorus in your application with our Getting Started guide.