Photo by Guillaume de Germain on Unsplash

Un Flutter Más Limpio Vol. 3: Dominando las Entidades

Entrando a la capa de dominio con entidades

Marcos Sevilla
Published in
6 min readJan 11, 2021

--

Ya vimos bastante de las reglas del juego con CLEAN en las ediciones anteriores de Un Flutter Más Limpio, ahora nos toca entrar a escribir código.

Así que busca tus lentes de desarrollador y ensuciémonos un poco las manos.

⚠️ Todo el código que vamos a escribir en este artículo es Dart puro, sin dependencias al SDK de Flutter.

Capa de dominio

Componentes de la capa de dominio

⚠️ Voy a alternar bastante estos dos términos en la siguiente sección así que para que no se confundan:

Clases abstractas son lo mismo que interfaces. En Dart, no hay una palabra reservada para crear una interfaz, pero la clase abstracta cumple esa exacta función.

Antes de explicar entidades, les informo que con este artículo empezamos a explicar la capa de dominio de nuestro proyecto, que consiste en los contratos que nosotros vamos a establecer en nuestro proyecto.

No vamos a firmar nada, es una expresión. Ya te explico qué son contratos.

Les decimos contratos a las clases abstractas o interfaces (si venís de otros lenguajes) porque delimitan reglas: propiedades, acciones… que se deben cumplir para una clase en específico.

INetworkManager es nuestro contrato o interfaz

En este ejemplo, podemos ver una clase abstracta/interfaz que define un administrador de la red. A esta clase se le llama contrato porque cualquier clase que extienda de ella, tiene que implementar sus métodos y tiene acceso a sus propiedades. A como podemos apreciar en la siguiente captura:

NetworkManager firma el contrato de INetworkManager y está forzado a implementar sus métodos

Aquí vemos que como NetworkManager no implementa isConnected, lanza un error. Entonces se dice que NetworkManager firma un contrato para implementar INetworkManager.

¿Se entiende la idea? Genial. Y ahora, ¿por qué hacemos contratos?

A como lo hablamos en los artículos anteriores, necesitamos que nuestro código sea escalable y eso implica que sea fácil de extender. De esa manera, teniendo clases abstractas podemos hacer las implementaciones que queramos de las interfaces tomando en cuenta distintos tipos de tecnologías o en el caso de Flutter, pueden ser distintos paquetes con una implementación por aparte para cada uno.

Y adicional a todo esto, los contratos nos facilitan bastante el testing de nuestra app, ya que podemos crear clases de prueba con paquetes como mockito y de esa manera correr nuestras pruebas en clases que tengan los métodos establecidos en nuestros contratos.

Ahora que dejamos eso claro, vamos con las entidades.

Entidades

Cuando vas a iniciar un proyecto desde cero, ¿qué programás primero?

En mi caso, programo las clases de los datos que voy a ocupar a lo largo de toda mi aplicación. Considero que esto es una buena práctica ya que nos ayuda a determinar la información que va a fluir por toda nuestra lógica.

Las entidades pueden ser clases comunes y corrientes que representen abstracciones de actores en la vida real, o bien algo muy abstracto como pueden ser clases propias de un framework como RenderObject.

No nos vamos a complicar mucho.

Las entidades deben ser nuestros tipos de datos, o bien clases, que se utilicen en distintas partes de nuestro software.

Al menos eso dice el tío Bob, y me parece correcto. Si bien todo termina siendo una entidad incluso en la capa más externa, definimos en nuestra versión de Clean que nuestras entidades son los objetos que nos puede retornar-o nosotros enviar a- un API, tomando el ejemplo más claro.

Cuando veamos los modelos en la capa de datos probablemente entiendan la utilidad de abstraer primero una entidad que simplemente defina propiedades y algunos comportamientos generales y luego extender su funcionalidad en modelos que implementen métodos más puntuales.

Digamos en resumen que una entidad tiene como función definir las características esenciales de un objeto. Las características específicas las implementa el modelo, porque los modelos son los encargados de usarse para casos de uso específicos.

El código sería así:

También podemos implementar y extender clases no abstractas

Response nos especifica las propiedades que toda respuesta del API debe tener. Estas son respuestas simples para simplificar el ejemplo, ya sé que no todas las respuestas en un API van a regresar lo que tienen estas clases. Déjenme terminar.

Ahora que tenemos nuestro contrato, podemos crear un modelo que lo “firme”, básicamente me refiero a que lo implemente o extienda. Le digo firmar ya que al heredar de esa clase estamos aceptando implementar todos los métodos que tiene y cumplir con las propiedades que tiene.

Por ejemplo con ResponseModel, creamos un String en el mismo constructor y simplemente le pasamos esa propiedad a la clase de la que estamos heredando, para cumplir con esa propiedad.

En este otro ejemplo hacemos exactamente lo mismo que en el anterior, con la única diferencia que agregamos una propiedad que ya no es base de nuestra entidad Response y así cumplimos una de las características de las que hablábamos en los volúmenes anteriores: extendemos la funcionalidad existente sin alterarla en el proceso.

Un punto extra válido a mencionar es que como esta es la base de todo nuestro gráfico, cuando hagamos un cambio a nivel de entidad vamos a tener errores en nuestros modelos y por la misma razón, en cualquier otra parte que se usen estos modelos.

Ahí vemos en acción la regla de la dependencia y cómo nos ayuda a tener un control de los cambios en nuestro código por las partes que dependen de otras.

Ok, ahora sí. Tal vez di mucha vuelta sobre algo bien simple pero me interesa que se entienda el propósito de esta separación. Fácilmente podríamos hacer de una vez nuestros modelos con todas las características que consideremos necesarias, pero el código que hagamos se volvería un poco repetitivo y hasta desordenado.

Espero que hayan asimilado bien los conceptos y sobre todo la separación en capas que estamos haciendo para extender la funcionalidad y orden de nuestro código.

Esta era la parte más sencilla y base para el resto (fuera de lo conceptual), en el siguiente volumen vamos a seguir explorando la capa de dominio con los repositorios, les prometo que esa va a hacer un poco más entretenida.

Lo de siempre…

Si aprendiste algo nuevo y te fue de utilidad, podés compartir este artículo para ayudar a otro/a desarrollador(a) a seguir mejorando su productividad y calidad al escribir aplicaciones con Flutter.

También hay una versión de este mismo artículo en inglés publicado en dev.to. De nada. 🇺🇸

Además, si te gustó este contenido, podés encontrar aún más y seguir en contacto conmigo en mis redes sociales:

Originally published at http://github.com.

--

--