Como independizar tu capa de lógica de tu capa de presentación…

A raiz del siguiente post del excelente blog de Oskar, Julio Trujillo comentó en un comentario (copio literalmente) “Sería interesante una explicación de como convertir Forms a WPF o al menos como poder diseñar una capa que permita conectar la capa de negocio a una de WPF o Forms indistintamente”. A este comentario respondí yo con unas cuantas ideas, pero luego Julio pidió a ver si podiamos exponer las “buenas prácticas” e incluso un ejemplo… Julio, no respondí a tu comentario, simplemente porque el tema es demasiado para un simple comentario, y se merece al menos un post… y estaba sacando tiempo 😉

Creo que el comentario de Julio, encerraba dos preguntas en una: cómo se puede convertir fácilmente nuestras aplicaciones winforms a wpf y por otro como reaprovechar el máximo código posible. Voy a exponer unas cuantas ideas que quizá os pueden ayudar pero que se resumen en dos: usad una arquitectura n-layer (por cierto que nosotros hablamos sólo de “n-capas” pero no confundais n-layer con n-tier), por un lado y el patrón separated presentation por el otro…

Arquitectura n-layer

En esta arquitectura, básicamente, distribuimos nuestro código en n-capas lógicas, donde n suele tener un valor cercano a 3: la separación clásica es presentación, lógica y datos. Aunque se pueden meter más (o menos, igual podemos prescindir de la capa de datos) capas lógicas (como p.ej. lógica de presentación).

Una arquitectura n-layer nos ayudará a reaprovechar nuestro código de la capa lógica con independencia de la capa de presentación que tengamos… Para ello basta seguir dos reglas que nos indicarán si vamos por buen camino:

  1. Nuestra capa de lógica debe estar en un proyecto separado (una librería de clases). Puede haber (y generalmente habrá) una referencia desde el proyecto que sea la capa de presentación al proyecto que es la capa de lógica pero nunca debe aparecer una referencia a la capa de lógica que vaya hacia la capa de presentación.
  2. El proyecto que contiene nuestra capa lógica no debe tener nunca ninguna referencia a ningún ensamblado de .NET que dependa, directa o indirectamente, de Winforms o WPF… P.ej. si te aparece una referencia a System.Windows.Forms… mal, porque estás ligando tu capa de lógica a una tecnología de presentación.

La comunicación desde la capa de presentación a la capa lógica puede ser acoplada: lógico, disponemos de una referencia en la capa de presentación a la capa lógica y por lo tanto podemos instanciar cualquier clase pública de la capa de lógica y llamar a sus métodos directamente.

La comunicación desde la capa lógica a la capa de presentación debe ser desacoplada: no tenemos otra opción, dado que no podemos tener ninguna referencia a la capa de presentación desde la capa lógica. Aquí tenemos tres alternativas válidas:

  1. Comunicación pasiva: es decir, la capa de lógica se limita a devolver toda la información que la capa de presentación solicita mediante los valores de retorno de los métodos. Así, la comunicación la inicia siempre la capa de presentación.
  2. Eventos, o cualquier mecanismo similar (como commands): Cuando la capa de lógica quiere informar de algo a la capa de presentación, lanza eventos a los que está suscrita la capa de presentación y esta actúa en consecuencia. Esto permite que la capa lógica realice envíos de información a la capa lógica sin ser necesario que esta última inicie la comunicación.
  3. Utilizar interfaces junto con un patrón service locator. Esta alternativa precisa el uso de un contenedor IoC (como Unity o Windsor), así como la aparición de un tercer assembly destinado a contener las interfaces. Usando esta aproximación, las clases de la capa de presentación implementan todas ellas las interfaces (definidas en el assembly aparte) y la capa de lógica obtiene referencias a los objetos de la capa de presentación usando las interfaces y el contenedor de IoC. Esta alternativa permite una conversación “tu-a-tu” con la capa de presentación.

Por supuesto las tres alternativas pueden combinarse.

Esto independiza nuestra capa de lógica (y de datos) de la presentación usada. Si alguna vez queremos migrar nuestra aplicación, p.ej. de winforms a wpf, sólo deberemos reescribir la capa de presentación… lo que según como esté diseñada puede ser un trabajo asumible o una obra de titanes.

Separated Presentation

Bajo este nombre se agrupan multitud de patrones (MVC, MVP, MVVM), pero todos ellos coinciden en lo básico: separa siempre tu código que muestra los datos (el formulario) del código que indica cuando y como debe mostrarlos.

Lamentablemente, desde los tiempos de Visual Basic, Microsoft nos acostumbra a desarrollar usando el “patrón formulario”, que básicamente consiste en crear un form, rellenar-lo de controles y asociar eventos a funciones tipo MyButton_Click() que contendrán todo el código. Este modelo es rápido, fácil de entender y produce buenos resultados… hasta que uno debe empezar a cambiar cosas. A lo mejor al cabo de unos meses en tu aplicación le aparece otro formulario que se parece mucho a un formulario ya existente pero a lo mejor muestra un par de controles más o se comporta ligeramente distinto… O bien haces copy-paste del primer formulario en uno nuevo y lo modificas (funciona pero luego vas a mantener dos formularios parecidos) o empiezas a meter ifs en el primer formulario hasta que todo se vuelve un galimatías.

