Saltar al contenido
DedicatedPHP Contactar

Cómo diseñar un estado de revisión para datos inciertos en una aplicación PHP

Diseña una revisión humana trazable en PHP: separa el dato recibido de la decisión, define transiciones y controla permisos, concurrencia y correcciones.

Diagrama conceptual de un registro recibido, una propuesta y una decisión humana dentro de un flujo de revisión en PHP

Cuando una aplicación recibe un dato ambiguo, forzarlo a una respuesta de «válido» o «inválido» suele esconder el problema en vez de resolverlo. Puede ocurrir con formularios, integraciones o resultados propuestos por un sistema automatizado: falta contexto para aceptar o descartar el registro con seguridad. En esos casos, la revisión humana de datos inciertos en PHP debe ser un estado del dominio, con reglas explícitas, no una etiqueta informal ni una bandeja improvisada.

El diseño tiene que responder cuatro preguntas: qué se recibió, qué se propone, quién debe decidir y qué ocurre después. Separar esas responsabilidades facilita operar el flujo, corregir errores y explicar por qué un dato se procesó de una manera determinada.

Clasifica el problema antes de crear estados

Clasifica el problema antes de crear estados — guía visual de DedicatedPHP

No todos los datos que no pueden avanzar requieren revisión humana. Distingue, al menos, estas situaciones:

  • Inválido: incumple una regla verificable, como un formato que no se puede interpretar. Corresponde rechazarlo o pedir que se corrija, según el origen y el producto.
  • Incompleto: falta un campo necesario. La acción puede ser solicitar el dato, conservar el borrador o esperar una fuente adicional.
  • Incierto: hay varias interpretaciones plausibles o la evidencia no permite decidir automáticamente. Puede necesitar evaluación por una persona.

La revisión no debe convertirse en el destino genérico de todo error. Si un valor incumple una restricción inequívoca, enviarlo a una cola de revisión solo añade trabajo. En cambio, cuando la decisión depende de contexto o criterio, un estado revisable permite detener el procesamiento sin declarar el dato correcto o incorrecto prematuramente.

También conviene distinguir este flujo de una cuarentena de importación. La cuarentena suele aislar entradas que no deben incorporarse al sistema, por ejemplo, porque fallaron controles técnicos o de seguridad. La revisión resuelve una ambigüedad de negocio. Un mismo registro puede pasar por ambos mecanismos, pero sus objetivos, permisos y criterios de salida no son intercambiables.

Define un ciclo de vida pequeño y explícito

Empieza con los estados necesarios para describir el proceso, no con una lista de todas las posibilidades futuras. Un ciclo básico podría ser:

  • pending_review: espera una decisión.
  • confirmed: una persona confirma un valor utilizable.
  • rejected: la persona determina que no debe procesarse.
  • reopened o una nueva entrada en revisión: una decisión anterior vuelve a estar en cuestión.

Define transiciones permitidas. Por ejemplo, un caso pendiente puede confirmarse o rechazarse; un caso ya resuelto puede reabrirse solo bajo condiciones autorizadas. No permitas que una petición cualquiera cambie el estado directamente a cualquier valor. Una transición debe especificar estado de origen, estado de destino, actor permitido y efectos asociados.

Guarda el motivo de la decisión y la identidad del responsable. Si se rechaza un dato, registra una razón útil, no solo un booleano. Si se confirma una interpretación, conserva qué valor se aprobó y, cuando corresponda, qué evidencia sustentó la decisión. Establece si el motivo es obligatorio según la acción y evita incluir información personal innecesaria en notas libres.

Separa entrada, propuesta y valor confirmado

Un diseño robusto no sobrescribe el dato recibido con la interpretación de un proceso automático o con la corrección de una persona. Mantén conceptos distintos:

  • Dato recibido: la entrada original y su procedencia, con los controles adecuados de acceso y conservación.
  • Interpretación propuesta: una sugerencia generada por reglas, una integración o un sistema asistido, junto con la información necesaria para evaluarla.
  • Valor confirmado: el valor que la aplicación utiliza después de la decisión autorizada.

Esta separación evita que una propuesta se trate accidentalmente como una verdad y permite reconstruir cómo se llegó al valor actual. No hace falta duplicar indiscriminadamente cada campo: conserva los datos necesarios para auditoría y soporte, aplicando políticas de retención y protección de datos. Si una propuesta incluye una puntuación o explicación automatizada, trátala como contexto, no como decisión.

Registra los cambios como eventos o entradas de historial inmutables para la aplicación ordinaria: identificador del caso, acción, estado anterior y nuevo, actor, fecha y motivo. El modelo puede mantener además el estado actual para consultar la cola con facilidad. La corrección se representa con una nueva decisión o reapertura vinculada al historial; borrar la decisión previa destruye trazabilidad.

