PHPackages                             mostafax/dual-layer-reporting-engine - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. mostafax/dual-layer-reporting-engine

ActiveLibrary[Database &amp; ORM](/categories/database)

mostafax/dual-layer-reporting-engine
====================================

Dual-layer MySQL→MongoDB async reporting engine for Laravel

09PHPCI passing

Since Jun 7Pushed 1mo agoCompare

[ Source](https://github.com/mostafax2/DualLayer-Reporting-Engine)[ Packagist](https://packagist.org/packages/mostafax/dual-layer-reporting-engine)[ RSS](/packages/mostafax-dual-layer-reporting-engine/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependenciesVersions (1)Used By (0)

DualLayer Reporting Engine
==========================

[](#duallayer-reporting-engine)

> Enterprise-grade MySQL → MongoDB async sync engine for Laravel.

[![Packagist](https://camo.githubusercontent.com/33ba8e0595cf1ce02c987c5cd7d768c258d7e73cccbe9e17dfe0c6b365b319b1/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6d6f7374616661782f6475616c2d6c617965722d7265706f7274696e672d656e67696e652e737667)](https://packagist.org/packages/mostafax/dual-layer-reporting-engine)[![Downloads](https://camo.githubusercontent.com/595344b130c5fb7e975c1155f9d74c20b06e30c14316bbfcccb493793f50a1de/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6d6f7374616661782f6475616c2d6c617965722d7265706f7274696e672d656e67696e652e737667)](https://packagist.org/packages/mostafax/dual-layer-reporting-engine)[![License](https://camo.githubusercontent.com/8bb50fd2278f18fc326bf71f6e88ca8f884f72f179d3e555e20ed30157190d0d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e2e737667)](LICENSE)[![PHP](https://camo.githubusercontent.com/0f16581d1180dbfd4c0e13166ec1267d4ad2f2fab8281ea6d6b284cf5c65d921/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e322532422d626c75652e737667)](https://php.net)[![Laravel](https://camo.githubusercontent.com/57b3a863c6bd691c3566277f0e9aaa31fdb411989eaf71edfb4249e03303e535/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c61726176656c2d31302532302537432532303131253230253743253230313225323025374325323031332d7265642e737667)](https://laravel.com)

---

Architecture
------------

[](#architecture)

```
┌──────────────────────────────────────────────────────────────────┐
│                         PRIMARY LAYER                            │
│                      MySQL (source of truth)                     │
│                                                                  │
│   User::create() → ModelSyncObserver fires                       │
│                         │                                        │
└─────────────────────────┼────────────────────────────────────────┘
                          │  dispatches
                          ▼
                 ┌─────────────────┐
                 │  ProcessSyncJob  │  (queued — Redis/SQS/DB)
                 └────────┬────────┘
                          │  calls
                          ▼
              ┌───────────────────────┐
              │      SyncEngine        │  ← orchestrator
              │  ┌─────────────────┐  │
              │  │ Idempotency     │  │  → skip if already processed
              │  │ check (Redis)   │  │
              │  └────────┬────────┘  │
              │           │           │
              │  ┌────────▼────────┐  │
              │  │ SourceDriver    │  │  → re-fetch from MySQL
              │  │ (Eloquent)      │  │
              │  └────────┬────────┘  │
              │           │           │
              │  ┌────────▼────────┐  │
              │  │ Transformer     │  │  → reshape for MongoDB
              │  │ (pluggable)     │  │
              │  └────────┬────────┘  │
              │           │           │
              │  ┌────────▼────────┐  │
              │  │ TargetDriver    │  │  → upsert / delete
              │  │ (MongoDB)       │  │
              │  └─────────────────┘  │
              └───────────────────────┘
                          │
                          ▼
┌──────────────────────────────────────────────────────────────────┐
│                       SECONDARY LAYER                            │
│                  MongoDB (analytics-optimised)                   │
└──────────────────────────────────────────────────────────────────┘
                          │
                   on failure:
                   exponential backoff (30s → 90s → 270s)
                   → dead letter after N attempts
                   → php artisan dual-report:reprocess --dead

```

---

Installation
------------

[](#installation)

```
composer require mostafax/dual-layer-reporting-engine
php artisan dual-report:install
```

---

Quick Start
-----------

[](#quick-start)

### 1. Register models in `AppServiceProvider`

[](#1-register-models-in-appserviceprovider)

```
use Mostafax\DualLayer\Support\Facades\DualReport;

public function boot(): void
{
    // Auto-detect transformer from DefaultTransformer (stores raw attributes)
    DualReport::observe(User::class);

    // Custom transformer
    DualReport::register(Order::class, new OrderTransformer());

    // With lifecycle hooks
    DualReport::observe(Product::class);
    DualReport::hooks(new ProductSyncHooks());
}
```

### 2. Create a custom transformer

[](#2-create-a-custom-transformer)

```
use Mostafax\DualLayer\Contracts\TransformerInterface;
use App\Models\Order;

class OrderTransformer implements TransformerInterface
{
    public function handles(): string    { return Order::class; }
    public function collection(): string { return 'orders'; }
    public function documentKey(): string { return 'source_id'; }  // MongoDB field name
    public function sourceKey(): string  { return 'id'; }          // MySQL attribute name

    public function transform(array $attr): array
    {
        return [
            'source_id'    => $attr['id'],
            'source_type'  => 'order',
            'customer_id'  => $attr['user_id'],
            'total'        => $attr['total'],
            'currency'     => $attr['currency'] ?? 'USD',
            'status'       => $attr['status'],
            'line_items'   => [],          // populated in a relation-aware transformer
            'placed_at'    => $attr['created_at'],
            'synced_at'    => now()->toISOString(),
            'meta' => [
                'tenant_id'     => $attr['tenant_id'] ?? null,
                'sync_version'  => $attr['updated_at'],
            ],
        ];
    }
}
```

### 3. Lifecycle hooks (optional)

[](#3-lifecycle-hooks-optional)

```
use Mostafax\DualLayer\Contracts\SyncHooksInterface;

class ProductSyncHooks implements SyncHooksInterface
{
    public function handles(): string { return Product::class; }

    public function beforeSync(string $operation, array $document): bool
    {
        // Return false to abort the sync for this record
        return $document['is_published'] ?? true;
    }

    public function afterSync(string $operation, array $document): void
    {
        Cache::forget("product:{$document['source_id']}");
    }
}
```

### 4. Multi-tenant model

[](#4-multi-tenant-model)

```
use Mostafax\DualLayer\Support\Traits\HasDualLayerSync;

class User extends Authenticatable
{
    use HasDualLayerSync;   // exposes getTenantId()

    // tenant_id column is automatically picked up by the observer
}
```

---

MongoDB Document Schema
-----------------------

[](#mongodb-document-schema)

### Default envelope (DefaultTransformer)

[](#default-envelope-defaulttransformer)

```
{
  "source_type": "user",
  "source_id": 42,
  "data": {
    "id": 42,
    "name": "Mostafa",
    "email": "mostafa@example.com",
    "created_at": "2024-01-01T00:00:00.000Z"
  },
  "synced_at": "2024-01-01T00:00:05.123Z"
}
```

### Custom transformer (OrderTransformer)

[](#custom-transformer-ordertransformer)

```
{
  "source_id": 1001,
  "source_type": "order",
  "customer_id": 42,
  "total": 149.99,
  "currency": "USD",
  "status": "completed",
  "placed_at": "2024-01-01T00:00:00.000Z",
  "synced_at": "2024-01-01T00:00:05.123Z",
  "meta": {
    "tenant_id": "acme-corp",
    "sync_version": "2024-01-01T00:00:04.000Z"
  }
}
```

---

CLI Commands
------------

[](#cli-commands)

```
# Install (publish config + migrate)
php artisan dual-report:install

# Status dashboard
php artisan dual-report:status

# Initial / catch-up bulk sync for existing records
php artisan dual-report:sync "App\Models\User"
php artisan dual-report:sync "App\Models\Order" --chunk=1000

# Requeue failed operations
php artisan dual-report:reprocess --failed
php artisan dual-report:reprocess --dead
php artisan dual-report:reprocess --dead --limit=500
```

---

Domain Events
-------------

[](#domain-events)

Listen to any of these events in your `EventServiceProvider`:

EventFired when`SyncCompleted`Document successfully written to MongoDB`SyncFailed`Sync attempt failed — retry will be scheduled`SyncRetried`Retry job dispatched (includes attempt # and backoff seconds)`SyncDead`All retry attempts exhausted — operation moved to dead letter```
use Mostafax\DualLayer\Domain\SyncOperation\Events\SyncDead;
use Mostafax\DualLayer\Domain\SyncOperation\Events\SyncFailed;

class EventServiceProvider extends ServiceProvider
{
    protected $listen = [
        SyncDead::class => [
            \App\Listeners\AlertOpsOnDeadSync::class,
        ],
        SyncFailed::class => [
            \App\Listeners\LogSyncFailure::class,
        ],
    ];
}
```

---

Configuration
-------------

[](#configuration)

```
DUAL_LAYER_TARGET=mongodb                  # mongodb | null
DUAL_LAYER_MONGO_CONNECTION=mongodb        # Laravel DB connection name
DUAL_LAYER_QUEUE=dual-layer-sync           # queue name
DUAL_LAYER_MAX_ATTEMPTS=3                  # retry limit
DUAL_LAYER_IDEMPOTENCY_STORE=redis         # redis | file
DUAL_LAYER_IDEMPOTENCY_TTL=86400           # 24h
```

---

Idempotency
-----------

[](#idempotency)

Every sync operation gets a deterministic `sync_id`:

```
sync_id = xxh128(model_class | model_id | operation | updated_at)

```

The same model state always produces the same `sync_id`. Before processing, the engine checks Redis — if the key exists, the job returns immediately. This means duplicate jobs from retry storms, observer double-fires, or deployment rollovers are all handled safely.

---

Retry &amp; Dead Letter
-----------------------

[](#retry--dead-letter)

AttemptBackoff1st30s2nd90s3rd270s4th+→ DEADDead-letter ops are visible in `php artisan dual-report:status` and reprocessable on-demand.

---

Scalability
-----------

[](#scalability)

TierJobs/daySetupSmall&lt; 1M1 Redis + 3 workersMedium1M–10MRedis Sentinel + 10 workers + read replicaLarge10M+Redis Cluster + Kubernetes HPA + MongoDB shardingWorkers are **stateless** — scale horizontally without coordination.

###  Health Score

20

—

LowBetter than 12% of packages

Maintenance60

Regular maintenance activity

Popularity6

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity11

Early-stage or recently created project

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/146624906?v=4)[Mostafa Elbayyar](/maintainers/mostafax2)[@mostafax2](https://github.com/mostafax2)

### Embed Badge

![Health badge](/badges/mostafax-dual-layer-reporting-engine/health.svg)

```
[![Health](https://phpackages.com/badges/mostafax-dual-layer-reporting-engine/health.svg)](https://phpackages.com/packages/mostafax-dual-layer-reporting-engine)
```

###  Alternatives

[jdorn/sql-formatter

a PHP SQL highlighting library

3.9k117.2M118](/packages/jdorn-sql-formatter)[propel/propel1

Propel is an open-source Object-Relational Mapping (ORM) for PHP5.

8351.6M87](/packages/propel-propel1)[jfelder/oracledb

Oracle DB driver for Laravel

11518.4k](/packages/jfelder-oracledb)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
