Indagando sobre por qué a veces consideramos que un método es largo o corto, encontré diversas respuestas. Por ejemplo, se sugiere que las funciones deberían tener como máximo cuatro líneas, como se describe en Clean Code. Sin embargo, en ocasiones, forzar este límite no tenía sentido, ya que generaba demasiada indirección. Después de explorar diferentes puntos de vista, me llamó mucho la atención la heurística del “hex flower” o “arquitectura basada en fractales” de Mark Seemann.
La indirección se produce cuando se añade una capa adicional entre el origen de una operación y su destino. Este intermediario puede ser una función, una clase, un patrón de diseño o cualquier otro tipo de estructura en el código.
La idea detrás de la heurística es que la capacidad de la memoria a corto plazo solo, permite almacenar entre 4 y 7 conceptos simultáneamente. Por lo tanto, no deberían haber más de 7 flujos dentro de una pieza de código. Veamos un ejemplo:
[HttpPost]
public IActionResult CreateUser([FromBody] CreateUserRequest request)
{
if (string.IsNullOrWhiteSpace(request.Name))
{
return BadRequest("El nombre es obligatorio.");
}
if (string.IsNullOrWhiteSpace(request.Email))
{
return BadRequest("El email es obligatorio.");
}
if (!new EmailAddressAttribute().IsValid(request.Email))
{
return BadRequest("El email no es válido.");
}
if (request.Age < 18)
{
return BadRequest("El usuario debe ser mayor de 18 años.");
}
if (string.IsNullOrWhiteSpace(request.Password))
{
return BadRequest("La contraseña es obligatoria.");
}
if (request.Password.Length < 8)
{
return BadRequest("La contraseña debe tener al menos 8 caracteres.");
}
var user = new User
{
Name = request.Name,
Email = request.Email,
Age = request.Age,
Password = request.Password
};
_userRepository.Add(user);
return CreatedAtAction(nameof(CreateUser), new { id = user.Id }, user);
}
¿Cuántas cosas están sucediendo en este método? Como puedes ver, hay 7 return que representan la cantidad de comportamientos de nuestro sistema, es decir, que si hiciéramos TDD, solo habría 7 tests. El problema es que si seguimos añadiendo más caminos, este código se volverá cada vez más difícil de entender. Esta situación está directamente relacionada con la complejidad ciclomatica, una métrica que mide el número de caminos linealmente independientes a través de un programa. En este caso, los 7 caminos indican una complejidad ciclomatica de 7, lo que sugiere que el código puede volverse difícil de mantener y probar, si se sigue incrementando la cantidad de decisiones lógicas o ramas en el método. La “flor” aparece cuando dibujamos los 7 posibles caminos.
Debemos asumir que el negocio seguirá cambiando, por lo que es importante estar preparados. Basándonos en el código que tenemos ahora, se nos pide además, que la contraseña incluya números para hacerla un poco más segura. Antes de implementar esta característica, vamos a extraer la contraseña en un value object, el cual tendrá los dos campos relacionados con la contraseña. La “flor” quedaría de la siguiente manera:
Ahora solo deberíamos añadir la nueva regla a la “flor” de la contraseña, manteniendo la carga cognitiva del controlador siempre por debajo de 7. Pero espera un momento, en mi controlador he añadido un test más, por lo tanto, ahora hay 8 caminos. Tienes razón, pero al extraer la lógica, los tests deberían haberse movido, lo que significa que nuestro controlador solo tendría dos caminos: uno en el que la contraseña es incorrecta y otro en el que continúa con el proceso de registro. Pasaríamos de tener dos tests a solo uno, ya que la razón por la cual la contraseña no es correcta a nivel del controlador no es relevante, porque ya existe una “flor” que valida cada aspecto de la contraseña.
Lo mismo aplica a los demás atributos del usuario, simplificando la lógica del controlador. De esta manera, mejoramos la simetría y el nivel de abstracción de nuestro código.
Esta técnica no solo se puede aplicar a funciones o clases, sino que también se puede aplicar desde el inicio del programa. De ahí proviene el concepto de diseño basado en fractales. Pensemos en nuestro programa como un árbol, donde el tronco es el punto de inicio y las ramas representan las configuraciones del mismo. Finalmente, tendríamos las flores que adornan el árbol, las cuales serían los componentes sobre los que vamos a trabajar.
Si ajustáramos el zoom al Program.cs
, deberíamos hacerlo de manera que no contenga más de 7 elementos. Solo se debería generar una flor con el siguiente aspecto:
En los ejemplos anteriores, dejamos el happy path y los posibles errores alrededor, pero podemos cambiar el lenguaje para simplemente representar conceptos de nuestra aplicación. Cuando trabajamos en la arquitectura de la aplicación, si es necesario configurar 20 servicios, será muy difícil reducirlos a 7. Por lo tanto, esta es la excepción a la regla: cuando hablamos de “arquitectura basada en fractales” no es exactamente lo mismo que “hex flower”.
La técnica puede darnos pistas de que, tal vez, la aplicación está haciendo demasiadas cosas. Por ejemplo, ¿te has planteado si podrías explicar el funcionamiento de la aplicación a otra persona a nivel conceptual? ¿Cuántas piezas hay que gestionar si no solo hablamos del registro de usuarios, sino de todo lo que se puede hacer con los usuarios? ¿Sería fácil verlo reflejado en las carpetas o en el código?
¿Quieres más? te invitamos a suscribirte a nuestro boletín para avisarte cada vez que recopilemos contenido de calidad que compartir.
Si disfrutas leyendo nuestro blog, ¿imaginas lo divertido que sería trabajar con nosotros? ¿te gustaría?
Pero espera 🖐 que tenemos un conflicto interno. A nosotros las newsletter nos parecen 💩👎👹 Por eso hemos creado la LEAN LISTA, la primera lista zen, disfrutona y que suena a rock y reggaeton del sector de la programación. Todos hemos recibido newsletters por encima de nuestras posibilidades 😅 por eso este es el compromiso de la Lean Lista