High-Level Architecture
- Frontend (Vue 3): A visual editor where users can drag, drop, and connect nodes to define a workflow. We’ll use a library like Vue Flow for this. The state of the graph (nodes, edges, and their configurations) will be stored as a JSON object.
- Backend (Laravel API):
- An API endpoint to save/load the workflow’s JSON definition to a database.
- A
Workflowmodel to represent the stored workflows.
- Execution Layer (Laravel Pipeline & Artisan):
- An Artisan command (
workflow:run) will trigger a specific workflow. - A Translator/Engine Service will parse the workflow’s JSON and translate it into a series of executable “Pipe” classes.
- Laravel’s Pipeline will execute these pipes sequentially, passing a “state” object through each one, which nodes can read from and write to.
- Branching Logic (Conditional/Switch): The standard Laravel Pipeline is linear. We’ll adapt our engine to handle branching by dynamically deciding the next node to execute based on the current state.
Step 1: Backend Setup (Laravel)
1.1. Create Project and Database Model
..._create_workflows_table.php and define the schema.
1.2. Set up the Workflow Model
In app/Models/Workflow.php, add a cast for the definition field.
1.3. Create the API Controller
routes/api.php.
Step 2: The Core Execution Engine
This is the heart of our system. It translates the visual graph into executable code.2.1. Define the State Object
The “state” is an object that gets passed from one node to the next. Each node can read from it and write to it. A simpleDataObject using ArrayAccess is perfect for this.
2.2. Create the Pipe Classes for Each Node Type
Create a directoryapp/Workflows/Pipes. Each class will represent a node type and have an handle method.
a) DB Access Node
Security Warning: Usingeval()is extremely dangerous if the code comes from untrusted users. In a real-world scenario, you should use a sandboxed environment or a pre-approved list of invokable classes. For this example, we’ll proceed withevalfor simplicity.
$state['fullName'] = $state['firstName'] . ' ' . $state['lastName'];
c) Terminator Node
This node will stop the pipeline and can format the final output.
2.3. The Workflow Engine Service (Handles Graph Traversal & Branching)
This service is the most crucial piece. It takes the workflow definition and executes it node by node.Step 3: The Artisan Command
WorkflowEngine.
Step 4: Frontend Setup (Vue 3)
We’ll use Vue Flow to create the visual editor.4.1. Install Vue and Dependencies
vite.config.js and resources/js/app.js to use Vue.
4.2. Create the WorkflowEditor.vue Component
This component will contain the graph editor.
data property). For a DB node, you would have inputs for Table, Operation, etc. This data is what gets passed as $config to your backend Pipe classes.
Here is a simplified example of a custom DB node:
type is db_access.
Step 5: Tying it all together
- Design a Workflow: Use your Vue frontend to create a workflow.
- Add a Trigger node.
- Add a DB Access node. Configure it to fetch a user:
table: 'users',operation: 'first',where: [{field: 'id', operator: '=', value: 1}],outputKey: 'user'. - Add a Terminator node.
- Connect Trigger -> DB Access -> Terminator.
- Save: Click “Save”. The Vue app sends a PUT request to
/api/workflows/1with a JSON payload like this:
- Run: Trigger the execution via an API call (e.g., from a button in your app) or directly via the command line.
- Via CLI:
- Via API: A
POSTrequest tohttp://your.app/api/workflows/1/run.
- Execution Flow:
- The
RunWorkflowcommand starts. - It calls the
WorkflowEngine. - The engine finds the
triggernode and starts. - It moves to the
db_accessnode (node-1). It executesDbAccessPipewith the configured data. The pipe runsDB::table('users')->where('id', 1)->first()and stores the result in$state['user']. - It moves to the
terminatornode (node-2), which ends the process and returns the final state. - The Artisan command prints the final state containing the user object.
UI Wireframe
Of course! Here is a text-based wireframe that visualizes the Vue front-end for your workflow engine. This wireframe illustrates the key components and their interactions. We’ll visualize two states:- The main editor view with a workflow loaded.
- The view when a specific node is selected, showing the contextual properties panel.
Wireframe 1: Main Editor View
This is the default view when a user opens a workflow to edit it.Breakdown of the Main View:
-
Header:
Workflow Engine: Application title.[ My First Workflow ▼]: A dropdown to switch between existing workflows.[ Save ]: Saves the current graph layout and node configurations to the Laravel backend.[ Run Workflow ]: Triggers the Artisan command via the API, potentially opening a modal to ask for initial input data.
-
Left Sidebar (
WORKFLOWS):- A list of all saved workflows.
- The
>indicates the currently selected workflow being displayed on the canvas. [+ New Workflow]button to create a new, empty workflow.
-
Main Content Area:
NODE PALETTE: A toolbar with buttons for each available node type. Clicking one of these would add a new node of that type to the canvas.( C A N V A S ): The main area where the graph is built.- Nodes: Represented by boxes (
+---+). Each has an icon, a type (Trigger,DB Access), and a user-defined label (Find User). - Edges: Represented by lines and arrows (
--->) connecting the nodes. - Handles/Ports: The points on a node where edges connect. For branching nodes like
If/Else, the outgoing edges are labeled (true,false).
- Nodes: Represented by boxes (
-
Footer:
- Displays status messages like “Ready”, “Saving…”, “Workflow Saved”, or “Execution Failed”.
Wireframe 2: Node Properties Panel (Contextual)
This view shows what happens when the user clicks on the “🗃️ DB Access (Find User)” node on the canvas. The right sidebar appears to show its specific configuration.Breakdown of the Properties View:
-
Canvas Interaction:
- The selected node,
DB Access (Find User), is highlighted (represented by+===============+).
- The selected node,
-
Right Sidebar (
PROPERTIES):- This panel is contextual. Its content changes based on the selected node’s type.
Node Label: A generic field for all nodes to give them a descriptive name.Table Name: An input field specific to theDB Accessnode.Operation: A dropdown (<...▼>) to select the database action (first,get,insert, etc.).Output Key: The variable name under which the result of this node will be stored in the workflow’sStateobject (e.g.,state['found_user']).Conditions: A dynamic form to build theWHEREclause. The user can add multiple conditions. This demonstrates how complex data for a node’s configuration can be captured.- It shows how values can be hardcoded (
1) or pulled from the workflow state (input_email), making the nodes dynamic.
- It shows how values can be hardcoded (
[+ Add]: A button to add anotherWHEREcondition row.
Vue2 Vertical Flow
Absolutely! A vertically flowing workflow is a fantastic way to simplify the UI, and it’s perfectly achievable in Vue 2 without the need for a complex graph library. This approach is often more intuitive for users who are used to step-by-step processes. Instead of a free-form canvas, we’ll build an interface that resembles a structured list, where branching is handled through indentation.Text-Based Wireframe: Vertical Workflow Editor
This wireframe shows a workflow for processing a new user signup. It includes a conditional branch.How to Build This in Vue 2
This design is much simpler to implement as it doesn’t require drag-and-drop or position management. It’s essentially a recursive list of components.1. The Data Structure (JSON)
The visual structure above maps beautifully to a nested array of objects. This is the JSON you’ll save to your Laravel backend.2. Vue 2 Component Structure
You would primarily need two recursive components.WorkflowEditor.vue (The Parent)
WorkflowStep.vue (The Recursive Child)
This component renders a single step and, if it’s a conditional, recursively renders more WorkflowStep components for its branches.
3. Backend Engine Adaptation
Your LaravelWorkflowEngine would also be simplified. Instead of traversing edges, you would recursively execute the arrays.

