Ir al contenido principal
Laravel, thinking fast.
$provisioning->update(['response' => $result['data']]);
$lead->update(['status' => LeadStatus::Customer->value]);

El estado cambia después de que la llamada externa haya tenido éxito, no cuando se intentó. La fila creada al principio era una reclamación; esto es la confirmación. Una reclamación que nunca se confirma es identificable y recolectable —un estado fijado con optimismo es indistinguible de uno real, para siempre.

La idempotencia es una decisión por herramienta

El capítulo 6 fijó tries = 1 en el job, y esta es la razón. La política de reintentos no puede vivir a nivel de job en cuanto un turno tiene efectos secundarios, porque un turno es una mezcla de herramientas con semánticas de reintento completamente distintas.

Comprobar una URL es idempotente: llámala cincuenta veces y no cambia nada. Sugerir paletas es idempotente. Crear un sitio no lo es, ni enviar un correo tampoco.

Así que el bucle no reintenta nunca, y cada herramienta se responsabiliza de su propia seguridad ante la repetición:

  • Idempotente por naturaleza — herramientas de sólo lectura. Nada que hacer.
  • Idempotente por una restricción — creación protegida por un índice único sobre la clave natural.
  • Idempotente por un token de un solo uso — el token es el permiso, se gasta una vez.
  • Genuinamente insegura de repetir — no debería ser una herramienta que el modelo pueda llamar libremente, que es el capítulo 11.

Pregúntale a cada herramienta: ¿qué pasa si esto se ejecuta dos veces? Si la respuesta no es «nada», la restricción que lo hace seguro va en la herramienta —no en una descripción, y no en el bucle.

Dile al usuario qué ha pasado

return Response::json([
    'site_id' => $siteId,
    'status' => $provisioning->status,
    'message' => 'Site provisioning has started.',
    'expected_url' => 'https://'.$url.'.example',
    'site_details' => $siteDetails,
    'account_url' => $accountActionUrl ?: null,
    'subscription_deadline' => 'Users must subscribe before the trial window ends
        to keep the site active. Use the window stated in your instructions —
        never state a duration of your own.',
]);

El resultado de una acción irreversible debería ser lo más informativo posible, porque es el recibo del usuario. No «hecho», sino qué se creó, dónde estará, qué debe hacer a continuación y para cuándo.

Ese último campo merece atención: le dice al modelo que se remita a sus instrucciones en lugar de inventarse un número. Es la regla del capítulo 2 sobre datos que el modelo no puede saber, aplicada donde equivocarse sería una promesa comercial falsa.

Lo que sostiene este capítulo

  • Una instrucción describe una intención; una restricción la impone. La unicidad no se consigue pidiéndola con educación.
  • Persiste primero una reclamación y deja que un índice único sobre la clave natural rechace el duplicado.
  • Captura la violación de restricción y tradúcela a algo sobre lo que el modelo pueda actuar, en vez de un 500 que quizá reintente.
  • Un token de un solo uso necesita dueño durante toda su vida. Sácalo, guárdalo y devuélvelo en todos los caminos de fallo.
  • Nunca mantengas una transacción a través de una llamada de red lenta. Un servicio externo colgado no debería agotar tu pool de conexiones.
  • Revierte antes de reportar. La observabilidad puede fallar; las compensaciones deben ejecutarse primero.
  • Confirma el estado sólo cuando el trabajo es real, para que las reclamaciones sin confirmar sigan siendo identificables.
  • La política de reintentos va por herramienta, no por job. Pregunta qué pasa si cada una se ejecuta dos veces.
  • Haz de un resultado irreversible un recibo —qué se creó, dónde, qué hacer luego y para cuándo.