Máquinas de Estado Finito (FSM)
Una cosa que aprendí luego de cometer el mismo error varias veces en proyectos pasados es dejar de usar booleans
en los diseños de las tablas de la base de datos. Estos te dan cierta facilidad al inicio pero son muy difícil de usarlos a futuro. Por ejemplo una tabla usuarios
con el campo estado
con valores true
es activo y false
es inactivo parece muy inofensiva; el problema viene cuando queremos agregar un tercer estado como por ejemplo reseteando
para identificar que usuarios están reseteando el password y la solución más fácil en ese momento es agregar un nuevo campo llamado reseteando
como boolean. Pero cuando quieres hacer una consulta a esa tabla de los usuarios activos, ahora tienes que verificar que reseteando también sea false
. Y esto pasa en muchos otros procesos, como facturas, documentación, etc.
Muchos de los procesos del negocio casi siempre tienen un flujo de trabajo asociado, y los estados no son blanco o negro, la solución que ha funcionado para mí en ya algunos proyectos, es usar máquinas de estados finitos o FSM por sus siglas en inglés.
Existen muchas librerías que permiten usar una FSM en diferentes lenguajes de programación. En el caso de PHP la que uso personalmente es: yohang/finite tiene un respetable número de descargas en packagist y es agnóstica al framework, la he usado con laravel, phalcon y symfony sin ningún problema.
En una FSM se define una configuración para una entidad, esta debe tener estados
, transiciones
y opcionalmente llamadas
.
Estados (states) como su nombre lo indica son los posibles estados en los que un objeto de la entidad puede estar. En nuestro ejemplo de usuarios, este puede tener los siguiente estados: activo, inactivo, reseteando.
Transiciones (transitions) son las posibles acciones que pueden hacer que un objeto cambie de un estado a otro. En nuestro ejemplo las transiciones pueden ser activar
que va de inactivo a activo, desactivar
que va de activo a inactivo y resetear
que va de inactivo o activo a reseteando y reestablecer
que va de reseteando a activo.
Llamadas (callbacks) son funciones del objeto que se llaman cuando una transición se ejecuta, esto me ha sido útil por ejemplo para llevar un control de cambios en el objeto, cuándo se hizo la transición y quién la ejecutó.
Una vez realizada la configuración, el API para verificar que transiciones puede realizar el objeto son:
$user->getCurrentState(); // por ejemplo ‘activo’
$user->can(‘activar’); // false porque está en estado activo solo puede ir a reseteando
$user->can(‘desactivar’); // true si se puede realizar esta transición de acuerdo a la configuración
$user->apply(‘desactivar’); // se realiza el cambio
$user->getCurrentState(); // ahora es ‘inactivo’
$user>getCurrentState()->getTransitions(); // muestra que posibles transiciones tiene disponible, en el caso de inactivo puede ir solo a ‘activar’
Para mayor información la documentación de yohang/finite es muy completa.