Este es un pequeño tutorial donde ejemplifico uno de los problemas más comunes al usar MVC, colocar queries extensos en el controlador, en pocas palabras la buena práctica la llamaríamos los finders van en el modelo. Ahora bien, si no sabes de qué va el MVC, te recomiendo este artículo
Los ejemplos estarán hechos en Rails pero aplican para cualquier framework que use MVC, en general aplica para cualquier framework que tenga modelos como MVVM o el ya mencionado MVC.
Cuenten en los comentarios si les gustarían más artículos sobre buenas prácticas de código.
La situación
Imaginemos que tenemos un blog, y queremos mostrar los últimos 10 artículos con más visitas, tomen como ejemplo la siguiente tabla para los artículos:
id | title | body | visits | created_at |
---|---|---|---|---|
1 | Qué es MVC | El MVC es... | 40 | Enero 13 |
2 | Diseño Web Frontend | En este artículo vamos a explicar... | 110 | Abril 2 |
3 | Desarrollo Backend | Ahora en este artículo... | 150 | Enero 6 |
Ahora imaginemos que tenemos una acción en nuestro controlador que debe mostrar los artículos con más de 100 visitas ordenados por la fecha en la que fueron creados, probablemente pondríamos algo así en el controlador
class ArticlesController < ApplicationController
def populares
@articles = Article.where("visits > ?",100).order("created_at desc")
end
end
El finder, el query o la consulta es Article.where("visits > ?",100).order("created_at desc")
, es un query hecho con el ActiveRecord de Rails, hace exactamente lo que pedimos, artículos con más de 100 visitas y ordenados por la fecha en que fueron creados. ¿Listo?
Los problemas
Las buenas prácticas nacen de código que genera problemas , si el código no genera problemas, no importa que te digan... está bien. Así que para demostrar por qué este código es ineficiente analicemos las problemáticas que puede causar.
No es fácil de testear
Antes he hablado de las pruebas automatizadas, sus beneficios y y demás, ahí explico cómo es que prácticas basadas en pruebas como TDD nos ayudan a escribir mejor código, código mantenible y eficiente.
El problema de el query que construimos es ¿cómo escribimos una prueba para saber que hace lo que queremos? Tendríamos que escribir una prueba de controlador, hacer la petición completa y estar evaluando ahí dentro si el código hace lo que queremos... iug.
Las pruebas deben ser independientes, es decir, debes poder probar el código sin otras dependencias, en este punto el test es ineficiente porque:
- Si el controlador falla nuestro query falla, eso no quiere decir que el query esté mal pero puede hacernos pensar eso, esta es la principal razón por la cuál las pruebas son independientes
- Normalmente tenemos que hacer una petición al controlador para que este se ejecute, si algo sale mal en esa petición (digamos la ruta no está definida) también fallará nuestro código, y de nuevo, eso no significa que el query esté mal.
- Es más complejo escribir la prueba, porque hay que seguir un ciclo de hacer una petición, recibirla en el controlador, ejecutar el query... nop.
No es replicable
¿Qué pasa si en otra acción queremos de nuevo traer los artículos populares? Deberíamos copiar y pegar el código hacia otro lado (copiar y pegar normalmente es señal de código que puede ser mejorado).
El problema aquí es... qué pasa si ahora nos dicen que los artículos populares son los que tienen más de 1,000 visitas en lugar de 100, bueno, tendrías que buscar todos los lugares donde colocaste este finder y modificar cada uno de ellos.
La solución
La solución es que los finders van en el modelo, no en el controlador y jamás en la vista, jamás, en serio, nunca lo hagas... de verda, es más, puede dañar tu salud (ok no), movamos nuestro query al modelo:
class Article < ActiveRecord::Base
#Este es un método de clase
def self.popular
where("visits > ?",100).order("created_at desc")
end
end
Y luego en el controlador solo haríamos:
class ArticlesController < ApplicationController
def populares
@articles = Article.popular
end
end
La ventaja de este nuevo enfoque es que ahora es fácil probar nuestro método, solo necesitamos una prueba unitaria para el método populares
de la clase Article, eso es facilisimo, nuestro test ahora es independiente del resto de la aplicación ;)
También si necesitamos en algún otro lugar el método, solo mandamos a llamar Article.popular
y listo, si nos dicen que cambiemos de alguna forma el método, modificamos el método y no andamos buscando en toda la aplicación.
Extras
Un punto extra a favor de que los finders van en el modelo, es que en frameworks como Rails, los métodos de un modelo que retornan una relación (no voy a entrar mucho a detalle en eso, chequen el ejemplo) se pueden encadenar, así:
class Article < ActiveRecord::Base
def self.popular
where("visits > ?",100)
end
def self.ultimos
order("created_at desc")
end
end
class ArticlesController < ApplicationController
def populares
@articles = Article.popular.ultimos
end
end
De esta forma podemos tener lugares de nuestra aplicación donde solo queramos los de más de 100 visitas aunque no estén ordenados, o podemos traer todos ordenados, aunque no tengan más de 100 visitas, todo encadenando los métodos que vamos creando ;)
En Rails también podemos usar scopes
para cambiar un poco la sintaxis de este código:
class Article < ActiveRecord::Base
scope :popular, ->{ where("visits > ?",100) }
scope :ultimos, ->{ order("created_at desc") }
end
Conclusión
Los finders van en el modelo
Recuerda que si gustas que haya más de estos artículos puedes escribirnos en los comentarios :)
Cursos
¿Quieres aprender más de estas buenas prácticas? Inscríbete a nuestro plan premium para seguir los cursos:
Donde enseñamos Rails de forma avanzada.