Implementa las decisiones en la capa de dominio

En PHP, encapsula las transiciones en un servicio de aplicación o una entidad de dominio, en lugar de confiar en que cada controlador aplique las mismas reglas. Un ejemplo esquemático:

final class ReviewDecisionService
{
    public function confirm(ReviewCase $case, User $actor, string $value, string $reason): void
    {
        if (!$actor->canReview($case)) {
            throw new DomainException('Acceso no permitido');
        }

        if (!$case->isPending()) {
            throw new DomainException('El caso no está pendiente');
        }

        $case->confirm($value, $actor->id(), $reason);
    }
}

El fragmento ilustra dónde ubicar reglas, no una implementación completa. Valida también el formato y los límites del valor, comprueba que el actor tenga autorización para ese caso y registra la decisión. No confundas autenticación —quién es el usuario— con autorización —qué puede hacer sobre ese registro—. Si existen roles o separación de funciones, expresa esas restricciones como reglas del producto.

Cuando la actualización del caso y la escritura del historial deben ser coherentes, guárdalas en una transacción de base de datos. Si la decisión dispara efectos externos, como una llamada a otra API, evita asumir que la transacción local también cubre ese sistema. Usa un mecanismo de entrega fiable, como una bandeja de salida transaccional cuando la arquitectura lo requiera, y diseña los consumidores para tolerar reintentos.

Diseña una cola que ayude a decidir

Una cola útil permite priorizar y encontrar casos sin ocultar su contexto. Incluye filtros pertinentes —estado, fecha, tipo o asignación— y muestra solo la información necesaria para comprender el caso. Si hay una propuesta, deja claro que es una propuesta. Expón procedencia, motivo de revisión y acciones disponibles, y evita presentar controles que el usuario no está autorizado a ejecutar.

La asignación puede reducir trabajo duplicado, pero no sustituye la autorización ni la protección frente a decisiones simultáneas. Una persona puede abrir un caso y otra resolverlo antes de que la primera envíe su respuesta. Para evitar que una pantalla antigua sobrescriba un cambio reciente, usa control de concurrencia optimista: compara una versión o marca temporal al guardar y rechaza la operación obsoleta para que el usuario recargue el caso.

Si el proceso necesita reservas temporales, define su vencimiento y qué ocurre cuando caducan. No uses una asignación permanente como bloqueo accidental. Registra acciones relevantes para poder explicar quién revisó el caso y detectar registros que permanecen pendientes más tiempo del aceptable para la operación.

Corrige y reabre sin borrar el pasado

Una decisión puede ser errónea o cambiar porque llegó nueva evidencia. Define quién puede reabrirla, qué motivo debe aportar y si el dato confirmado deja de estar disponible mientras se revisa de nuevo. La respuesta depende del impacto del valor en procesos posteriores; por ejemplo, puede ser necesario detener nuevas acciones, pero no eliminar automáticamente efectos ya producidos.

Conserva la decisión anterior en el historial y crea una nueva versión o evento de revisión. Si corregir el dato exige recalcular información dependiente, identifica esos efectos de forma explícita y establece un procedimiento idempotente para evitar duplicados. Una corrección de estado no debe fingir que una operación externa ya se deshizo.

Prueba reglas, permisos y efectos posteriores

Prueba reglas, permisos y efectos posteriores — guía visual de DedicatedPHP

Las pruebas deben cubrir el flujo completo y sus límites, no solo que el formulario se guarde. Como mínimo, verifica:

  • Que un caso pendiente acepte las transiciones previstas y rechace las demás.
  • Que actores sin permiso no puedan decidir, reabrir ni consultar datos restringidos.
  • Que cada decisión conserve actor, motivo y valores anteriores y nuevos, según la política definida.
  • Que dos cambios concurrentes no permitan que una decisión obsoleta sobrescriba otra.
  • Que un fallo al registrar el historial no deje el estado actualizado sin trazabilidad.
  • Que reintentar un efecto posterior no duplique operaciones ni notificaciones.

Como lista de comprobación de diseño, confirma que la incertidumbre tiene una causa identificable; que dato original, propuesta y valor confirmado están diferenciados; que cada transición tiene permiso y motivo; que la cola aporta contexto suficiente; y que reabrir conserva el historial y define el impacto operativo. Si alguno de estos puntos depende de convenciones informales, todavía falta una regla del sistema.

¿Quieres aplicar estas ideas a tu proyecto?Hablemos de tu plataforma PHP.
Ver servicio relacionado