Laravel.io
<?php
// IGNORE THIS STUFF UP HERE - Just to keep syntax highlighting from breaking my awesome pseudo-code
$newTask = array();
$asyncTaskController = new stdClass();
$asyncTaskController->store = function($x){};
// IGNORE THIS STUFF UP HERE - Just to keep syntax highlighting from breaking my awesome pseudo-code ?>

    <script>
        /**
         * AsyncTask - What it looks like
         */
        var newTask = {
            tmpId: 'some-client-side-uuid',
            id: null, // null, then eventually, a real id once created on server
            taskType: 'SiteEnvironmentBackup',
            status: 'PENDING', // <PENDING | ACTIVE | FINISHED | FAILED | CANCELLED>
            data: { /** mixed data, specific to task & task type */ }
        };
    </script>

<?php
/**
 * Step 1 - UI Sends CreateAsyncTask request, controller stores it
 *
 * Store task in DB
 * Dispatch AsyncTaskCreated event w/ new real ID
 * Find class responsible for handling given task type
 * Call "start" on said class
 * - ie. new App\SiteEnvironmentBackup( $newTask )->step1();
 * $newTask->status = 'ACTIVE';
 * $newTask->save();
 */
$asyncTaskController->store( $newTask );

/**
 * Workflow Needed from here
 *
 * Multi-Step, Sequential, Asyncronous Workflow
 *
 * ie.
 *
 * ->step1() makes request to Orchestration server to start new
 *  SiteEnvironmentBackup "Job". Request immediately returns a "JobID"
 *
 *  When Job is finished, Orch Server pings Laravel to on
 *  /api/orch_job_finished with Job Result Data
 *
 * ->step2() is called, analyizes last Job Result Data, makes request
 *  to yet another external server asyncronously
 *
 *  Process continues until we run out of steps, then
 *  App\SiteEnvironmentBackup( ... )->verifyResults() is called
 *
 *  If results are valid, $newTask->status = 'FINISHED';, otherwise,
 *  $newTask->status = 'FAILED'; & $newTask->failureData = { ... };
 *
 * Goals:
 * - Would like an expressive way of defining this
 *   "Multi-Step, Sequential, Asyncronous Workflow" within a single
 *   class, even if other classes are needed to modularize the logic
 *
 * Foreseen Issues:
 * - With potentially 100 different taskTypes, I don't want to
 *   register event listeners for all of them globally as they will
 *   only be used when a task of that type is running. Is this a real
 *   issue?
 * - Not sure how to make sure each "step" in the given class has
 *   access to the data from the previous - Likely store it on the
 *   $newTask itself in the DB
 * - Need to keep track of which "Step" a task is on
 * - When Orch server pings Laravel on /api/orch_job_finished
 *   (for ex.), need to have some way of querying DB for AsyncTasks
 *   that are on a "step" that depend on that "Job" being finished,
 *   then need to call App\SiteEnvironmentBackup( ... )->next()
 *
 * Possible Syntax Ideas:
 */
class MultiStepTask {

    protected $task;

    public function __construct( AsyncTask $task ) {
        $this->task = $task;
    }

    public function start() {
        // Calls first step, moves step cursor
    }

    public function next() {
        // Calls subsequent steps, moves step cursor
    }

    public function verifyResults() { return true; }
}

class SiteEnvironmentBackup extends MultiStepTask {

    protected $steps = [
        'checkPlanQuota',
        'startBackupJob',
        'verifyBackupValidity'
    ];

    /**
     * Will need access to Service Container - ie. OrchService $orch
     */
    public function __construct( AsyncTask $task, OrchService $orch ) {
        parent::__construct( $task );
        //...
    }

    public function checkPlanQuota() {
        //...
    }

    public function startBackupJob() {
        //...
    }

    public function verifyBackupValidity() {
        //...
    }

    /**
     * Optional to define this, parent class returns true if not
     */
    public function verifyResults() {
        //...
    }
}

$taskHandler = new SiteEnvironmentBackup( $newTask );
$taskHandler->start();
// $newTask->status = 'ACTIVE';
// Orch server pings Laravel
$taskHandler->next();
$taskHandler->next(); //...
$valid = $taskHandler->verifyResults();

if ( $valid ) {
    // $newTask->status = 'FINISHED';
    // Dispatch AsyncTaskFinished event
} else {
    // $newTask->status = 'FAILED';
    // Dispatch AsyncTaskFailed event
}

Please note that all pasted data is publicly available.