NutriFlow is a digital platform for active people, athletes, and those looking to improve their diet. It is also for users who need a personalized and flexible meal plan based on their goals, dietary restrictions, and budget.
By William Henao, Nicolas Zapata and Samuel Deossa.
A continuación, se documentan las refactorizaciones y patrones de diseño implementados para mejorar la calidad, mantenibilidad y extensibilidad del software.
Esta actividad se centró en refactorizar el comando de gestión import_csv.
-
Problema Identificado: Inicialmente, el comando (
import_csv) estaba fuertemente acoplado a la lógica de bajo nivel para leer y procesar un archivo CSV específico. La claseCommandcontenía código para abrir el archivo, leerlo línea por línea y crear objetos en la base de datos. Esto violaba el Principio de Inversión de Dependencias (D de SOLID), ya que un módulo de alto nivel (la política de importación) dependía directamente de un detalle de bajo nivel (el formato CSV). -
Solución Aplicada:
- Se definió una abstracción (
ProductImporter) ensupermarket/services.py, una interfaz con un métodoimport_products(). - Se creó una implementación concreta (
CSVProductImporter) que hereda de la abstracción y contiene la lógica específica para leer archivos CSV. - Se modificó el comando
import_csvpara que dependa únicamente de la abstracciónProductImporter. La instancia concreta del importador es inyectada en el comando en tiempo de ejecución.
- Se definió una abstracción (
-
Mejoras Obtenidas:
- Desacoplamiento: El comando ya no conoce los detalles de la fuente de datos, solo sabe cómo trabajar con la interfaz
ProductImporter. - Flexibilidad y Extensibilidad: El sistema ahora puede soportar nuevos formatos de datos (JSON, XML, etc.) simplemente creando nuevas clases que implementen la interfaz, sin modificar el comando existente.
- Testabilidad: Es más fácil probar el comando de forma aislada, proporcionando un "doble de prueba" (mock) que implemente la interfaz
ProductImporter.
- Desacoplamiento: El comando ya no conoce los detalles de la fuente de datos, solo sabe cómo trabajar con la interfaz
Esta actividad se construyó sobre la refactorización anterior para gestionar la creación de los diferentes tipos de importadores.
-
Problema Identificado: Aunque la Inversión de Dependencias desacopló el comando, el propio comando todavía tenía la responsabilidad de decidir qué clase concreta de importador instanciar (ej.
importer = CSVProductImporter(...)). Si se añadían más importadores, el comando necesitaría una lógica condicional (if/elif/else) para seleccionar el correcto, violando el Principio Abierto/Cerrado. -
Solución Aplicada:
- Se implementó el Patrón Factory (Fábrica) a través de una clase
ImporterFactoryensupermarket/services.py. - Esta fábrica tiene un método estático
get_importer(file_path)que centraliza la lógica de selección. Analiza la extensión del archivo y devuelve una instancia del importador concreto correspondiente (CSVProductImporter,JSONProductImporter, etc.). - El comando
import_csvfue modificado para usar esta fábrica. Ahora, en lugar de crear la instancia directamente, simplemente le pide a la fábrica que le proporcione el importador adecuado:importer = ImporterFactory.get_importer(file_path).
- Se implementó el Patrón Factory (Fábrica) a través de una clase
-
Mejoras Obtenidas:
- Centralización de la Creación: La lógica para crear importadores ahora reside en un único lugar, facilitando su gestión.
- Principio Abierto/Cerrado: El comando
import_csvahora está "cerrado" a modificaciones pero "abierto" a extensiones. Para soportar un nuevo formato, solo se necesita modificar la fábrica, sin tocar el código del comando. - Código más Limpio: Se elimina la lógica condicional de instanciación del cliente (el comando), resultando en un código más limpio y con una responsabilidad más clara.
Esta actividad se aplicó para simplificar la vista (controlador) generate_meal_plan.
-
Problema Identificado: La vista
generate_meal_plancontenía una cantidad excesiva de lógica de negocio. Orquestaba la obtención de datos, la construcción de un prompt complejo para la IA, la llamada a la API externa, el parseo y validación de la respuesta, y la gestión de la sesión. Esto hacía que la vista fuera difícil de leer, probar y mantener, violando el principio de Responsabilidad Única. -
Solución Aplicada:
- Se implementó el Patrón Facade (Fachada) creando la clase
MealPlanFacadeen un nuevo archivoia/services.py. - Toda la lógica compleja del subsistema de generación de planes (interacción con la base de datos, la API de OpenAI, etc.) se movió al interior de esta fachada.
- La fachada expone un único método simple,
generate_plan(), que orquesta todas las operaciones internas y devuelve un resultado claro (ya sea los datos del plan o un error). - La vista
generate_meal_planfue refactorizada para ser un cliente simple de la fachada. Ahora solo instanciaMealPlanFacadey llama a su métodogenerate_plan(), encargándose únicamente de la interacción HTTP (gestión de sesión y renderizado de plantillas).
- Se implementó el Patrón Facade (Fachada) creando la clase
-
Mejoras Obtenidas:
- Separación de Capas: Se logra una clara separación entre la capa de presentación (la vista) y la capa de lógica de negocio (la fachada).
- Simplificación del Controlador: La vista es ahora mucho más simple, limpia y centrada en sus responsabilidades de controlador.
- Encapsulación y Reusabilidad: El complejo subsistema de IA está encapsulado y puede ser reutilizado fácilmente por otras partes de la aplicación sin duplicar código.
- Mejora en la Testabilidad: La fachada puede ser probada de forma unitaria e independiente de la capa web.