Créeme: Olvida el “patrón formulario” cuanto antes. Sirve para pequeñas aplicaciones que no precisen un mantenimiento excesivo, pero para aplicaciones mayores usa alguna variante de separated presentation.

Si nos centramos en el caso concreto de Winforms y WPF, una de las mejores elecciones es MVP: si ya sabes que tu aplicación deberá ser migrada (o bien debe ser multi-presentación) entonces MVP te ahorrará bastante trabajo: “básicamente” sólo deberás rediseñar los formularios y podrás aprovechar el modelo y los presenters. Yo por ejemplo suelo usar siempre MVP cuando desarrollo en Winforms (no por temas de migración, sino porque considero que es el patrón que aplica mejor) y cuando estoy en WPF uso MVVM o MVP dependiendo de la ocasión.

Reconozco que si se debiera migrar una aplicación WPF creada usando MVVM a winforms habría bastante trabajo, pero de todos modos muchas veces si se migra una aplicación a una tecnología de presentación nueva es para aprovechar las nuevas capacidades (me cuesta imaginarme una migración de WPF a winforms, mientras que al revés puede ser más habitual).

En fin, migrar una aplicación a otra tecnología de presentación siempre cuesta trabajo, pero si separamos bien las capas (n-layer) y organizamos bien nuestra capa de presentación (separated presentation) tendremos mucho de ganado…

Saludos!!!!

