3USD tu primer mes de Premium 😱 Canjear promo No me interesa

notifications Notificaciones

Marcar todas como leídas

Ver más

lightbulb_outline

MVC buenas prácticas - Coloca los finders en el modelo

timer 4 Min.

remove_red_eye 13604

calendar_today 23/10/15

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:

  1. 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
  2. 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.
  3. 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:

  1. Backend Avanzado
  2. Desarrollo de API's
  3. Desarrollo Ecommerce

Donde enseñamos Rails de forma avanzada.

Otros artículos del blog

Comunidad

Excelente explicación y se que ya lo han dicho antes

more_vert

Inicia sesión o Regístrate para poder agregar tu respuesta.

more_vert

Inicia sesión o Regístrate para poder agregar tu respuesta.