Skip to content

Add a checkout workflow step

Use a workflow step when checkout must coordinate a new durable side effect. Do not put it in a controller: retries, compensation, and recovery belong beside the existing checkout steps.

Use a workflow step when checkout must coordinate a new durable side effect. Do not put it in a controller: retries, compensation, and recovery belong beside the existing checkout steps.

Insert a step from a provider

import type { ApplicationService } from "@adonisjs/core/types";
import { PlaceOrderWorkflow } from "@adocommercekit/core/services/main";
import type { WorkflowStep } from "@adocommercekit/core";
import type { PlaceOrderContext } from "@adocommercekit/core/services/main";

const reserveMembershipCredit: WorkflowStep<PlaceOrderContext> = {
  name: "reserve_membership_credit",

  async execute(context) {
    // Use context.trx for database state owned by the checkout transaction.
    // Store enough identity in context to compensate safely.
  },

  async compensate(context) {
    // Idempotently release only the credit reserved by execute.
  },
};

export default class CheckoutExtensionsProvider {
  constructor(private app: ApplicationService) {}

  async boot() {
    const workflow = await this.app.container.make(PlaceOrderWorkflow);
    workflow.insertAfter("reserve_inventory", reserveMembershipCredit);
  }
}

Use the actual step name exported by the running workflow. Fail at boot if the anchor is absent; silently appending changes ordering guarantees.

Design the compensation first

Before implementing execute, answer:

  • What durable resource is acquired?
  • What stable identifier proves this step owns it?
  • Can execute run twice safely?
  • Can compensate run when execution stopped halfway?
  • What happens after process death, when in-memory compensation never ran?

If recovery needs new information, persist it before the external side effect and add a reconciliation path.

Keep transaction boundaries visible

Some workflow steps open or join database transactions. External network calls must not hold database locks longer than necessary. Prefer a prepare/commit protocol: persist intent, perform the remote operation with an idempotency key, then finalize with a guarded update.

Test failure injection immediately before and after the step. Assert the cart state, inventory, payment, new resource, and idempotency record—not merely that an exception was thrown.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close