Rails: Skinny Controller vs. Fat Model, o debería hacer que mi Controller sea anoréxica?

Sé que preguntas similares han sido respondidas antes, como por ejemplo:

  • ¿A dónde debería ir la lógica?
  • dónde hacer ciertas tareas, etc.

Pero tengo una pregunta más específica: ¿hasta qué punto debería tomar este axioma? Mantenga su controlador delgado, ¡haga que su modelo sea gordo!

Aquí hay un ejemplo:

Por ejemplo, digamos que tengo múltiples fonts de datos de verificación. Un buen ejemplo sería un número VIN – Puedo verificarlo en contra, fuente de datos del fabricante, fuente de datos del DMV, también mis bases de datos locales – para ver lo que tengo registrado. Entonces tengo un modelo llamado Vin y vins_controller. Dentro del modelo tengo 5 métodos:

  • check_against_local_db
  • check_against_dmv
  • check_against_car_maker_1
  • check_against_car_maker_2, etc.

En mi controlador manteniendo el REST, en el show de acción – Tengo una statement de caso simple que mira los params [: source], y en función de la fuente especificada – llamará al método de comprobación específico.

Ahora aquí está la pregunta: ¿Debo dejar la lógica que gobierna a qué fuente de datos llamar en el controlador o debo moverlo a un modelo y luego en el controlador simplemente hacer algo como check_vin (source, vin)?

¿Debería hacer que mi controlador sea anoréxico?

EDITAR

Estoy cambiando esto a la respuesta oficial de @ jay-godse (gracias, en ese momento fue una buena respuesta). Las cosas han cambiado mucho desde 2010 y esta pregunta todavía tiene algunas opiniones, por lo que espero que esto apunte a algunas personas en la dirección correcta y les ayude a organizar su código correctamente.

La gem Trailblazer aborda los problemas planteados en la pregunta realmente bien.

Deberías probar Trailblazer . Este es un marco delgado sobre Rails (en realidad, se ejecuta con todos los frameworks de Ruby), presenta una capa de servicio, objetos de formulario, direccionamiento indirecto entre la persistencia y el código de dominio, y así sucesivamente.

Realmente ayuda a mantener todos los componentes de software delgados ya que le da más capas de abstracción que hacen que sea mucho más fácil averiguar dónde poner qué.

Por ejemplo, ¿por qué presionarías tu lógica de validación en el controlador y el modelo cuando tienes un objeto de formulario para eso?

Hay un viejo dicho,

Las estructuras de datos inteligentes y el código tonto funcionan mucho mejor que al revés.

Todo lo que ha descrito se trata de cómo una pieza de datos se relaciona con otra. Esa es su pista de que la lógica para esas relaciones, incluida la validación, debería estar en el modelo o en la capa de la base de datos.

Un controlador anoréxico es un signo de datos bien diseñados (¿inteligentes?). Mi experiencia me dice que cuanto más esfuerzo dediques al diseño de tus datos, menos código tendrás que escribir en general.

Los controladores son los mejores para analizar las entradas, llamar a los modelos apropiados y luego formatear las salidas.

Pondría la lógica en mi modelo, especialmente si estoy TDD (y siempre estoy TDD, excepto cuando no lo hago). Probar el modelo suele ser mucho más fácil que probar el controlador.

Me gusta abordar preguntas como esta pensando en la responsabilidad. ¿Qué es en este caso “responsable” de verificar el VIN? El modelo. El controlador simplemente está allí para pasar los parámetros … para “controlar” según la entrada del usuario.

Si no está del todo claro, piénselo de esta manera: ¿dónde colocar este código causará la menor cantidad de impacto si necesita ser reutilizado? Digamos … si dos acciones diferentes en dos controladores diferentes necesitan verificar un VIN, ¿qué debería hacerse? Si dejó este código en el controlador, esencialmente tendría que duplicarlo también en el nuevo controlador, pero si lo hubiera colocado en el modelo, simplemente llamaría al método check_vin del nuevo controlador y no tendría código. la duplicación sería necesaria. Al asignar responsabilidades donde tienen más sentido, ha mejorado la reutilización de su código.

Es responsabilidad del controlador “analizar” los params , pero la validación debe hacerse en el modelo.

Haría algo como esto en el controlador:

 @my_model = MyModel.new(:source => params[:source] ...) if(@my_model.valid?) # treat valid model here (ie save the model and redirect to show) else # treat validation error here (usually throw an error) end 

Su controlador será simplemente “flaco”. Para los controladores verdaderamente “anoréxicos”, es posible que desee echar un vistazo a inherited_resources o resource_this . En algunos casos, estos le darán un controlador de 3 líneas, implementando toda la stack RESTful.