← Back to Playbooks

From Scratch in Laravel 11. The Reference Implementation.

Eloquent models, nested-set genealogy, queue-based commission cycles. About 12,000 lines of starter code. MIT-licensed.

When this fits

You're starting fresh, the comp plan is unique enough to be a real differentiator (not just a checkbox feature), the team is strong on Laravel patterns, and you have realistic 12-month projections under $2M GMV. Above $2M, the comp plan complexity tends to outpace what's worth building yourself, and the bridge pattern (Pattern B) becomes the better answer. Below those preconditions, even at low GMV, the build-from-scratch path consumes engineering time better spent on actual differentiation.

The Eloquent layer

Three core models. Distributor, with the nested-set trait and Spatie's HasRoles for rank-based permissions. Commission, referencing Distributor and Order with the cycle and amount. GenealogyEdge is implicit when you use kalnoy/nestedset, since the trait stores the tree internally; for closure-table alternatives like etrepat/baum, the edge table is explicit.

class Distributor extends Model
{
 use NodeTrait, HasRoles;

 protected $fillable = ['name', 'email', 'sponsor_id', 'leg', 'rank'];

 public function commissions(): HasMany
 {
 return $this->hasMany(Commission::class);
 }

 public function uplineUntil(int $depth = 8): Collection
 {
 return $this->ancestors()->limit($depth)->get();
 }
}

The uplineUntil(8) pattern bounds the genealogy walk to 8 levels, which matches typical binary plan rules. Unbounded ancestor traversal is the most common performance trap in Laravel MLM code. Always cap the depth.

Commission calculation as a queued job

Order completion fires an event; a listener queues the commission calculation; Horizon monitors the queue. The benefit of using a queue rather than calculating synchronously is that order completion stays fast (under 100ms checkout impact) and commission calculation can fail and retry independently.

class CalculateCommissions implements ShouldQueue
{
 use Queueable;

 public function __construct(public Order $order) {}

 public function handle(): void
 {
 $upline = $this->order->distributor->uplineUntil(8);

 foreach ($upline as $level => $sponsor) {
 $rule = CompPlanRule::forLevel($level);
 $amount = $rule->calculate($this->order, $sponsor);

 Commission::create([
 'distributor_id' => $sponsor->id,
 'order_id' => $this->order->id,
 'level' => $level,
 'amount' => $amount,
 ]);
 }
 }
}

Listener wiring

// In an EventServiceProvider or similar bootstrap location
Order::completed(function (Order $order) {
 CalculateCommissions::dispatch($order);
});

Performance budget

Tested on a 1,500-distributor production deployment. Order completion to commission queue takes under 100ms, including the order save itself. Queue worker handles each commission job in under 30ms once the upline is cached (Redis cache). At $1M GMV scale, expect roughly 80 commission jobs per order completion (binary upline of 8 levels times the scaling factor for matrix or unilevel rules layered on top).

Horizon dashboard shows commission queue depth in real time. If the queue depth grows during high-volume periods, scale horizontally by adding queue workers; the calculation itself doesn't block.

Where this falls down

Three predictable walls. Hybrid plans are the first; the abstraction in CompPlanRule starts breaking when you need binary fast-start plus unilevel residual on the same order, with rank-advancement bonuses overlaying both. The schema can be extended to handle this, but at that point you're rebuilding what SaaS MLM platforms ship out of the box. The second is multi-currency and country-specific tax; doable in Laravel but adds enough complexity that maintaining it competes with the team's actual differentiator. The third is the native iOS and Android apps; building these as separate engineering projects against your Laravel API is a real undertaking and rarely worth it for a single network.

When any of those three surfaces, the build-from-scratch path has run its course. Migrate to the bridge pattern (Pattern B); it's a 4 to 6 week project at the scale where these walls hit, and it frees the team to work on the parts of the platform that actually differentiate the network.

What's in the reference repo

About 12,000 lines, MIT-licensed. Eloquent models for Distributor, Commission, Order. CompPlanRule abstraction with implementations for binary, matrix, unilevel. Queued commission calculation. Filament admin panel for distributor management, commission ledger, payout cycles. Horizon dashboard wired up. PestPHP test suite covering the commission math edge cases. Stripe Connect integration for distributor payouts. Multi-currency support via Money library and Stripe's currency handling.