16 comentarios sobre “Como independizar tu capa de lógica de tu capa de presentación…”

  1. Hola Eduard

    Cuando dices «desde los tiempos de Visual Basic, Microsoft nos acostumbra a desarrollar usando el “patrón formulario » das de lleno en el problema. Para los desarrolladores que venimos de VB6 existe un serio problema de acoplamiento entre UI y lógica de negocio.

    Decirte que me has aclarado algo muy básico (pero no trivial, es muy importante seguir estas recomendaciones que aportas sobre las referencias de ensamblados) y considero que es vital tener presente como primer paso al modelo de capas.

    Además, siguiendo el patrón de la interfaz «PropertyChanged» (creo que era el ‘observer’) que eleva eventos desde la capa de negocio hacia UI (y también a la inversa) puede ser un segundo paso, ya que continua con el desacoplamiento.

    Muchas gracias por haber ayudado a clarificar más la arquitectura de este modelo de desarrollo

    Un saludo

  2. yo soy otro de los «VB6-eros» aunque me estoy «kitando» .
    Bueno el caso.
    Siempre piensas en app de escritorio..
    Pero en mi caso quiero utilizar ASP o WPF , pienso utilizar un patron , Asi que voy a investigar las propuestas dadas….

    Pero si teneis alguna propocision diferente me gustaria que la comentaseis, asi tenerla en cuenta..

  3. Hola, gracias por vuestros comentarios 🙂

    @manu

    Me he centrado en aplicaciones de escritorio simplemente porque el comentario de Julio que ha originado mi post trataba sobre winforms y wpf, pero todo lo que he dicho es aplicable entre aplicaciones desktop y web.

    Si debes realizar una aplicación que sea a la vez desktop y web se impone forzosamente una estructura n-layer: tu capa de lógica será la misma con independencia de la capa de presentación.

    ¿Y para la presentación? Pues ya depende, las capacidades de WPF y de Web son bastante distintas, así que seguramente deberás rehacer bastante trabajo. Sobre patrones «separated presentation» en web suele usarse mucho MVC. Incluso MS ha sacado su implementación para ASP.NET (originalmente llamada ASP.NET MVC), que personalmente a mi *me encanta* al que te recomiendo sin duda que le eches un vistazo.

    @Julio

    Encantado que te haya gustado el post… 🙂

    [Edito]: Efectivamente el patrón «PropertyChanged» se engloba también dentro de la comunicación desacoplada (no deja de ser un evento)… y por cierto es una de las bases de MVVM!

    Un abrazo!

  4. Hola Eduard, muy bueno el articulo a ver si saco algo de tiempo y puedo realizar algunos posts que tengo pensados sobre el patron MVVM para WPF

    Saludos nos vemos en el CodeCamp

  5. Hola Eduard,

    en el articulo hablas de separated presentation y n-layer como dos cosas diferentes. No seria la primera un caso particular de la segunda?

    Gracias,

  6. @Vicenç
    En mi opinión n-layer se centra en separar las distintas capas de tu aplicación (generalmente datos, lógica y presentación), mientras que separated presentation son un conjunto de patrones que te indican como «ordenar» tu código, pero SÓLO en la capa de presentación.

    De todos modos hay semejanzas, dado que ambos lo que tratan es desacoplar… Nunca había considerado separated presentation un caso particular de n-layer, pero igual sí que podría ser un «n-layer centrado en presentación» 🙂

    Saludos y gracias por tu comentario!

  7. Hola Eduard,

    muchas gracias por la respuesta. Me miraré entonces el enlace que has puesto de separated presentation para ver si me aclaro pq veo que no lo tengo muy claro.

    Si tengo dudas ya te las comento.

    Muchas gracias!

  8. Fantástico! IoC y MVC son mis dos quebraderos de cabeza acutales.

    Los entiendo por separado pero no veo muy claro cuando pueden trabajar juntos… sí entiendo por ejemplo que una app puede tener un proxy, un observer y un MVC pero como digo MVC+IoC (locator por ejemplo) no lo veo 🙁

    Seguire currandomelo…

  9. @Galcet

    MVC y IoC pueden «trabajar» juntos sin problema! De hecho se complementan a la perfección 😉

    Con MVC tienes una manera de organizar el código de tu capa de presentación: así tienes una separación entre modelos, vistas y controladores. Pero separación de código no implica necesariamente código desacoplado: así tus controladores dependerán directamente de las vistas p.ej. Puedes meter interfaces para empezar a desacoplar código, pero las dependencias no las puedes eliminar fácilmente (en algún sitio tendrás el new de la vista o del controlador).
    Además de que seguro que tendrás dependencias de tus controladores y modelos a clases externas, como p.ej. un servicio de log.
    IoC te ayuda a desacoplar todo este código. Por un lado usando «service locator» desacoplas tus modelos y controladores de toda clase externa: Necesitas un servicio de log? Pídelo al «service locator» y que este te devuelva una instancia de una clase X que implemente ILog. Tus dependencias son única y exclusivamente con ILog. Nunca en tu código habrá un new MyLogService(). Evidentemente en algún sitio se configura el «service locator» para que sepa que ILog es en realidad MyLogService, pero esto se puede hacer en un bootstrapper o por configuración en ficheros.
    El otro patrón típico de IoC, la inyección de dependencias te permite desacoplar tus vistas, controladores y modelos. Un controlador requiere acceso a una vista? Deja que el contenedor IoC inyecte esta vista en el controlador: tu no creas la vista, lo hace el contenedor de IoC por tí, y una vez creada informa al controlador cual es esta vista. El controlador puede que lo hayas creado tu, pero tampoco lo harás directamente, lo obtendrás va el «serivce locator».

    Se puede usar MVC sin IoC perfectamente, pero combinando ambos obtienes lo mejor: una separación de tu capa de presentación y además un desacople total de tu código!

    Un abrazo!

  10. Mil gracias…

    En esta frase lo has clavao «Pero separación de código no implica necesariamente código desacoplado»… una explicación barbara. Te puedo decir una cosa…»tu, tu eres bueno, eres muy bueno» 😀

    Otra cosita, a lo mejor no soy el único que tiene esta duda, por lo que probablemente un post sobre esta mezcle de patrones nunca estaria mal. (verdad??)

    Otra vez gracias. Sinceramente he visto el matrix 😀

    Galcet.

  11. Solo una cosa más, lo prometo.

    Me puedes recomendar un libro, ebook,… sobre patrones??? y por favor no me redirecciones a M.Fowler. 🙂

  12. Efectivamente como decís todos el problema es la costumbre del patrón formulario.
    En mi desarrollo actual tengo un nivel de desacoplamiento bastante alto en la parte de Acceso a datos usando modelado tradicional capa-entidad de la base de datos con Data Access AppBlock de la EntLib 3.1, pero en el resto esta separada la lógica de la entidad y la lógica de los forms en componentes y luego los forms.

    Así que tengo las entidades mapeadas a la antigua a clases dividiendo en clase de acceso a datos y la clase en si mas su lógica esencial, tras ello la lógica de procesos separada en forma de clases especiales y componentes de winforms, para luego aplicarlos en formularios de windows.
    Vamos que lo único separado correctamente es las entidades luego el resto es un mix.

    Estaré al tanto de tus post sobre el tema.

    Saludos!

  13. Muy cierto lo que dices, en especial el punto de si separamos bien las capas podemos migrar mas facil una aplicación. En realidad no solo migrarla, si no tambien exponer diferentes tipos de UI para la misma aplicación ya que siempre se usa la misma lógica de negocios – o capa de servicios – y lo que varia es realmente como invocamos esta capa desde el UI.

    Saludos

  14. @Diego
    Efectivamente, exponer distintos tipos de UI es una causa mucho más habitual que la de migrar la UI… me centraba en la migración porque esa fue la pregunta que originó mi post 🙂

    @Todos
    Gracias por vuestros comentarios!!!

Responder a anonymous Cancelar respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *