Quantcast
Channel: Adictos al trabajo
Viewing all 996 articles
Browse latest View live

Aplicaciones web con Spring Boot capa a capa

$
0
0

En este tutorial vamos a aprender a desarrollar una aplicación web con los recursos que nos brinda Spring Web.

Índice de contenidos


1. Introducción

Muchos de vosotros habréis oído hablar de Spring Boot. Para los más despitadillos, os diré que se trata de un proyecto creado a partir de Spring, el cual nos permite desarrollar y arrancar de forma muy rápida aplicaciones basadas en Spring. Hoy os voy a demostrar que realmente esto es así, y para ello vamos a desarrollar una aplicación web muy sencillita, paso a paso. La aplicación consistirá en un pequeño servicio que nos muestra un mensaje de bienvenida al ser invocado.

Bueno, ¡pues comencemos a programar! 😀


2. Entorno

Este tutorial ha sido realizado en un entorno con las siguientes características:

  • Hardware: MacBook Pro Retina 15’ (2,5 GHz Intel Core i7, 16 GB DDR3)
  • Sistema Operativo: OS X El Capitan 10.11.5
  • Entorno de desarrollo: IntelliJ IDEA Ultimate 2016.1
  • Java 1.8

3. Creando el proyecto

En primer lugar, creamos un proyecto maven. Para indicar que queremos utilizar Spring Web con Spring Boot, añadimos lo siguiente al fichero pom.xml:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.4.1.RELEASE</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

De este modo, el proyecto que acabamos de crear extiende del proyecto padre spring-boot-starter-parent, e incluye las dependencias agrupadas en el starter spring-boot-starter-web. Un starter es un conjunto de dependencias que nos ayudan a cubrir las necesidades de un tipo de proyecto concreto. Por ejemplo, el starter que estamos utilizando sirve para cubrir las necesidades de un proyecto web. Más adelante utilizaremos otros starters, entre ellos el que nos ayuda a integrar MyBatis en la aplicación.

Podemos definir en qué versión de Java desarrollaremos nuestra aplicación, añadiendo lo siguiente al fichero pom.xml:

<properties>
    <java.version>1.8</java.version>
</properties>

En este caso, la versión que vamos a emplear será la 1.8. Otra cosa importante es la definición de una buena estructura de paquetes. Un buen ejemplo puede ser el siguiente:

paqueteria

Esta estructura de paquetes agrupa las clases en cuatro paquetes principales: mapper para la capa de acceso a datos, repository para la capa de repositorio, service para la capa de servicio, y web para la capa controlador.

No hay que seguir este ejemplo al pie de la letra ni mucho menos, es más, puede que la estructura de paquetes de otros proyectos sea muy distinta pero totalmente válida. Lo que pretendo mostraros es que debe existir una estructura de paquetes ordenada para que la aplicación sea mantenible y para que la responsabilidad de las clases quede bien clara.


4. Definiendo la clase principal

Toda aplicación en java debe contener una clase principal con un método main. Dicho método, en caso de implementar una aplicación con Spring, deberá llamar al método run de la clase SpringApplication. A continuación, definimos de una forma muy fácil la clase principal de nuestra aplicación.

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }
}

La etiqueta @Configuration, indica que la clase en la que se encuentra contiene la configuración principal del proyecto.

La anotación @EnableAutoConfiguration indica que se aplicará la configuración automática del starter que hemos utilizado. Solo debe añadirse en un sitio, y es muy frecuente situarla en la clase main.

En tercer lugar, la etiqueta @ComponentScan, ayuda a localizar elementos etiquetados con otras anotaciones cuando sean necesarios.

Para no llenar nuestra clase de anotaciones, podemos sustituir las etiquetas @Configuration, @EnableAutoConfiguration y @ComponentScan por @SpringBootApplication, que engloba al resto.

@SpringBootApplication
public class Application {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }
}

5. Empaquetando el proyecto y arrancando el servidor

Tras haber definido la clase principal del proyecto, podemos proceder a empaquetarlo y arrancar el servidor con nuestra aplicación. Si hemos añadido correctamente las dependencias al fichero pom.xml no debería haber ningún problema de empaquetado, y maven no nos devolvería ningún error en tiempo de compilación.

Para salir de dudas, vamos a ejecutar el siguiente comando en el directorio raíz del proyecto:

$ mvn clean package

Si todo va bien, tras ejecutar esta instrucción, se generarán los ficheros .class a partir de las clases .java y se empaquetará el proyecto en un fichero .jar. Podemos ver también qué dependencias han sido incluidas en el proyecto, es decir, qué dependencias engloban tanto los starters añadidos como el proyecto padre. Esto es posible con el siguiente comando:

$ mvn dependency:tree

Por otro lado, los proyectos de tipo Spring Boot integran un servidor de aplicaciones, por lo que arrancar una aplicación Spring Boot es muy fácil.

En el directorio raíz del proyecto ejecutamos el siguiente comando:

$ mvn spring-boot:run

Si no se produce ningún error en tiempo de ejecución, el servidor estaría levantado y listo para recibir peticiones.


6. Desarrollando la capa controlador

Definamos ahora el comportamiento de la aplicación implementando el resto de clases. Vamos a comenzar por la capa de más alto nivel, la de los controladores, donde expondremos los servicios de la aplicación.

El servicio que vamos a crear tendrá un comportamiento muy simple. Recuperará de base de datos un mensaje de bienvenida cuyo contenido variará en función del idioma del usuario, recibiendo como parámetro el mismo nombre de usuario. El comportamiento del controlador será aún más sencillo, ya que lo único que hará será llamar a la capa de servicio y devolver lo que ésta nos retorne.

Comenzaremos por el desarrollo del test que valide el controlador. Nuestro controlador se llamará SampleController, así que el test del controlador se llamará SampleControllerTest y estará en el mismo paquete que SampleController pero en el directorio test. Necesitaremos incluir una serie de dependencias englobadas en el starter spring-boot-starter-test:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

El contenido de la clase de test será el siguiente:

public class SampleControllerTest {

    private SampleController sampleController;

    private SampleService sampleService;

    @Before
    public void init(){
        sampleService = mock(SampleService.class);
        sampleController = new SampleController(sampleService);
    }

    @Test
    public void sampleControllerShouldCallService() {
        String userName = "nroales";
        String expectedMessage = "message";

        when(sampleService.welcome(userName)).thenReturn(expectedMessage);

        String message = sampleController.welcome(userName);

        verify(sampleService).welcome(userName);
        assertTrue(message.equals(expectedMessage));
    }
}

Hemos declarado un atributo de la clase SampleService, ya que lo que vamos a probar es que el controlador invoque a la capa de servicio y que devuelva lo mismo, así que debemos crear SampleService para que el test compile. Además, lo mockeamos para simular su comportamiento, pues el objetivo de este test no es probar la capa de servicio.

Vamos a hacer que el test pase de rojo a verde con la siguiente implementación de SampleController:

@Controller
public class SampleController {

    @Autowired
    private SampleService sampleService;

    public SampleController(SampleService sampleService) {
        this.sampleService = sampleService;
    }

    @RequestMapping(value = "/welcome/{userName}", method = RequestMethod.GET)
    @ResponseBody
    public String welcome(
            @PathVariable("userName") String userName
    )
    {
        return sampleService.welcome(userName);
    }
}

En el fragmento de código anterior aparecen algunas anotaciones. Vamos a ver qué significa cada una de ellas:

  • @Controller: Con esta anotación Spring podrá detectar la clase SampleController cuando realice el escaneo de componentes.
  • @Autowired: A través de esta anotación Spring será capaz de llevar a cabo la inyección de dependencias sobre el atributo marcado. En este caso, estamos inyectando la capa de servicio, y por eso no tenemos que instanciarla.
  • @RequestMapping: Con esta anotación especificamos la ruta desde la que escuchará el servicio, y qué método le corresponde.
  • @ResponseBody: Con ella definimos lo que será el cuerpo de la respuesta del servicio.
  • @PathVariable: Sirve para indicar con qué variable de la url se relaciona el parámetro sobre el que se esté usando la anotación.

Podemos también utilizar la etiqueta @RestController en lugar de @Controller, que sustituye al uso de @Controller + @ResponseBody, quedando el controlador de la siguiente forma:

@RestController
public class SampleController {

    @Autowired
    private SampleService sampleService;

    public SampleController(SampleService sampleService) {
        this.sampleService = sampleService;
    }

    @RequestMapping(value = "/welcome/{userName}", method = RequestMethod.GET)
    public String welcome(
            @PathVariable("userName") String userName
    )
    {
        return sampleService.welcome(userName);
    }
}

7. Desarrollando la capa de servicio

Aunque a partir de este punto no aparezcan las clases de test, no quiere decir que no sean necesarias para completar el desarrollo que estamos realizando. Sin embargo, he decidido omitirlas para que el tutorial no se extienda demasiado, pero siempre es recomendable respaldar nuestra aplicación con una batería de pruebas (y más aún hacer TDD).

Vamos implementar la capa de servicio. Un método de servicio definirá una operación a nivel de negocio, por ejemplo, dar un mensaje de bienvenida. Los métodos de servicio estarán formados por otras operaciones más pequeñas, las cuales estarán definidas en la capa de repositorio. El mapper, por último, contendrá las operaciones de acceso a datos que serán invocadas por el repositorio.

En este caso, el servicio realizará una sola llamada al repositorio, pasándole como parámetro el nombre de usuario. Lo llamaremos SampleService.

@Service
public class SampleService {

    @Autowired
    private SampleRepository sampleRepository;

    public SampleService(SampleRepository sampleRepository) {
        this.sampleRepository = sampleRepository;
    }

    public String welcome(String userName) {
        return sampleRepository.getMessageByUser(userName);
    }

}

La anotación @Service funciona de forma parecida a la anotación @Controller, ya que permite que Spring reconozca a SampleService como servicio al escanear los componentes de la aplicación.


8. Desarrollando la capa de repositorio

Ahora tenemos que desarrollar el repositorio al que ha invocado el servidor. Por tanto, crearemos la clase SampleRepository e implementaremos el método getMessageByUser.

@Repository
public class SampleRepository {

    @Autowired
    private SampleMapper sampleMapper;

    public SampleRepository(SampleMapper sampleMapper) {
        this.sampleMapper = sampleMapper;
    }

    public String getMessageByUser(String userName) {
        String language = sampleMapper.getLanguageByUser(userName);

        return sampleMapper.getMessageByLanguage(language);
    }
}

Para recuperar el mensaje de bienvenida dado el nombre de usuario tendremos dos métodos en la capa de acceso a datos, siendo uno para recuperar el idioma dado el usuario, y otro para recuperar el mensaje dado el idioma. Desde el repositorio llamamos a los dos.


9. Desarrollando la capa de acceso a datos

Ya solo nos queda implementar la capa de acceso a datos. En esta capa es en donde se definen las consultas a base de datos, a través de interfaces denominadas mappers. Vamos a crear el mapper con los dos métodos invocados en el repositorio, que son getLanguageByUser y getMessageByLanguage.

@Mapper
public interface SampleMapper {

    String getLanguageByUser(@Param("userName") String userName);

    String getMessageByLanguage(@Param("language") String language);
}

Utilizamos la etiqueta @Mapper para indicar que una interfaz es un mapper, y así Spring pueda localizarla. También utilizamos la etiqueta @Param para que MyBatis identifique los campos a la hora de procesar las consultas.

Para poder trabajar con MyBatis debemos incluir algunas dependencias, agrupadas dentro del starter mybatis-spring-boot-starter:

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.1.1</version>
</dependency>

También debemos añadir el conector correspondiente a la base de datos que vayamos a utilizar. Podemos hacerlo incluyendo su dependencia maven en el pom.xml. En caso de utilizar una base de datos MySQL añadimos la siguiente dependencia:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.6</version>
</dependency>

Y en el fichero application.properties, localizado en el directorio resources, añadiremos la información de nuestra base de datos, siendo testdb el nombre de la base:

spring.datasource.url=jdbc:mysql://localhost:3306/testdb
spring.datasource.username=user
spring.datasource.password=pass
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

Una vez tenemos todo lo necesario para trabajar con MyBatis, definimos cada uno de los dos métodos del mapper. Dentro de la carpeta resources, creamos una estructura de directorios idéntica a la estructura de paquetes donde se encuentra SampleMapper.java, y creamos el fichero SampleMapper.xml.

Tendrá el siguiente contenido:

<mapper namespace="com.autentia.demo.mapper.SampleMapper">

    <select id="getLanguageByUser" resultType="String">
        SELECT USL_LANGUAGE FROM USER_LANGUAGE WHERE USL_USER = #{userName}
    </select>

    <select id="getMessageByLanguage" resultType="String">
        SELECT MSG_DESCRIPTION FROM MESSAGES WHERE MSG_MESSAGE = "welcome" AND MSG_LANGUAGE = #{language}
    </select>

</mapper>

La primera query recupera de la tabla USER_LANGUAGE el campo USL_LANGUAGE dado el valor del campo USL_USER, obteniéndose el idioma asociado al usuario userName. La segunda query, recupera el campo MSG_DESCRIPTION de la tabla MESSAGES dado el valor del campo MSG_LANGUAGE para MSG_MESSAGE igual a “welcome”, obteniéndose el mensaje de bienvenida en el idioma del usuario. Aunque parezca obvio, vuestras consultas deberán concordar con el diseño de la base de datos.

Lo que aparece dentro de #{userName} y #{language} son los identificadores que hemos designado a los parámetros de entrada con las anotaciones @Param.


10. Todo listo, ¡invoquemos al servicio!

Es el momento de la verdad… ¡Vamos a invocar a nuestro servicio! Ya sabéis, para ello levantamos el servidor con el siguiente comando:

$ mvn spring-boot:run

Una vez haya arrancado el servidor procedemos a invocar al servicio, y para ello accedemos a la url http://localhost:8080/welcome/userName. Tenemos que sustituir userName por el nombre de usuario que hayamos guardado en nuestra base de datos. Como respuesta, veremos en el navegador el mensaje de bienvenida que hayamos definido.


11. Conclusiones

Gracias a Spring Boot nos acabamos de marcar en un momento una aplicación totalmente funcional.

Hemos comenzado creando un proyecto maven e indicando en el pom.xml que el proyecto es de tipo Spring Boot, heredando de spring-boot-starter-parent el proyecto creado. Después hemos elegido el starter que más se ajusta a las necesidades de nuestro tipo de proyecto, en este caso spring-boot-starter-web, y lo hemos añadido como dependencia. Luego hemos creado la clase principal de la aplicación, implementando posteriormente las clases e interfaces que definen su comportamiento, y por último hemos arrancado el servidor.

El tiempo que hemos perdido en la configuración del proyecto es mínimo, y solo nos hemos tenido que preocupar de implementar los métodos que definan el comportamiento de los servicios. Tampoco hemos perdido tiempo en montar el servidor de aplicaciones, ya que Spring Boot cuenta con un Tomcat embebido.

Ya no tenéis excusa para no desarrollar proyectos web en Java. ¿Habéis visto qué fácil y rápido es tener una aplicación Java web funcional desde cero? Os animo a que lo probéis 😉

¡Hasta el próximo tutorial!


12. Referencias


Aprende a generar ideas. Desarrolla tu creatividad

$
0
0

Esta vez os comparto el vídeo de la charla impartida en Autentia Aprende a generar ideas. Desarrolla tu creatividad.

Como ya comentaba en el post sobre el libro Creatividad S.A. de Edwin Catmull, me surgió el interés en aprender qué era la creatividad, qué podemos hacer para ser más creativos y qué puede hacer una empresa para crear un entorno en el que se fomentara la creatividad de sus empleados para dar como resultado productos innovadores y originales, tras empezarse a desarrollar el primer producto de mi empresa Autentia, y que ya ha sido lanzado TEAMS.

Por tanto, con la información recabada del libro antes mencionado, y del libro ÁgilMente de Estanislao Bachrach, del cual también dejé una entrada en este mismo portal , solo faltaba compartir ese nuevo conocimiento adquirido con mis compañeros. Así fue como surgió la siguiente charla impartida en nuestra oficina, de la que os comparto el enlace y espero que os guste.

Tendencias del diseño gráfico para 2017

$
0
0

¿Cuáles son las tendencias en diseño gráfico para el 2017? Claves para hacer un site a la última.

Índice de contenidos

  • 1. Introducción
  • 2. Tendencias 2017
  • 3. Conclusiones

  • 1. Introducción

    El mundo del diseño web avanza a una velocidad vertiginosa, pero eso ya lo sabemos. Así que, estar al día es una obligación. Para el año 2017, aquellos que queráis hacer una página web o rediseñar la que tenéis, tendréis que tener en cuenta lo primero, que ya hay aspectos que se han vuelto esenciales y necesarios como el diseño responsive optimizado, el scroll vertical o la inclusión de redes sociales en el site; y lo segundo, las tendencias. Pero ¿cuáles son las tendencias en diseño gráfico para el 2017?


    2. Tendencias 2017

    Las tendencias en diseño web son las prácticas que se han ido asentando por mejorar la experiencia de usuario en la navegación.

    • Diseño tipo tarjetas
    • Diseño_Tarjetas

      Cada tarjeta presenta información independiente de una forma visual y atractiva, lo que mejora la usabilidad.

    • Microinteracciones
    • Microinteracciones_Like

      Son pequeñas interacciones del usuario con la interfaz que se suceden a diario: hacer click en un “like”, animación al enviar un mensaje, mensajes de texto “humanos”…

    • Material Design
    • Material_design

      “A visual language for our users that synthesizes the classic principles of good design with the innovation and possibility of technology and science.” Así define Google el material design. El principal objetivo es crear diseños limpios y modernos que parezcan más realistas para los usuarios y así mejorar la usabilidad. Para ello utilizaremos capas en el diseño de las interfaces; tipografías, grids y colores que ya no sólo son decorativos, sino que aportan jerarquía, significado y foco; o la continuidad visual a través de transiciones y surface reactions.

    • Evolución del Flat Design
    • Flat_Design

      El Flat Design se queda con nosotros este año gracias a su compatibilidad con la estética minimalista, el material design o el diseño responsive. Tenderá a los gradientes, sombras largas, pequeñas animaciones y simulación de capas 3D.

    • Animaciones
      • De carga: aquellas animaciones que sirven para entretener al usuario en los procesos que requieren que éste espere.
      • Cinemagraphs: o fotografías animadas.
      • Cinemagraphs

        Johan Blomström [CC BY 2.0 (http://creativecommons.org/licenses/by/2.0)], Wikimedia Commons


    3. Conclusiones

    El diseño gráfico en 2017 centra su atención en la usabilidad y la optimización sin olvidar el impacto visual. Se implanta el responsive y perdemos el miedo al scroll vertical. Añadimos interactividad, animación, imágenes full screen o iconos con un sentido más funcional, sin olvidar la sencillez y capacidad intuitiva de los elementos. Flat design y Material design se unen para dar una mejor experiencia de usuario, más real y auténtica.

Infraestructura Inmutable. Qué es y por qué deberíamos implantarla en nuestra organización

$
0
0

A través de este artículo se pretende explicar qué es y en qué se basa el concepto de Infraestructura Inmutable, sus principales ventajas y cuáles son las aproximaciones por las que podemos optar para poder implantarlo en nuestra organización.

Índice de contenidos


1. Introducción

La reducción drástica del coste del hardware unido a la explosión de proveedores de infraestructura en la nube (IaaS) han sido el hilo conductor de lo que se conoce como Infraestructura Inmutable. En este artículo descubrirás en qué se basa este concepto y cuáles son las espectaculares ventajas de uso.


2. ¿Qué es infraestructura inmutable?

El concepto de infraestructura inmutable deriva del concepto de programación “Inmutabilidad”, es decir, que una vez se crea o se instancia algo, éste nunca será modificado.

En base a este concepto, la infraestructura o el contenedor donde se va a ejecutar nuestra aplicación también forma parte de nuestro producto, y no solamente la aplicación en sí misma.

Esto significa que una vez la instancia esté funcionando, esta no se verá modificada. Cuando se requiera la aplicación de algún cambio una nueva instancia será creada, pero la anterior seguirá estando disponible por si hubiera que hacer rollback del propio entorno.

Con esto conseguimos crear versiones del entorno completo de una forma controlada y segura, que puede además ser fácilmente versionables y ubicadas en el tiempo, eliminando los problemas derivados de las instancias “rotas” casi por completo.

Además de ello, gracias al nivel de capacidad de virtualización de los sistemas operativos, concepto fundamental donde se apalanca el concepto de infraestructura inmutable, estas imágenes pueden ser desplegadas de forma casi instantánea.

immutable_infrastructure-animated-gif

Fuente: O’Reilly Media


3. ¿Por qué surge el concepto de infraestructura inmutable?

Básicamente por los problemas, y por lo tanto, costes asociados a la administración y mantenimiento de la infraestructura mutable. A continuación es planteo una lista de motivos por los que el mantenimiento artesanal de la infraestructura compuesta por componentes tradicionales de larga duración (y por lo tanto mutable) es insuficiente para las tareas de operación en un entorno de servicios distribuidos en la nube:

  • Incremento de la complejidad operacional: Con la implantación de las arquitectura de servicios distribuidos y la necesidad de escalado dinámico, hacen extremadamente complejo el encargarse de todas esas máquinas. Usando los métodos tradicionales de mantenimiento para actualizaciones o parches por cientos de miles de instancias es difícil, propenso a errores y un desperdicio de tiempo.

  • Despliegues más lentos, incremento de fallos: Cuando la infraestructura está compuesta de componentes “copos de nieve” (que cada uno de ellos tiene una configuración única, artesanal) derivados de los métodos tradicionales de mantenimiento (a través de scripts o herramientas de gestión de la configuración), existen muchas más probabilidades de que algo vaya mal.

  • Identificación de errores y amenazas para mitigar el daño: Los sistemas mutables de larga duración se apoyan en la identificación de errores o amenazas para prevenir el daño. Es de sobra conocido que esta tarea es imposible de realizar, puesto que existe una gran incertidumbre de lo que le pueda a pasar al sistema en el futuro. No sabemos lo que no conocemos. No podemos tener todas las casuísticas bajo control.


4. ¿Por qué hacer que nuestras aplicaciones sigan el patrón de infraestructura inmutable?

Los beneficios de la infraestructura inmutable son numerosos, siempre y cuando se aplique de forma conveniente a tu aplicación, se disponga de un entorno completamente automatizado y existan formas de recuperación de la infraestructura. A continuación te detallo una lista de los principales beneficios:

  • Simplificación de operaciones: Si se dispone de un método de despliegue completamente automatizado, es posible renovar los componentes de la aplicación de tal forma que no sea necesario llevar un control de los cambios que han ocurrido en la instancia.

  • Despliegues continuos, disminución de fallos: Con infraestructura inmutable, es posible saber qué se está ejecutando y cómo se comporta, haciendo que los despliegues de las actualizaciones sean rutinarios y continuos, disminuyendo así los fallos en producción. Cada uno de los cambios es registrado en el repositorio de código y en los procesos de Continuous Integration / Continuous Deployment.

  • Reducción de errores y amenazas: En general, la compleja pila de hardware y software en la que se apoya los servicios que desplegamos tiende a degradarse con el tiempo. Disponer de una forma automatizada de sustitución de esa pila en vez de estar manteniéndola, hace que se reduzca drásticamente la deriva de la configuración, las fallas de seguridad y el esfuerzo necesario para cumplir con los SLAs

  • Reinicios de la Nube sin impacto: Si nuestra infraestructura es inmutable sabemos en todo momento qué estamos ejecutando, y que disponemos de métodos de recuperación completamente automatizados que permiten que los reinicios de las instancias donde se albergan las aplicaciones deberían ser totalmente transparentes para la propia aplicación.


5. ¿Cómo implementar Infraestructura Inmutable en nuestro producto?

Con los servidores inmutables, la transición de estados desaparece ya que se realiza una reconstrucción completa todo el tiempo. Tras cada uno de los commits, se crea una imagen, de despliega y se testea. Una vez que es testada, la imagen puede ser copiada N veces, y seguirá funcionando como se espera.

Para lograr esto existen dos aproximaciones:

  • Imágenes ‘Golden’ dinámicas: Cada vez que se hace un commit, se crea una instancia en la nube, se provisiona y de obtiene una imagen. Esta imagen es el artefacto que se desplegará en los diferentes entornos. En caso de que se requiera auto-escalado, es necesario actualizar la configuración para instalar la nueva imagen.

  • Contenedores: Con los contenedores, el concepto es muy similar. Sin embargo, éstos son mucho más ligeros y tardan mucho menos en arrancar. Se provisiona una imagen que es publicada en un repositorio. Desplegar el contenedor es arrancar tu aplicación.

En cualquiera de los dos casos, todos los procesos deberían estar automatizados end-to-end, y ser parte de un Pipeline de Continuous Delivery. El servidor de Continuous Integration debería detectar los cambios en el repositorio de código (aplicación y/o infraestructura) y ejecutar una serie de scripts para construir y publicar la imagen.

large-container-vessel-ship-and-horizon

Fuente: InfoWorld


6. Conclusiones

  • Una infraestructura inmutable es un patrón para administrar servicios cuya infraestructura se divide en “aplicación” y “todo lo demás”. Los componentes “todo lo demás” son reemplazados en cada despliegue.

  • Aplicar infraestructura inmutable es una forma de simplificar la administración de las actualizaciones: los servidores nunca se corrompen y permite pensar en una aplicación como un único artefacto desplegable.

  • Para adoptar un modelo de infraestructura inmutable es necesario disponer de una infraestructura basada en Cloud, orientando las aplicaciones a imágenes que contengan tanto los datos como el servidor en un solo artefacto.

¿Habéis implantado un modelo de infraestructura inmutable en vuestra organización?

Cuéntanos tus experiencias dejándome un comentario en el artículo o a través de mi cuenta de Twitter: @drodriguezhdez


7. Referencias

Cómo testear en Elasticsearch 5 sin morir en el intento

$
0
0

En este tutorial desgranamos las claves para poder construir tests de integración en Elasticsearch 5 sin perder los nervios por el camino.

Índice de contenidos


1. Introducción

Los tests de integración dentro de Elasticsearch siempre han tenido su truquillo.

Con la actualización a la versión 5 de Elasticsearch, han cambiado algunas cosillas que pueden que nos den algún que otro dolor de cabeza para poder solucionarlas.

Con este tutorial pretendo evitar que paséis por ello dándoos las claves para que no paséis ni un minuto atascados resolviendo problemas del contexto de ejecución de Elasticsearch.


2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro Retina 15′ (2.3 Ghz Intel Core I7, 16GB DDR3).
  • Sistema Operativo: Mac OS Sierra 10.12
  • Entorno de desarrollo: Eclipse Neon
  • Apache Maven 3.3.3

3. Creación del proyecto

Creamos un proyecto Maven, a través del arquetipo “quick-start-maven”, y eliminamos todo aquello que le sobra (como es el fichero App.java y AppTest.java).

En este tutorial, únicamente nos vamos a centrar en cómo preparar la clase de tests de integración utilizando Elasticsearch 5, concretamente la versión 5.0.1.

proyecto-vacio-elasticsearch5-test-int


4. Importación de las dependencias necesarias

Basándonos en la documentación oficial de Elasticsearch v5, añadimos en el fichero ‘pom.xml’ las dependencias necesarias para poder construir y ejecutar tests de integración.

Adicionalmente, añadimos también la dependencia a la librería de Hamcrest para poder añadir algo más de legibilidad a nuestros tests.

<dependencies>
			<dependency>
				<groupId>org.apache.lucene</groupId>
				<artifactId>lucene-test-framework</artifactId>
				<version>6.2.1</version>
				<scope>test</scope>
			</dependency>
			<dependency>
				<groupId>org.elasticsearch.test</groupId>
				<artifactId>framework</artifactId>
				<version>5.0.1</version>
				<scope>test</scope>
			</dependency>
			<dependency>
				<groupId>org.hamcrest</groupId>
				<artifactId>hamcrest-all</artifactId>
				<version>1.3</version>
			</dependency>
		</dependencies>

5. Construyendo el primer test

En teoría, ya podemos comenzar a construir nuestros tests de integración sin ningún problema.

Para evitar quebraderos de cabeza adicionales y como esto es un test de integración, esto es, que entran en juego varias piezas para ejecutar el tests, a mi me gusta empezar con un test vacío para que únicamente nos aseguremos de que el contexto de integración del test se levanta correctamente.

En este sentido, comenzamos construyendo una clase de test de integración vacía. En esta ocasión, yo he llamado a la clase ES5TestIT.

ES5TestIT.java
package com.autentia.tutoriales.es5;

	public class ES5TestIT {

	}

Siguiendo las indicaciones de la documentación oficial de Elasticsearch, extendemos la clase de test de ‘ESIntegTestCase’. Con esto nos aseguramos de que una vez que ejecutemos el test, se levantará todo el contexto de Elasticsearch sin necesidad de tener un servidor externo corriendo.

package com.autentia.tutoriales.es5;

	import org.elasticsearch.test.ESIntegTestCase;

	public class ES5TestIT extends ESIntegTestCase{

	}

Continuamos añadiendo nuestro primer test. En esta ocasión, vamos a construir un test vacío para asegurarnos únicamente de que todo el contexto de integración funciona correctamente.

package com.autentia.tutoriales.es5;

	import static org.hamcrest.CoreMatchers.is;

	import org.elasticsearch.test.ESIntegTestCase;
	import org.junit.Test;

	public class ES5TestIT extends ESIntegTestCase{

		@Test
		public void shouldInitializeContextsSuccessfully() {
			assertThat(true, is(true));
		}

	}

Y finalmente, ejecutamos el test de integración. Este test, según la documentación oficial, debería levantar un Elasticsearch embebido y finalizarlo correctamente.

La realidad es, que antes de que esto llegue a pasar, hay que resolver algunos problemas.


6. Resolviendo la falta de dependencias

Si habéis seguido el tutorial y habéis llegado a este punto, el error que debería haberos salido es el siguiente:

error-dependencias-test-integration-elasticsearch5

Es decir, que para poder ejecutar el test de integración es necesario incluir un par de dependencias más relacionadas con el sistema de Logging.

Para resolver este error, añadimos las dependencias de log4j al fichero ‘pom.xml’, tanto el API como el Core.

<dependencies>
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-api</artifactId>
			<version>2.6.2</version>
		</dependency>

		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-core</artifactId>
			<version>2.6.2</version>
		</dependency>

		<dependency>
			<groupId>org.apache.lucene</groupId>
			<artifactId>lucene-test-framework</artifactId>
			<version>6.2.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.elasticsearch.test</groupId>
			<artifactId>framework</artifactId>
			<version>5.0.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.hamcrest</groupId>
			<artifactId>hamcrest-all</artifactId>
			<version>1.3</version>
		</dependency>
	</dependencies>

Y volvemos a ejecutar el test de integración para ver si se han resuelto los errores de falta de dependencias, y poder levantar el contexto de forma correcta.


7. Con el JarHell hemos topado

Si habéis seguido los pasos del tutorial hasta aquí, y habéis ejecutado el test, es muy probable que os haya salido este mensaje de error.

error-jarhell-test-integracion-elasticsearch5

Y aquí está el famoso error de JarHell… infierno de librerías!! Este dichoso error sí que es un infierno.

La clase JarHell (vaya nombrecito) es una clase que forma parte del framework de testing de Elasticsearch, y lo que hace es recorrer cada una de las dependencias verificando que las clases que se disponen en las dependencias están una, y solo una vez.

Es alucinante descubrir cuantísimas librerías tienen exactamente copy&paste de clases de otras librerías: contenido idéntico, mismo qualified name (es decir, mismo paquete!), etc.

La investigación de este error me ha llevado por la consulta de decenas de webs, decompilación de librerías para comprobar con mis propios ojos lo que este error estaba indicando, probando cosas tan absurdas como empezar a meter exclusions en el pom.xml para ver si se resolvía (si claro que se resolvía, pero dejaba de funcionar otras cosas por que faltaban otras clases!!! Lógico).

Como sería la cosa, que en la versión 2.4 de Elasticsearch, disponían de un método de desactivación del JarHell incluyendo una propiedad a nivel de máquina virtual en la ejecución de los tests. Pero amigos, en la versión 5 de Elasticsearch, (al menos en la versión en la que se está redactando este tutorial que es la 5.0.1), ese método de desactivación ha desaparecido del código fuente!! Ya no podemos desactivarlo a nivel de propiedad de VM en la ejecución del test.

Como decía un famoso anuncio de TV: Una solución quiero!


8. Resolviendo el infierno del JarHell

Tras la revisión de unos cuantos hilos de discusión en la comunidad de Elasticsearch, uno de los miembros planteaba la solución de engañar al ClassLoader haciendo que cargase la clase de JarHell que nosotros queremos, en vez de cargar la del propio framework de testing de Elasticsearch.

Esta es la solución que vamos a implementar: Vamos a desarmar el infierno del JarHell sustituyéndola por una clase totalmente dummy. Puede que quizás esta solución nos esté ensuciando el código fuente, pero teniendo en cuenta cuales son las alternativas y que esto solo afecta las clases de testing, podemos asumir este cambio. Al menos hasta que se proporcione otras alternativas (en versiones posteriores?)

Para resolverlo, se crea la clase org.elasticsearch.bootstrap.JarHell dentro de src/test/java con el siguiente contenido:

package org.elasticsearch.bootstrap;

		import java.io.IOException;
		import java.net.URISyntaxException;
		import java.net.URL;

		//IMPORTANT: Needed since the -Dtests.jarhell.check has been disabled in ES 5.0
		//DO NOT REMOVE
		public class JarHell {

			public static void checkJarHell() throws IOException, URISyntaxException {

				//IMPORTANT: Needed since the -Dtests.jarhell.check has been disabled in ES 5.0
				//DO NOT REMOVE
			}

			public static URL[] parseClassPath()  {
				return new URL[0];
			}
		}

Puede que cuando vayamos profundizando en la generación de nuestros tests de integración, haya que ir añadiendo nuevos métodos dummy que hagan el Override de los métodos de la clase JarHell. Para el ejemplo que vamos a mostrar, es más que suficiente añadir los métodos que hemos expuesto anteriormente.

project-explorer-jarhell

Finalmente, volvemos a ejecutar el test de integración para comprobar si el contexto levanta correctamente finalmente.


9. Queda detenido por el SecurityManager

Si habéis seguido todos los pasos del tutorial hasta aquí, tras la ejecución del test de integración habrá aparecido el siguiente error:

security-manager-error-elasticsearch5-testit

Pues parece ser que esta vez, el SecurityManager nos ha detenido ya que el test de integración no tiene política de seguridad declarada ni aceptada para poder ejecutar el test completamente.

Parece ser que la ejecución de este test de integración requiere unos permisos de seguridad especiales que se ven reflejadas a través de políticas seguridad. Pero ni con esas.

Tras mucho buscar, y mucho probar a meter ficheros “.policy” dentro de todos los directorios habidos y por haber, indicándole políticas garantizando la propiedad que se indica en los errores, y un sinfín de pruebas, el test de integración no es capaz de encontrar esas políticas de seguridad.

¿Cómo lo hemos resuelto os preguntaréis? Seguid leyendo.


10. Resolviendo problemas con el SecurityManager

En realidad no es una resolución del problema, sino más bien, una desactivación del problema. La solución pasa por indicar que no queremos que el SecurityManager realice un chequeo de los privilegios de aquellas operaciones que lo requieran.

Para ello, es necesario añadir un argumento a las opciones de la VM cada vez que se vaya a ejecutar un nuevo test: -Dtests.security.manager=false

solve-security-manager-es5-testit

Este es el aspecto del Runner de Eclipse al haberle añadido la nueva propiedad.

Finalmente, ejecutamos el test de integración para ver si con esto, ya hemos resuelto todos los problemas.


11. Contexto levantado! Lo conseguimos

Tras la realización de estos pasos, y tras haber ejecutado el test de integración parece ser que por fin se ha levantado el contexto de forma correcta, y no se ha quejado de ningún otro problema.

es5-testit-success

A partir de aquí, podemos empezar a montar la lógica del test propiamente dicha, pero eso ya será objeto de un nuevo tutorial. Podemos utilizar esta base para poder disponer nuestros tests de integración en Elasticsearch v5.0.1


12. Conclusiones

A continuación os dejo las conclusiones de la construcción de test de integración en Elasticsearch 5.0.1:

  • Es necesario añadir las dependencias de log4j para la ejecución de los tests.
  • Engañamos al entorno de ejecución cargando una clase JarHell que no es la de Elasticsearch para poder continuar con la ejecución del test
  • Desactivamos la inspección de privilegios del SecurityManager para poder continuar con la ejecución de los tests.

13. Contacto

¿Habéis tenido algún que otro problema con los tests de integración de Elasticsearch v5? Déjame un comentario en este artículo o coméntamelo a través de mi cuenta de Twitter: @drodriguezhdez.

Echa un vistazo al código del tutorial en mi repositorio de GitHub: Source code del Tutorial.


14. Referencias

Comunicación entre Microservicios: ¿Compartimos código?

$
0
0

Índice de contenidos


1. Introducción

Una de las tendencias más de moda en las arquitecturas backend a día de hoy son los microservicios. Básicamente donde antes teníamos una (o pocas) grandes aplicaciones que hacían de todo, llamadas monolitos, ahora tenemos varias (o muchas) pequeñas aplicaciones especializadas en un área específica de dominio, normalmente especificado según DDD (Domain Drive Design).

Como consecuencia de esta partición en pequeños microservicios, estamos sustituyendo las llamadas clásicas entre clases de Java en la misma JVM (si estás trabajando con Java, ¡que no es obligatorio!) por llamadas entre microservicios a través de sus APIs públicas. Por ejemplo:

  • Microservicio de compras recibe una petición de compra del producto 123 por parte del usuario 987.
  • El microservicio de compras llama a:
    • microservicio de catálogo para saber los detalles del producto 123
    • microservicio de usuarios para conocer los detalles del usuario 987.
    • microservicio de medios de pago disponible y de descuentos con el importe.

En un monolito se hacen llamadas a los métodos correspondientes de clases que actúan como servicios. Son llamadas a nivel de código Java (o el lenguaje que sea), y no suele haber problemas por compartir o no las clases de modelo, ya que conforman un todo.


2. Llamadas entre servicios: el caso de los microservicios

Un microservicio sólo se puede comunicar con el exterior a través de su interfaz API pública. Si usamos cualquier otro medio no estamos independizando bien el diseño y no estamos haciendo microservicios correctamente.

Por centrar la explicación con el modelo típico y no atender a exotismos: la interfaz API pública típica es una interfaz REST con JSON. La situación habitual es que un microservicio reciba una petición REST a una URL que está publicando, lea el JSON de entrada (si es un POST, por ejemplo) y lo convierta a una estructura entendible por el lenguaje en el que está programando.

Si usamos Java, lo habitual será usar Jackson o Gson para parsear el texto en JSON y convertirlo en una serie de DTOs que contienen esa información, que posteriormente son consumidos por las capas inferiores. El mecanismo de emisión de la respuesta es el contrario: el core de la aplicación genera unos DTOs y con un parser son convertidos a una cadena JSON entendible por el cliente.

Cuando un microservicio de compras hace una petición al microservicio de catálogo para preguntar por un producto, lo hace a través de una interfaz REST, intercambiándose un JSON de este tipo:

{
  "id":"123",
  "marca":"Phillyps",
  "modelo":"Jander",
  "precio":"213€"
}

Para producir este JSON, debería tener un DTO para que el parser actuase. Este DTOs sería algo así:

public class Producto{

    private int id;
    private String marca;
    private String modelo;
    private float precio;

    public Producto(int id, String marga, String modelo, float precio){
        //Típico constructor
    }

    //Típicos getters
}

Tenemos claro, por diseño DDD,que esta información y por tanto este DTO debe residir en el microservicio de catálogo. Hasta aquí todo normal… Veamos ahora el "problema":

El microservicio que lee del microservicio de catálogo es el de compras. Y como mensaje recibe el JSON antes citado, y por tanto necesita de un DTO para interpretar estos datos en su lado. Recuerda que son dos microservicios independientes uno del otro, se decir, un .war cada uno, y por tanto de primeras no podemos hacer un "import" del DTOs del servicio que lo emite… ¿Qué hacemos con el DTO?¿Lo escribimos de nuevo en el cliente?¿Lo compartimos?¿Lo generamos automáticamente?

La cuestión no es baladí, y es posible que las primeras veces que trabajes con microservicios la intentes resolver de un modo tradicional, pero luego, a medio plazo puede que te hagas preguntas que te hagan plantearte el diseño y maldecir el momento en el que tomaste la decisión sin reflexionar mucho acerca del futuro.


3. ¿Qué hacemos?

Después de leer unos cuantos artículos y ver puntos de vista (algunos en las referencias), aún no tengo claro qué alternativa debería ser la protagonista: cada una tiene sus ventajas y desventajas, aunque tampoco querría poner todas al mismo nivel, porque sí que me parece que unas son mejores que otras.

Con la intención de intentar ver qué opinaba la comunidad, lancé una encuesta en Twitter con 4 alternativas, y que contestaron 38 personas, a buen seguro la mayoría con mayor capacidad que la mía:

encuesta

(Des)Afortunadamente, el resultado tampoco pareció concluyente bajo mi punto de vista, así que voy a intentar exponer las alternativas que he encontrado, con lo que creo que son sus puntos positivos y negativos para que puedas tomar una opinión:

Alternativa 1: No compartir nada.

Es la opción más purista y desde el punto de vista de los microservicios la que creo más correcta. Consiste en que cada microservicio se considera aislado de los demás (como debe ser) y no comparte los DTOs de la interfaz del API.

La consecuencia es que cada microservicio que acceda al microservicio de catálogo para obtener los datos del producto deberá implementar el DTO para así poder mapear el JSON a algo legible por las capas inferiores.

Pros:

  • Independencia total entre microservicios. La situación ideal y que menos nos va a condicionar en el futuro.
  • Es lo que haremos si no compartimos lenguajes entre microservicio productor y microservicio consumidor.
  • Cada cliente puede consumir sólo la información que necesita, no toda.

Contras:

  • Deberemos repetir la implementación en cada microservicio. Si comparten lenguaje, deberemos escribir (copiar el fichero está permitido ^_^) de nuevo el mismo DTOs n veces para los n clientes de ese microservicio.
  • Un cambio en el DTO del productor obliga a editar n DTOs de los clientes (aunque debería ser una versión nueva del API). Pero quizá en realidad no todos porque alguno puede que no se vea afectado por el cambio al consumir estrictamente lo necesario (esto iría a los pros).
  • No existe comprobación por parte del compilador ante un cambio del productor (nueva versión de API).

Dentro de esta alternativa, como opción exótica y un tanto descabellada, se podría incluir el proceso de copia desde los DTOs del servidor a los clientes a través de algún tipo de automatización en el sistema de integración contínua. Ya puestos a no escribir y copiar, que lo haga una máquina. Obviamente, esto no permitiría, al menos de forma sencilla, elegir qué atributos de los DTOs nos quedaríamos en cada servicio.

Alternativa 2: Que el servicio comparta en forma de paquete independiente su capa de DTOs de interfaz

Esta alternativa consiste en que el servidor separa en un subproyecto específico los paquetes de DTOs que forman la interfaz REST. Al final este subproyecto genera un archivo empaquetado que puede ser además utilizado por los clientes, por lo que se puede distribuir y establecer una dependencia.

Pros:

  • Sólo se desarrolla una única vez los DTOs del servidor.
  • No hay duplicidad de código: más fácil de mantener, especialmente los cambios.
  • Nos aseguramos la perfecta coordinación con los clientes de los mensajes (versionado?).
  • Podemos beneficiarnos de ciertos checks en tiempo de compilación al estar ligados servidores y clientes.

Contras:

  • Existe un acoplamiento con el microservicio servidor más claro, ya que existe una dependencia específica sobre él (sobre el paquete de DTOs). Nos puede perjudicar en el futuro.
  • Usamos un mismo paquete para dos propósitos: el del servidor y el del cliente, esto nos puede llevar a problemas.
  • No se puede reutilizar si se emplean otros lenguajes en los clientes que no soportan el empaquetado que ha generado el servidor.
  • Los clientes pueden estar accediendo a información que no les interesa, como atributos que forman parte del mensaje recibido pero que no utilizan.

Alternativa 3: El servidor publica un SDK/API para los cliente.

De este modo los microservicios de consumo incorporan esta API publicada por el servidor para acceder a sus servicios. Es parecida a la alternativa anterior, pero con la diferencia de que el servidor no utiliza en su implementación esta API, que además puede incluir más cosas además de los simples DTOs. Ya había pensado en una solución similar, pero que José Moreno me lo sugiriese como alternativa le da más validez a la alternativa 🙂

Pros:

  • No duplicamos apenas código.
  • Cierta independencia entre clientes y servidores al no acoplarse con un componente específico del servidor, sino a una especia de "Driver".
  • Mantenemos los checks de compilació si hay algún cambio en la especificación del cliente.
  • Podemos incluir ciertos aspectos específicos en el API cliente, como por ejemplo conectores o transformaciones específicas del mensaje si fuera necesario.

Contras:

  • Debemos mantener una versión diferente por cada lenguaje que empleen los microservicios clientes. No es tan común cambiar de lenguaje de microservicios dentro de una misma arquitectura.
  • Posible mayor desarrollo al poder incluir más que los DTOs y tener que pensar que es un SDK.
  • Sigue sin ser una solución completamente desacoplada.

Alternativa 4: Usar algún tipo de IDL (Interfaz de definición de language)

Este concepto se daba de forma parecida en la época (¿pasada?) de las arquitecturas SOA y el SOAP, bueno, y de XML en general. Gracias a XML, Schemas y WSDL (WADL), son los propios servicios los que hacen una publicación del formato de mensajes que emitían los servicios, e incluso qué servicios estaban soportados. Gracias a ello, mediante herramientas automáticas de generación de código, como por ejemplo JAXB en Java, era posible generar los DTOs que podían manejar ese mensaje.

Cuando pensamos en términos de microservicios, solemos pensar en REST con JSON, aunque cada uno es libre de emplear el protocolo/tipo de mensaje que quiera (XML. Frente a los mensajes SOAP, REST con JSON es menos formal y más directo, por lo que no suele ser una prioridad contar con un esquema sobre el que validar la información transmitida.

No obstante esto no quiere decir que no existan iniciativas para poder describir un esquema de JSON sobre el que se pueda, además de validar y poder documentar el API, crear clientes de forma más o menos automática. Algunas de las opciones más importantes:

  • JSON Schema para establecer un esquema de mensajes JSON que se puede utilizar para crear clases en el cliente usando por ejemplo: http://www.jsonschema2DTOs.org/ . Está respaldado por la IETF.
  • Open API (antes Swagger) para la documentación de las API, en cuyos mensajes, además de metadatos, paths, o descripciones, se usa JSON Schema para especificar los mensajes JSON esperados. Es posible generar código a partir de la especificación usando herramientas como https://github.com/swagger-api/swagger-codegen
  • RAML para la deescripcion de API que también tiene la capacidad de soportar los JSON Schema para la definición de mensajes, además de una nueva especificación en formato YAML. Podemos usar múltiples herramientas para generar código a partir de RAML, como https://github.com/mulesoft/raml-for-jax-rs
  • API BluePrint: similar a las anteriores, también dispone de un ecosistema de herramientas como https://github.com/pksunkara/alpaca

Y en general cualquier otro sistema que aparezca para la documentación de API en el que se pueda llegar a generar código a partir de una especifcación.

Pros:

  • Si tenemos (o estamos obligados) que documentar un API con alguna de estas herramientas, tenemos gran parte del trabajo hecho.
  • De paso tendremos acceso a múltiples funcionalidades de estas herramientas, como la creacion de API mocks, facilidades para documentación, suites de test, validadores…
  • Es independiente del lenguage del cliente: no es necesario "picar" unas clases por cada tipo de lenguage de cliente usado.

Contras:

  • ¿A quién le gusta documentar API?
  • ¿A quién le gusta mantener actualizada la documentación de las API?

4. ¿Qué alternativa elegir?

Si soy sincero no tengo claro cuál elegir, por eso he expuesto lo que creo que son los pros y los contras de cada una de las soluciones. Ya es cuestión tuya evaluar cuál es la que mejor se adapta a las circunstancias de tu proyecto de microservicios.

No obstante voy a mojarme y voy a dar mi punto de vista, seguramente equivocado sobre mis preferencias. Yo me quedaría con alguna de estas dos opciones:

  • Alternativa 1: no compartir nada, es la que más beneficios nos va a aportar a medio y largo plazo, ya que es la que fuerza a una menor dependencia entre clientes y servidores. Pero en el día a día resulta bastante costosa.
  • Alternativa 3: El servidor publica un API cliente, parece tener un cierto compromiso entre independencia y costes. Es reutilizable entre clientes y no establecer una dependencia explícita con el servidor, al no ser utilizado por este (al contrario que la Alternativa 2 de contar con un paquete común entre servidor y cliente).

Pero siempre tienes que tener en la cabeza las características del proyecto y sus posibilidades de evolución (que si usas microservicios será porque esperas que evolucione mucho ¿no?). La experiencia me indica que este tipo de decisiones se toman pensando en el próximo commit, pero luego se pagan dentro de 3 o 4 sprints.


5. Conclusión

Los microservicios presentan nuevos retos que no se daban en las arquitecturas monolíticas. Ahora las llamadas entre diferentes ámbitos de la aplicación transcienden más allá de la máquina virtual en la que se ejecuta el cliente, produciéndose llamadas entre API de diferentes microservicios. Esto obliga a que seamos cuidadosos a la hora de compartir código entre diferentes servicios que comparten algunos elementos del modelo de datos.

En este post hemos expuesto una serie de alternativas al problema de compartir modelo de datos de las peticiones entre microservicios productores y consumidores, exponiendo sus puntos fuertes y débiles para que el lector pueda tomar una decisión por sus propios medios, ya que parece que no existe un consenso establecido sobre cómo hacerlo.


6. Referencias:

Algunos artículos que considero interesantes sobre esta temática

Cómo hacer una inyección de dependencias opcional gracias a Spring y la clase Optional

$
0
0

Cómo hacer una inyección de dependencias opcional gracias a Spring y la clase Optional

spring-inject


1. Introducción

Con Spring Framework es muy sencillo hacer un inyección de dependencias opcional gracias la anotación @Autowired(required=false). De esta forma conseguimos que si no está definido el bean al que se está haciendo referencia, su valor quedará a null y podremos obrar en consecuencia para, por ejemplo, proporcionar un valor por defecto.

import org.springframework.stereotypeorg.Service;
import org.springframework.beans.factory.annotation.Autowired;

@Service
public class Foo {

    @Autowired(required=false)
    private Bar bar;

    @PostConstruct
    void afterPropertiesSet() {
        if (bar == null) {
            bar = new DefaultBar();
        }
    }

    ...
}

Pero @Autowired es una anotación específica de Spring y no pertenece al estándar JSR 330, así que ¿cómo podemos hacerlo usando la anotación estandar @Inject?

En este tutprial vamos a dar respuesta a esta pregunta.



2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro 15” (2.5 GHz Intel i7, 16GB 1600 Mhz DDR3, 500GB Flash Storage).

  • AMD Radeon R9 M370X

  • Sistema Operativo: Mac OS X El Capitan 10.11.6

  • Java v1.8.0_112

  • Maven v3.3.9

  • Spring 1.3.7.RELEASE



3. Usando Spring + @Inject + Optional

Gracias a Spring podemos simular un inyección opcional, comportamiento que no está definido en el estándar:

import org.springframework.stereotypeorg.Service;
import javax.inject.Inject;
import java.util.Optional;

@Service
public class Foo {

    @Inject
    private Optional<Bar> optionalBar;

    private Bar bar;

    @PostConstruct
    void afterPropertiesSet() {
        bar = optionalBar.orElse(new DefaultBar());
    }

    ...
}

Podemos ver como en la línea 8 estamos usando la anotación estándar @Inject sobre un tipo Optional. Es aquí donde Spring se encarga de hacer la “magia” y dar valor a este atributo en función de si existe o no un bean de tipo Bar.

Luego en el @PostConstruct, en la línea 15 es donde, igual que en el ejemplo anterior, comprobamos la existencia del bean y en caso de no estar presente inicializamos el atributo con el valor por defecto.



4. Conclusiones

Ojo, y no penséis que os estoy recomendando esta construcción, de hecho más bien lo contrario. Tener un Bean opcional es raro, y usar la clase Optional si no es como valor de retorno de un método es más que cuestionable. Así que cuidado con hacer este tipo de cosas porque si no está muy justificado puede ser un mal olor.

Esto lo descubrí el otro día por casualidad y simplemente quería mostrároslo en este tutorial porque me ha parecido curioso.



6. Referencias

Imágen inicial extraída del artículo: http://www.arquitecturajava.com/spring-inject-cdi-y-standards/



5. Sobre el autor

Alejandro Pérez García (@alejandropgarci)
Ingeniero en Informática (especialidad de Ingeniería del Software) y Certified ScrumMaster

Socio fundador de Autentia Real Business Solutions S.L. – “Soporte a Desarrollo”

Socio fundador de ThE Audience Megaphone System, S.L. – TEAMS – “Todo el potencial de tus grupos de influencia a tu alcance”

Como evitar parones con Java 8 y OS X El Capitan o macOS Sierra

$
0
0

Como evitar parones con Java 8 y OS X El Capitan o macOS Sierra



1. Introducción

Con OS X El Capitan y macOS Sierra y las últimas versiones de la Máquina Virtual Java (JVM) puede ser que en tu ordenador experimentes ciertos “parones”, como si la JVM se quedara congelada durante dos o tres segundos. No pasa siempre, pero es bastante molesto. Nosotros en concreto lo hemos detectado:

  • Cuando depuras (si en vez de depurar haces una ejecución normal no ocurre) un test o aplicación desde el IntelliJ. No es culpa del IntelliJ, pero lo hemos detectado en esta situación porque justo al darle a “depurar” se ve como todo el entorno se queda como congelado durante dos segundos hasta que de verdad empieza la depuración del test o aplicación.

  • Cuando se arranca una aplicación (en nuestro caso concreto una aplicación con SpringBoot). De nuevo no tiene nada que ver ni con IntelliJ ni con SpringBoot.

En estas dos situaciones que he comentado se hace muy notable este problema ya que son procesos muy rápidos y de repente vemos como se “eternizan” al quedarse congelado todo el entorno.

En general pasa con más procesos, por ejemplo una compilación de un proyecto completo con Maven, pero al ser un proceso más largo, esta pequeña parada puede pasar desapercibida.

En este tutorial vamos a ver un workaround para solucionarlo.



2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro 15” (2.9 GHz Intel i7, 16GB 2133 MHz LPDDR3, 500GB Flash Storage).

  • AMD Radeon Pro 460 4096 MB

  • Sistema Operativo: macOS Sierra 10.12.2

  • Java 1.8.0_112

  • Maven 3.3.9

  • IntelliJ IDEA 2016.3.2



3. El problema es del DNS

Pues sí, aunque parezca raro, el problema parece que es del DNS, que se queda bloqueado cuando intenta resolver el dominio localhost

Esto sólo pasa con las últimas versiones de OS X El Capitan y posteriores (macOS Sierra) en conjunción con las últimas versiones de la JVM, aunque no se precisar versiones concretas.

Por ahora el workaround es modificar el /etc/hosts (recomiendo Gas Mask) y aseguraos de que tenéis la siguiente línea:

127.0.0.1       localhost     <hostname>.local

Ojo porque es importante tenerlo en la misma línea, si lo separáis en dos líneas no se soluciona el problema!

Donde <hostname> es el nombre de vuestro equipo. Lo podéis ver si abrís un Terminal y ejecutáis el comando:

$ hostname

Más información en: https://youtrack.jetbrains.com/issue/IDEA-157303

Muy recomendable hacerlo!!!



4. Conclusiones

Cuando de repente contempleis un comportamiento anómalo o diferente siempre hay una explicación, ya que, aunque a veces no lo parezca, los ordenadores son sistemas deterministas (el problema es que a veces intervienen tantas variables que resulta casi imposible saber lo que está pasando). Por eso lo mejor es Googlear un poco ya que somos muchos y casi nunca seremos los primeros en haberlo descubierto.

Por eso siempre, tranquilidad y cabeza para tirar de logs, tener claro el caso que reproduce el problema, descartar alternativas, buscar por Internet casos similares, y finalmente arreglar y documentar 😉



5. Sobre el autor

Alejandro Pérez García (@alejandropgarci)
Ingeniero en Informática (especialidad de Ingeniería del Software) y Certified ScrumMaster

Socio fundador de Autentia Real Business Solutions S.L. – “Soporte a Desarrollo”

Socio fundador de ThE Audience Megaphone System, S.L. – TEAMS – “Todo el potencial de tus grupos de influencia a tu alcance”


Problemas con macOS Sierra y el SSH para acceder a repositorios Git

$
0
0

Problemas con macOS Sierra y el SSH para acceder a repositorios Git



1. Introducción

Es posible que desde la llegada de macOS Sierra hayáis notado ciertas molestias a la hora de trabajar con vuestros repositorios remotos de Git. Esto lo habéis podido notar sobre todo si:

  • Usáis la línea de comandos

  • Tenéis vuestro fichero de identidad (supongamos que vuestro SSH key es ~/.ssh/id_rsa) con clave de acceso (passphrase)

Los síntomas serán que, a partir de macOS Sierra, seguramente cada vez que vais a hacer una operación contra el servidor, como push, pull, o fetch, os preguntará cual es vuestra clave para acceder al SSH key. Esto antes no ocurría porque la clave se guardaba en el llavero (Keychain), pero este comportamiento por defecto se ha quitado en macOS Sierra.

Aunque he hablado de la línea de comandos, seguramente también tengáis problemas con otras aplicaciones que igualmente usan vuestra SSH key, como por ejemplo el Sourcetree.



2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro 15” (2.9 GHz Intel i7, 16GB 2133 MHz LPDDR3, 500GB Flash Storage).

  • AMD Radeon Pro 460 4096 MB

  • Sistema Operativo: macOS Sierra 10.12.2

  • Git 2.10.1



3. Solucionando el problema con ssh-agent

Una solución rápida sería quitarle la clave (passphrase) a nuestro SSH key, pero teniendo en cuenta que nuestra SSH key define nuestra identidad, esta opción no sería nada recomendable ya que estaríamos facilitando mucho el robo de esta identidad.

Para poder mantener el acceso al SSH key con clave y no tener que estar poniendo la clave cada vez que queremos usar nuestra identidad, vamos a usar el programa ssh-agent. Esta es una pequeña aplicación que guarda las claves en memoria mientras dura la sesión, de forma que cuando queremos acceder a nuestra identidad será ssh-agent quién proporcione la clave en vez de tener que hacerlo nosotros.

Además vamos a combinar el uso de ssh-agent con nuestro llavero de claves (Keychain) de forma que la clave se guarde en este y tampoco tengamos que meter la clave cada vez que empezamos una nueva sesión al reiniciar la máquina. Esta en concreto es la funcionalidad que se ha desactivado con macOS Sierra.

Para ello vamos a crear el fichero ~/.ssh/config con las siguientes líneas:

Host *
  UseKeychain yes
  AddKeysToAgent yes
  IdentityFile ~/.ssh/id_rsa

Ahora (sólo hay que hacerlo la primera vez) vamos a añadir nuestra SSH key al ssh-agent, de manera que introduzcamos la clave y se guarde en el llavero (Keychain). Para ello ejecutamos en un terminal:

$ ssh-add -K ~/.ssh/id_rsa

Importante el -K que es lo que hace que se añada al Keychain.

Si queremos comprobar que se ha añadido correctamente, podemos ejecutar el siguiente comando para ver un listado de las SSH keys que están siendo gestionadas por el ssh-agent:

$ ssh-add -l

Desde este momento deberíamos de poder acceder al SSH key sin necesidad de introducir la clave manualmente. Así que tanto Git como Sourcetree deberían de volver a funcionar con normalidad.





5. Conclusiones

Muchas veces ejecutamos aplicaciones de escritorio, IDEs, …​ que nos facilitan la vida en nuestro trabajo diario, pero se nos olvidan los principios de funcionamiento de las cosas que usamos. Por eso de vez en cuando está bien remangarse un poco y ver cómo funcionan las cosas a nivel de Sistema Operativo.

Ya veis que, después de todo, tampoco es tan difícil, y vemos cómo con unos sencillos pasos podemos combinar comodidad y potencia.



6. Sobre el autor

Alejandro Pérez García (@alejandropgarci)
Ingeniero en Informática (especialidad de Ingeniería del Software) y Certified ScrumMaster

Socio fundador de Autentia Real Business Solutions S.L. – “Soporte a Desarrollo”

Socio fundador de ThE Audience Megaphone System, S.L. – TEAMS – “Todo el potencial de tus grupos de influencia a tu alcance”

Spark Framework

$
0
0

Spark Framework

spark-logo

Índice de contenidos.


1. Introducción

En el tutorial de hoy, os quiero hablar sobre un framework Java que acabo de conocer. Dicho Framework es Spark y no, no es el Spark relacionado con Big Data. Aun así, si no esperabas esto, te invito a que te quedes a leer éste y conozcas este nuevo framework que aseguro te sorprenderá por su sencillez, su ligereza y su potencia.

En el siguiente enlace podéis descargar el código fuente con el ejemplo usado en este tutorial. Para descargar pulsa aquí.


2. Spark

Como he comentado en el punto anterior, Spark es un framework Java que te permitirá desarrollar aplicaciones web. Se caracteriza por ser super sencillo y muy ligero, lo que permitirá desarrollar aplicaciones web de una manera muy rápida.

Está construido basado en el desarrollo Lambdas de Java 8. Lo que hará que el código de nuestras aplicaciones sea mucho menor y más sencillo.

Si no tienes conocimiento sobre Lambdas te recomiendo que primero eches un ojo al siguiente tutorial: Expresiones Lambda con Java 8

El objetivo principal que persigue este framework es eliminar la gran cantidad de ficheros de configuración, que acaban generándose en cualquier aplicación, centrandose en el desarrollo rápido de la aplicación.

Otra diferencia es que el paradigma de programación cambia totalmente. Empezarán a desaparecer esa gran cantidad de anotaciones que cada vez empiezan a sobrecargar aun más el código. Por lo que no te preocupes si al principio echas de menos el uso de anotaciones. Te aseguro que con Spark es posible programar sin anotaciones.

Decir que ya que están tan de moda los microservicios, Spark será una gran opción para desarrollar tus microservicios debido a su sencillez y ligereza. Es más, te aseguro que podrás montar tu API REST en tan solo 5 minutos o menos…


2. Usando Spark por primera vez.

Para usar Spark tan solo tendrás que añadir la dependencia en el pom.xml de tu proyecto maven:

<dependency>
    <groupId>com.sparkjava</groupId>
    <artifactId>spark-core</artifactId>
    <version>2.5.4</version>
</dependency>

Y una vez añadida la dependencia…úsalo! En el siguiente ejemplo vamos a crear el típico recurso de API “Hola mundo”:

package com.autentia.spark.rest.spark.rest;

import static spark.Spark.get;

public class HelloWorldResource {
	public static void main(String[] args) {
		get("/helloworld", (req, res) -> "Hello World");
	}
}

Y ya está! acabamos de crear nuestro primer recurso de API con Spark. Arranca el main() de tu aplicacióin, entra en la siguiente URL http://localhost:4567/helloworld y comprueba el resultado:

spark-helloworld

Si te das cuenta no hemos usado ningún tipo de anotación y hemos sido capaces de crear nuestro primer API REST. Olvídate de cualquier tipo de anotación y céntrate en lo que te interesa, en desarrollar tu aplicación.

Lo que Spark hace internamente para servir nuestro API es levantar un server Jetty que está embebido dentro de nuestra aplicación.

El puerto por defecto en el que Jetty se levanta es el 4567, pero, perfectamente podemos cambiarlo haciendo uso de ‘port()’:

package com.autentia.spark.rest.spark.rest;

import static spark.Spark.get;
import static spark.Spark.port;

public class HelloWorldResource {
	public static void main(String[] args) {
		port(8080);
		get("/helloworld", (req, res) -> "Hello World");
	}
}

Si nos fijamos, el método ‘get()’ está recibiendo todas las llamadas de tipo get al recurso ‘/helloworld’ y el procesado se lleva a cabo a través de una función lambda.

Dicha función recibe por parámetro dos objetos:

  • req: que contendrá información de la petición
  • res: que contendrá información sobre la respuesta del server

Pues bien, haciendo uso de dichos objetos podemos enriquecer nuestro recurso aun más.

Vamos a hacer que reciba por la URL un parámetro con el nombre de la persona a quien saludar:

package com.autentia.spark.rest.spark.rest;

import static spark.Spark.get;
import static spark.Spark.port;

public class HelloWorldResource {
	public static void main(String[] args) {
		port(8080);
		get("/helloworld", (req, res) -> "Hello " + req.queryParams("name"));
	}
}

Volvemos a entrar en nuestra aplicación y comprobamos el resultado:

spark-hello-ismael

3. Devolviendo JSON en mi aplicación.

Hoy en día hay multitud de aplicaciones que están compuestas por una parte FRONT, desarrollada con algún framework javascript, que lo que hace es atacar a un endpoint que contendrá todos los servicios de dicha aplicación.

Uno de los estándares de intercambio de datos más usados entre dichos sistemas es JSON.

Pues bien, Spark nos permite perfectamente indicar que tipo de dato devolver. En este caso basta con indicar el tipo de content-type que vamos (en este caso ‘application/json’), a devolver y construir el json de vuelta.

Aprovechando el ejemplo, también devolveremos el código HTTP 200 de respuesta indicando que todo ha ido bien:

package com.autentia.spark.rest.spark.rest;

import static spark.Spark.get;
import static spark.Spark.port;

public class HelloWorldResource {
	public static void main(String[] args) {
		port(8080);
		get("/helloworld", (req, res) ->
		{
			res.status(200);
			res.type("application/json");
			return "{message: 'Hello " + req.queryParams("name") + "'}";
		});

	}
}

Volvemos a entrar en nuestra aplicación y comprobamos el resultado:

spark-helloworld-json

5. Conclusiones

Como hemos podido ver, montar un API REST con Spark es muy sencillo y arrancar nuestro primer ejemplo será cuestión de 5 minutos.

En la actualidad están muy extendidos los desarrollos de microservicios. Pues bien, Spark, debido a su ligereza y sencillez es una muy buena opción como para tenerlo en cuenta en nuestros desarrollos.

Que duda cabe, que en este tutorial se ha hecho una muy pequeña introducción al Framework. En tutoriales futuros ampliaré más los ejemplos integrando éste potente framework con librerías como Jackson para manejar de una forma más sencilla los objetos JSON, construiremos un API más rico y hablaremos sobre como desarrollar tests unitarios del API.

Comentando Design thinking Lidera el presente. Crea el futuro.

$
0
0

Acabo de terminar de leer el libro de Manuel Serrano Ortega y Pilar Blázquez Ceballos: Design thinking Lidera el presente. Crea el futuro.

Me pareció una buena obra introductoria de Design thinking y me quedé con ganas de más herramientas que presentan al lector (o adaptaciones personales).

Poniendo en contexto mi situación, estoy ayudando a organizaciones que definen proyectos con design thinking (que se lo han currado) a traducir los nuevos escenarios de oportunidad a historias de usuario para abordarlos con metodologías ágiles: al final me he “contaminado” de muchas otras herramientas en la fase de definición de esos proyectos y he decidido profundizar.

Os cuento las partes que me han gustado, que corresponden con las esquinas que he doblado y lo que me han hecho pensar (siempre recordando que seguro que otro lector encontrará otras muchas cosas de valor).

– Citan a David Burney: la gente que exige saber la respuesta demasiado pronto, mata la innovación”. En el proceso de Design Thinking, cuando participamos técnicos o gente muy táctica, siempre hay alguien que le parece una tontería y pérdida de tiempo el proceso. Les enseñaré esta frase.

– Dicen “un buen pensador de diseño emplea y practica el integrative thinking, observando e interpretando la experiencia global como un todo, abriendo y cerrando el zoom según convenga.

– Introduce el modelo de Stuart Pugh de 11 etapas, aunque en el libro solo incluyen las de diseño: Detección de necesidades, estudio de mercado, especificaciones, diseño conceptual (generación de ideas y visualización), diseño de detalle y validación. Para la generación de ideas cuenta el método SCAMPER: Sustituir, combinar, adaptar, modificar, buscar otros usos, eliminar y reordenar. Me recuerda al libro Thinker Toys.

– También introduce el modelo de Bruno Murani: Problema, definición, elementos, recopilación de datos, análisis de datos, creatividad, materiales/tecnología, experimentación, modelos, verificación y solución..

– Dedica muchas páginas al proceso de design thinking: Comprender (entrevistas), observar (empatizar), definir (estructurar), idear (brainstorming), prototipar (pensar con las manos el customer journey), testear (sobre elementos físicos) e implementar (ser críticos). En este bloque tienen protagonismo y superposición la experiencia de usuario, creatividad, selección y diseño/ejecución.

– Señalan, parafraseando a Tim Brown, que todo proceso tiene 3 etapas (cosa que podemos ver en común en los 3 modelos anteriores) : inspiración, ideación e implementación.

– Cuentan el modelo Mc Kinsey del valor de la gente en forma de T: ingenieros con experiencia en marketing, artistas con MBA, etc. Es decir, que no sólo hace falta que haya en equipos gente de múltiples disciplinas sino gente con inquietudes y habilidades multi-disciplinares.

– Especifica las 4 partes de un business canvas: cómo, qué, quién y cuánto, y links con herramientas. Es más interesante de lo que parece porque al crear los míos propios, cambiando casillas, me he cargado un poco ese concepto.

– Invitan al usuario a visitar páginas, donde pueden probar estas herramientas de conceptualización interactiva mente.

– También habla del Lean canvas, Empathy Map y Value Proposition Canvas. De este último, creo que tengo que escribir un post dedicado.

En la evaluación global del libro, creo que al ser cortito y tener puntos brillantes es muy adecuado para regalar a responsables de producto de la viaja escuela. Dicen que formar es como encender un fuego y no como llenar un vaso. Puede encender fuegos. Eso si, hay que advertir que las primeras hojas casi me invitan a dejarlo y luego mejora. Sobre todo será útil si te planteas un problema e intentas aplicar las herramientas.

SAFe para CEOs y CIOs: El Nivel de Portfolio

$
0
0

1. Introducción

Para darle continuidad a los artículos en los que he venido documentando los conceptos y niveles básicos de SAFe, en el presente artículo se explicará los detalles del Nivel de Portfolio como marco de trabajo para la construcción y organización de compañías Lean-Agile entorno a la generación de valor.

Nivel de Portfolio

Figura 1. Nivel de Portfolio. Recuperado de www.scaledagileframework.com

2. El Nivel de Portfolio

Siendo el escalafón más alto de SAFe, en el Nivel de Portfolio es donde se encapsulan los mecanismos presupuestarios y de gobernanza necesarios para garantizar que las inversiones de la Empresa cumplan con los objetivos estratégicos del negocio.

Según las dimensiones de una Empresa cada Portfolio de SAFe consiste en un una o varias cadenas de valor en donde se definen, desarrollan y despliegan los tangibles que generan valor para el negocio y los clientes.

Dicho esto, los siguientes conceptos son considerados como claves:

2.1. La Conexión con la Empresa:

Se contempla que las comunicaciones entre la Empresa y Portfolio fluyan de manera bidireccional. En un sentido para que la Empresa provea el contexto estratégico necesario para la toma de decisiones y en el otro para dar retroalimentación de los procesos a los responsables del negocio.

comunicacion SAFe

Figura 2. Modelo de comunicaciones con la empresa con una cadena de valor. Recuperado de www.scaledagileframework.com

Dado que los temas estratégicos son la salida de un proceso de colaboración continua, se requiere que las comunicaciones y alineación sea completa. En tal sentido, se promueve el uso de indicadores de rendimiento que proporcionen datos cuantitativos que permitan la detección de fortalezas, debilidades, oportunidades y amenazas presentes en el Portfolio.

2.2. La Gestión del Portfolio:

La gestión de las actividades y la gobernabilidad de las inversiones sobre el Portfolio están previstas dentro del Program Portfolio Management (PPM). El PPM representa el nivel más alto como autoridad presupuestaria previsto por SAFe. Sobre él recaen las responsabilidades de la estrategia de inversión, la gestión del programa y la administración de los fondos de inversión.

Por lo general estas tareas son asistidas por una Oficina de Gestión del Programa que comparte las responsabilidades de guiar la ejecución del programa y la gobernabilidad.

Más allá de la responsabilidad de la asignación de los fondos de inversión en los flujos de valor, con el PPM los presupuestos son desarrollados y administrados con un enfoque Lean-Agile que prevé que la toma de decisiones sea rápida y con un control fiduciario apropiado.

Con este proceso la empresa ejerce su responsabilidad de inversión y retorno sobre los acordados como prioridades del negocio.

2.3. La Gestión del flujo del Portfolio en Épicas:

Otra de las responsabilidades a Nivel del Portfolio es el descubrimiento, definición y administración de las principales iniciativas que son requeridas por el negocio y que son transversales para toda la organización.

Para su gestión este tipo de iniciativas son divididas en dos tipos según su naturaleza. Las Épicas de Negocio, que captan y reflejan las nuevas capacidades del negocio y que pueden ser proporcionadas con la colaboración de todos los flujos de valor y las Épicas Facilitadoras que reflejan las iniciativas técnico-arquitectónicas que son necesarias para las nuevas capacidades.

Para gestionar este flujo de trabajo y hacerlo visible para todos los interesados, SAFe proporciona un Kanban en Nivel de Portfolio. En él, se hace visible y se proporcionan límites para los procesos. Con estos paneles se gestionan las épicas aprobadas y priorizadas.

Estos sistemas Kanban están diseñados para un propósito específico: capturar, analizar, aprobar y realizar un seguimiento a las épicas. Un ejemplo de este tipo de sistema podría ser el que se muestra en la figura 3.

Portfolio Kanban

Figura 3. Ejemplo de sistema Kanban propuesto por SAFe.

3. Conclusiones

Según lo hemos visto, la formulación de las estrategias de negocio son en gran parte una preocupación centralizada en la que colabora los principales responsables del negocio. Sin embargo la ejecución de dichas estrategias está descentralizada en el Portfolio y se apoya en la transparencia, la retroalimentación constante y una adecuada gestión de métricas para la correcta definición y desarrollo de las soluciones necesarias para hacer frente a los cambios en las necesidades de los clientes y en las nuevas oportunidades del mercado.

Con el Nivel de Portfolio se brindan los mecanismos presupuestarios y de gobernanza necesarios para garantizar la ejecución de dichas estrategias.

En Autentia proporcionamos soporte a la implantación corporativa de metodologías ágiles ayudando a la transformación digital de grandes organizaciones. Te invito a que te informes sobre los servicios profesionales de Autentia y el soporte que podemos proporcionar a tu empresa en en la implantación de frameworks de escalado de agile como SAFe y LESS.

4. Referencias

Cómo borrar ficheros del histórico de Git, y en general cómo manipular todo el histórico

$
0
0

Cómo borrar ficheros del histórico de Git, y en general cómo manipular todo el histórico





1. Introducción

Hay dos situaciones que son bastante típicas cuando trabajamos con repositorios de código:

  • Una es subir ficheros que no corresponden al repositorio. Bien porque los subimos por equivocación o porque los subimos con información sensible, como por ejemplo claves de usuario.

  • La otra es querer dividir un repositorio en dos porque ha crecido demasiado. Llega un momento donde aparecen proyectos con suficiente entidad como para tener su repositorio propio.

En estas situaciones si simplemente hacemos un nuevo commit quitando del repositorio los ficheros, el problema que se nos presenta es que estos siguen estando en el histórico, por lo que, si es información sensible siempre se podrá seguir recuperando, y si son ficheros que no deben estar en este repositorio (porque los pusimos por error o porque hemos dividido el repositorio en dos) seguirán ocupando espacio.

Para este tipo de situaciones Git es ideal ya que nos permite reescribir por completo todo el histórico, de forma que no dejemos rastro alguno de los ficheros en cuestión.

Si bien esto parece un poco de magia negra, en este tutorial vamos a ver cómo hacerlo.

¡¡¡Ojo porque estamos manipulando el histórico así que estas operaciones NO SON REVERSIBLES!!! Es más que recomendable que antes de hacer todo esto os hagáis una copia completa del todo el repositorio.



2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro 15” (2.5 GHz Intel i7, 16GB 1600 Mhz DDR3, 500GB Flash Storage).

  • AMD Radeon R9 M370X

  • Sistema Operativo: Mac OS X El Capitan 10.11.6

  • Git 2.9.2

  • Java v1.8.0_112



3. La herramienta: BFG Repo-Cleaner

BFG Repo-Cleaner es una herramienta de terceros (no pertenece a Git), escrita en Scala y Open-Source, que se plantea como la mejora alternativa para hacer este tipo de trabajamos, no porque no lo podamos hacer con Git directamente, sino porque es infinitamente más rápida y más fácil de usar, ya que es una herramienta pensada específicamente para hacer este trabajo.

Si también queréis ver una alternativa de cómo hacer esto mismo directamente con Git git-filter-branch, aquí tenéis una buena referencia: GitHub Help – Remove sensitive data

Para ejecutar BFG simplemente tenemos que descargarlo de su página web. Es un fichero .jar por lo que para poder ejecutarlo tenemos que tener instalado Java.

Para que resulte más sencillo ejecutarlo nos podemos hacer un alias:

$ alias bfg='java -jar <ruta donde esté instalado>/bfg.jar'


4. Preparando el repositorio

En primer lugar nos vamos a asegurar de que en el HEAD de nuestro repositorio no están los ficheros que queremos borrar. Esto es porque BFG considera este commit como protegido y no nos va a dejar cambiarlo (esto se puede forzar, pero mejor hagamos las cosas por las buenas). Así que usaremos git rm o cualquier otra técnica que usemos de forma habitual para conseguir un HEAD limpio, y nos aseguramos de haber hecho el commit y el push para garantizar que nuestro repositorio remoto está correctamente actualizado.

También tenemos que estar coordinados con el resto de nuestros compañeros y que no tengan trabajo pendiente ya que al cambiar el histórico no van a poder mergear nada. Mi recomendación sería que todo el mundo llegue a un punto estable, hacer la modificación del histórico y luego que todo el mundo se vuelva a clonar el repositorio. Así seguro que no tenemos problemas.

Ahora que ya tenemos nuestro repositorio remoto limpio y a nuestros compañeros coordinados, necesitamos clonarnos el repositorio en local ya que BFG trabajar en local, pero no nos vale un clone normal, tenemos que hacer un nuevo clone con la opción --mirror. Con esta opción conseguimos lo que se denomina bare repository, que es una copia del repositorio con todos los ficheros de administración y control, y que no podemos usar para hacer checkout local. Por ejemplo:

$ git clone --mirror git://example.com/some-big-repo.git


5. Borrando los ficheros que sobran

BFG nos permite varias opciones de borrado del histórico, por ejemplo por tamaño, o incluso convertir ficheros a Git LFS (Git Large File Storage), o simplemente cambiar texto dentro de un fichero. También podemos borrar ficheros o directorios completos (todo su contenido de ficheros y subdirectorios). Cuando hacemos borrado ficheros o directorios tenemos que tener en cuenta que lo hace por nombre no por ruta completa, así que cuidado con nombres demasiado comunes, no sea que borremos más de la cuenta!!!

Por ejemplo, para borrar directorios:

$ bfg --delete-folders "submodule-*" some-big-repo.git

En el ejemplo se ve como podemos usar wildcards en el nombre de los directorios o ficheros.

Para borrar ficheros:

$ bfg --delete-files "really-big-file.zip" some-big-repo.git

Estas operaciones lo que hacen internamente es sacar la referencia de estos ficheros de cualquier commit, pero los ficheros siguen estando dentro del repositorio. Para eliminarlos por completo tenemos que limpiar el propio repositorio de Git:

$ cd some-big-repo.git
$ git reflog expire --expire=now --all && git gc --prune=now --aggressive


6. Borrar los commits vacíos

Si hemos borrado muchos ficheros o directorios (por ejemplo si lo que hemos hecho es partir un repositorio en dos) es muy probable que en el histórico se nos hayan quedado commits vacíos, commits que se ven en el histórico pero que dentro no se hace referencia a ningún ficheros. Lo ideal sería limpiar estos commits, reescribiendo el histórico, ya que no partan ningún valor.

Para ello lo mejor que he encontrado por ahor es hacer:

$ git filter-branch -f --prune-empty --tag-name-filter cat -- --all

Esto borra los commits vacíos, pero aun así nos pueden quedar commits de merges también vacíos. Para eliminar estos commits de merges vacíos podemos hacer:

$ git filter-branch -f --prune-empty --parent-filter /tmp/rewrite_parent.rb master

Veis que estoy haciendo referencia a un script en Ruby /tmp/rewrite_parent.rb. Este lo podéis poner en la ruta que queráis y tiene que tener el siguietne contenido:

#!/usr/bin/ruby
old_parents = gets.chomp.gsub('-p ', ' ')

if old_parents.empty? then
    new_parents = []
else
    new_parents = `git show-branch --independent #{old_parents}`.split
end

puts new_parents.map{|p| '-p ' + p}.join(' ')

Referencias de estas operaciones:



7. Cómo ver todos los ficheros en el histórico

Para ver como nos va quedando el histórico poodemos ejecutar el siguiente comando que sacará un listado de todos ficheros que existen dentro del histórico:

git log --pretty=format: --name-only --diff-filter=A | sort -u

La referencia: Stack Overflow – List all the files that ever existed in a Git repository



8. Subir de nuevo los cambios a nuestro repositorio remoto

Todo el trabajo que hemos hecho está aplicado sobre nuestro repositorio local (acordaos del clone --mirror que hicimos al principio), así que por ahora no hay peligro. Pero llegados a este punto nos toca subir los cambios a nuestro repositorio remoto para que se los puedan disfrutar todos nuestros compañeros.

¡¡¡Ojo aquí porque a partir de aquí las operaciones que hagamos no tiene vuelta atrás!!!

Ante de hacer la subida os recomiendo que borréis todos los tags que tengáis en el repositorio remoto. Esto es porque al reescribir el histórico han cambiado las referencias y al intentar hacer la subida se encuentra con un conflicto (yo por lo menos no he conseguido forma de forzar esta operación y los he borrado a mano). No hay problema en borrarlos, porque como a continuación vamos a hacer una subida del repo, estos tags se van a volver a crear, con las referencias a los commits correctos.

Y ya sin más dilación ejecutamos hacemos la subida del repositorio:

$ git push origin --force --all


9. Conclusiones

Git nos ofrece una gran potencia y flexibilidad pero, como pasa en Spiderman un gran poder conlleva una gran responsabilidad. Así que recordad que las operaciones que hemos visto aquí son destructivas e irreversibles, por lo que debemos usarlas con precaución y siempre coordinados con el resto del equipo.

Una vez repetida la advertencia, si me gustaría destacar como tenemos a nuestro alcance una potente herramienta para empezar pequeños (un único repositorio cuando todavía no tenemos claras todas las piezas que formarán parte del proyecto) y una vez las cosas van cogiendo peso, ir separándolo en otros repositorios con entidad suficiente.



10. Sobre el autor

Alejandro Pérez García (@alejandropgarci)
Ingeniero en Informática (especialidad de Ingeniería del Software) y Certified ScrumMaster

Socio fundador de Autentia Real Business Solutions S.L. – “Soporte a Desarrollo”

Socio fundador de ThE Audience Megaphone System, S.L. – TEAMS – “Todo el potencial de tus grupos de influencia a tu alcance”

Cambia el “Send from” de tus correos de WordPress sin plugins

$
0
0

Aprende a cambiar el correo que aparece en “Send from” de tus correos de WordPress sin plugins.

Índice de contenidos


1. Introducción

Al hacer la instalación de WordPress, en los correos que envía la aplicación aparece como remitente “wordpress@tudominio.com”.


En la web aparecen muchos plugins que permiten cambiarlo, pero corremos el riesgo de vulnerar la seguridad de WordPress. Con este sencillo tutorial aprenderás a modificarlo con una función muy sencilla. Vamos a verlo.


2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Nginx 1.11.8
  • WordPress 4.7.0
  • PHP 7.0

3. La solución

Primero debes localizar el archivo functions.php, que está en la carpeta del tema. La ruta es wp-content/themes/tutema. Una vez localizado, lo editas y añades el siguiente código:

Cambiar FROM
function filter_wp_mail_from($email){
	$sitename = strtolower( $_SERVER['SERVER_NAME'] );
	if ( substr( $sitename, 0, 4 ) == 'www.' ) {
	$sitename = substr( $sitename, 4 );
	}

	$myfront = "loQueYoQuieraEnFrom@";
	$mydomain = $sitename;
	$myfrom = $myfront . $mydomain;
	return $myfrom;
	}
	add_filter("wp_mail_from", "filter_wp_mail_from");

También puedes cambiar el “email name” añadiendo el siguiente código en el mismo documento functions.php:

Cambiar NAME
function filter_wp_mail_from_name($from_name){
	return "Nombre de remitente";
	}
	add_filter("wp_mail_from_name", "filter_wp_mail_from_name");

4. Conclusiones

Con dos sencillas funciones puedes evitar instalar un plugin y ahorrar trabajo y tiempo.


5. Referencias

Scaled Scrum con Nexus

$
0
0
Estos dos últimos días he asistido al curso de Jerónimo Palacios (@giropa832), que podéis encontrar aquí https://jeronimopalacios.com/training/scaled-professional-scrum/

 

Después de la charla que di en la CAS2016,  Jerónimo me propuso que fuera a una de sus formaciones para que viera la posición oficial de Scrum.org y me pareció muy buena idea: seguro que algo aprendería. Mi visión de la agilidad es muy libre, y tomo de cada metodología y el sentido común lo que creo más razonable, y no está mal ver otros planteamientos más “corporativos”.

Dentro de las recomendaciones de la formación, proponía llevar los deberes hechos habiendo leído la guía de Nexus (os recomiendo que visitéis también los Whitepapers). Realmente son 12 hojas, y en principio tenía dudas de lo que podría dar de sí un curso de 2 días completos.

Ya os tengo que adelantar que ha merecido claramente la pena. Sobre todo porque, adicionalmente a la guía en el curso, se presentan un montón de prácticas complementarias (voy a mezclar una cosa con la otra en el resumen). Lo que se trata de diferenciar es el núcleo de Nexus de la otras prácticas.

También he de decir que en el curso había gente muy avanzada en Agile de otras empresas, que ya estaban experimentando con Nexus y tenían dudas concretas. Las conversaciones han sido muy enriquecedoras y amplias, no solo relacionadas con escalado de Agile: modelos de formación de empresas, frustraciones a la hora de ayudar a implantar Agile, diferencia entre Scrum y gestión del cambio organizacional, etc. A un curso también se va a conocer otra gente interesante.

Como siempre, adelanto la frase “vemos la vida como somos, no como es” por lo que voy a contar un conjunto de cosas que me han gustado. No sé si seré del todo preciso respecto a lo que Nexus dice, ya que la capacidad de aprendizaje es limitada. Seguro que otra persona le podría sacar otro partido en base a su expertis: tendréis que ir porque esto son sólo unas pinceladas. Claramente, Jerónimo tiene un buen discurso en amplitud y profundidad (no está mal acordarnos de eso de “una comunidad de profesionales” valorando/validando el talento y conocimiento de compañeros).

————

Lo más destacado podría decir que es lo más evidente: hacer Scrum de verdad antes de escalar. Muchas organizaciones tienen al Product Owner ausente, es un delegado de un jefe controlador que luego le desautoriza, el Scrum Master está poco formado, hay poco conocimiento sobre técnicas de integración continua, los equipos no son cross-funcionales, hay poca auto-crítica en las retrospectivas, etc. Parece lógico que para escalar hay que partir de una buena base. Las empresas se empeñan en meter Scrum en sus estructuras de poder, cuando lo que hay que hacer es cambiar la estructura.

Hay que formar a toda la organización y si el CEO/área financiera o área de compras tienen formación, no se producirán desajustes.

Nexus sigue los principios de Scrum, con más de 2 equipos. Con dos parece razonable seguir usando Scrum.

En Nexus hay un refinamiento como práctica formal al principio, donde se define un único back-log para los distintos equipos.  El PO puede tener mucha carga de trabajo lo que no significa que tenga que estar solo. Tampoco significa que el Scrum Master tenga que estar recordándole constantemente cuál es su trabajo para que el equipo no se quede sin carga (volvemos a los del mal Scrum de base). Jerónimo recomendaba dejar que los niños se caigan a poca altura, y se tuviera que poner solución desde la dirección, para que no llegase a ser la altura demasiado elevada.

Otra cosa interesante de la que se habló es la de hacer responsable al PO de la rotación el equipo técnico (en parte). La rotación tiene un coste muy elevado. También se contó lo contrario, y como Jerónimo está recomendando a sus clientes el contratar equipos externos y si no son capaces de hacer entregas de valor, prescindir de ellos. Siempre que esto no sea una perversión, puede parece muy razonable para ganar capacidad y servir de muestra (a esto sí jugamos en Autentia).

Para definir las prioridades, parece razonable mapear un user-history-mapping con los objetivos de negocio. Los equipos tienen una capacidad y parece lógico hacer estimaciones rápidas para determinar la correlación entre el coste y el alineamiento con el objetivo. El modo de hacerlo me pareció simple y brillante simplemente poniendo los objetivos a la izquierda, como filas, de las épicas estimadas. Obviamente estamos a un nivel de detalle alto y esto tendría que repetirse a nivel de historias cuando se haya hecho un plan de releases.

Los miembros de los equipos se pueden auto-asignar al equipo más conveniente en base al valor que pueden aportar.  Esto como siempre con cariño, para que estén equilibrados y pensando en lo mejor para el Nexus y no estar con los amiguitos.

La entrega de valor es una entrega integrada de todos los equipos. La duración del Sprint puede ser distinta en cada equipo pero deben ser múltiplos (uno puede ser de 2 y otro de 4 pero no de 3) para coincidir tanto en el refinamiento como en la revisión  y retrospectiva del Nexus, independientemente de las prácticas del equipo. Esto suena muy razonable en organizaciones por componentes, que Nexus no condiciona a que no existan (por ejemplo con un host, eso de 2 velocidades).

Hay un Nexus Integration Team (NIT) que esta formado por el PO, SM y miembros de los distintos equipos cuya prioridad es entregar ese valor integrado. Tendrá funciones de coach, delegación y ejecución, según sea el caso.

Si no se produce una entrega de valor el NIT puede pasar a modo de emergencia. Esto significa subordinar el trabajo de los equipos a la integración. En el curso se habló varias veces de la fijación por hacer que la gente tenga ocupadas todas las horas. Pongamos un caso: si tenemos tres equipos y no somos capaces de integrar su trabajo en el ciclo ¿parecería lógico que los equipos siguieran construyendo para ocupar sus horas y complicar todavía más el ciclo de integración? Parece lógico bajar la “productividad” y automatizar la integración. Obviamente si integras con facilidad y no dispones de mecanismos de pruebas automáticas, esto de integrar más deprisa no vale para nada: no tienes red de seguridad.

Es importante mapear las dependencias entre historias y tareas. La integración es la clave por lo que visualizar las dependencias es fundamental.

En la reunión de revisión (esto aplica a Scrum en su amplio sentido), que normalmente mal llamamos de demostración, se debe hablar del incremento del proyecto y el PO también debe contar el estado actual del negocio, para que los equipos se alineen.

Fue divertido ejercer de CEO cuando los presente en el curso decían de hacer partícipes a la dirección de sus herramientas. Ya os adelanto que a la dirección le tienes que resumir la información y, a la vez, disponer del detalle si lo piden. Si no lo haces así, te pondrán una oficina de proyecto. Se hablaron de distintas técnicas de comunicación con Nexus de muchos equipos, como el modo feria de muestras, donde cada equipo contase sus avances. Esto que podría parecer una tontería puede ser muy útil para hacer conscientes a los equipos de las necesidades de no solo hacer, sino poner en valor el trabajo: saber venderlo en grandes organizaciones.

Las retrospectivas del Nexus se hacen en tres fases: NIT-equipo-NIT. Tienen como objetivo obtener feedback del incremento integrado. La auto-organización del individuo en Scrum se ve limitada por la de su equipo. En Nexus la auto-organización del equipo se ve limitada por la decisiones a nivel de NIT, por lo tanto también la de los miembros. Esto no significa que dentro de cada equipo no puedan tener sus propios mecanismos internos de retrospectiva o mejora.

A escala, los time boxes son relativos, no están definidos/limitados por la metodología Nexus.

Jerónimo dijo una frase muy interesante: “los desarrolladores deben dejar de amar su código y empezar a amar el valor aportado por ese código”. Y esto no significa que el código no deba ser excepcional. Otra frase destacada para equipos que se enredan o pierden encadenando retos técnicos es “no hables en la reunión diaria si no puedes decir el incremento de valor que has entregado”.

Surgieron comentarios sobre empresas que con personal cualificado, interno y haciendo Scrum de verdad no tuvieron que escalar más equipos.

También hablamos de las estrellitas o héroes dentro de los equipos. Agilidad es que una empresa vaya a por un objetivo. Si se cambia la dirección, toda la organización se debe alinear al nuevo objetivo. Posiblemente en las organizaciones (por la oferta/demanda) se entre produciendo un alto paternalismo donde los programadores imponen hacer solo los que les gusta. Cada vez me encuentro más incluso que imponen las tecnologías o los días de la semana que quieren trabajar.

Una de las preguntas iniciales que planteé es si el Scrum Master es un rol a tiempo completo y cómo encaja en el Nexus, ¿uno por Nexus?, ¿uno por equipo?. Jerónimo insistió en que es el equipo o Nexus el que debería querer que el Scrum Master no se fuera. Además, le pagas por disponibilidad, para que cuando tengas una movida, esté allí. Es interesante mirarlo desde esta perspectiva.

Bueno, supongo que habréis percibido que hay chicha. Gracias Jerónimo por el curso, y estaré agradecido a todo el que me quiera invitar a uno suyo, me encanta ser un eterno aprendiz y tratar de compartir lo aprendido.

Visitaré su blog https://jeronimopalacios.com/blog/ porque comentó que estaban trabajando en su comunidad sobre el cambio organizacional, donde supongo que estamos todos de acuerdo en que todavía está más en la comunidad de ideas que en la de experto.


Async/Await en javaScript

$
0
0

En este tutorial veremos qué es la especificación async/await, como transpilar nuestro código para poder usarlo en navegadores y qué ventajas ofrece respecto a las promesas.

Índice de contenidos

1. Introducción


Las promesas en javascript son una manera de conseguir que el código asíncrono se comporte como si fuera síncrono, facilitándonos la vida enormemente. Sin embargo, esto tiene algunas limitaciones. Async/Await es una propuesta para extender la sintaxis de javaScript con las palabras reservadas async y await, cuyo uso permitirá -cuando se estandarice- tratar las funciones que devuelven promesas en nuestro código como si fueran funciones síncronas que devuelven directamente valores en vez de promesas.

Aunque actualmente ningún navegador soporta async/await, podemos utilizar este mecanismo transpilando código que lo utilice a código que sí entiendan los navegadores.

Cómo funciona:

Por ejemplo, sea getFilm una función que se conecta a un hipotético servicio web que devuelve una película al azar, y getMain() otra función promisificada del mismo servicio que devuelve el nombre del protagonista de una película dada. Con la sintaxis de Async/Await podemos hacer lo siguiente:

async function queue(){
        var film = await getFilm(); //Supongamos que toca 'Matrix'
        var main = await getMain(film); //Neo
        console.log(main);
    }
    queue();//escribirá 'Neo' en la consola.

En vez de tener que utilizar la respuesta de la primera petición en un incómodo then( function(){} ), usando la palabra especial await podemos usar getFilm() y getMain() como si devolvieran valores síncronos en vez de promesas.

Esto es mucho más cómodo de escribir, y sobre todo resulta en código mucho más legible.

2. Entorno


Cualquier editor de texto y un navegador debería valer para ejecutar snippets de javaScript.

No obstante, async/await es una especificación de ecmaScript 7, y todavía no es soportada de forma nativa por ningún navegador ni por node.js. Para poder utilizar async/await en nuestro código a día de hoy, tendremos que usar algún transpilador para convertir nuestro código con la sintaxis de async/await en código que utilice promesas o generadores.

Como es un proceso relativamente complejo, he dedicado más abajo un capítulo a este tema.

3. Limitaciones de las promesas


El código que pusimos más arriba, asumiendo como hemos dicho que getFilm y getMain son métodos que hacen peticiones http a un servicio web y devuelven promesas, sería equivalente al siguiente:

getFilm()
    .then(getMain)
    .then(console.log);

Que, tal vez, sabiendo cómo funcionan las promesas, sea más fácil de escribir y de leer.

No obstante, esto es porque getMain y console.log toman un solo parámetro, lo que hace que sea muy sencillo pasarlas directamente a then como parámetro.

Imaginemos un cambio tan tonto como hacer un console.log al final del protagonista y también del film. El código promisificado se vuelve como el que sigue:

var film = null;
    getFilm( function(res){
        film = res;
        return getMain(film);
    })
    .then(function(res){
        console.log(film , res);
    });

De pronto, es bastante más complejo y enrevesado. En cambio, si queremos retocar nuestro anterior código con async/await, podríamos simplemente hacer:

(async function(){
        var film = await getFilm();
        var main = await getMain(film);
        console.log(main , film);
    })();

A parte de la molestia de tener que envolver todo el código en una función declarada con la palabra reservada await -en este caso es una función auto-invocada, IIFE-, y de tener que usar await antes de invocar funciones que devuelven promesas, realizar cambios en código que utiliza async/await es mucho más sencillo que en código promisificado, y es mucho más agradable de leer.

4. Async/Await


Cuando usamos la palabra reservada async al declarar una función, suceden dos cosas:

  • Podemos usar la palabra await dentro de esa función para acceder directamente a los valores que devolverían métodos que devuelven promesas.
  • La propia función que estamos declarando devuelve su valor de retorno como una promesa.

Cuando usamos la palabra reservada await al invocar una función:

  • Si la función sin await hubiera devuelto una promesa satisfecha, la llamada devolverá el valor de esa promesa.
  • Si la función sin await hubiera devuelto una promesa rechazada, lanzará un error con la razón del rechazo.
  • Si la función sin await hubiera devuelto un valor que no es una promesa, la llamada devolverá ese mismo valor (esto incluye undefined en llamadas a funciones sin valor de retorno).

Intentar utilizar await en cualquier lugar que no sea una función declarada como async resultará en un error. Por el contrario, se puede utilizar la palabra reservada async para declarar funciones sin usar await en ningún momento; no es que sea muy útil, sin embargo.

Funciones async como promesas

Como dijimos, cuando declaramos una función usando async podemos encadenar then y catch tras ella, dado que es una promesa:

async function get(){
        return 100;
    }

    film().then(console.log); /100

Esto es así aunque internamente no haya código asíncrono de ninguna clase (como en el ejemplo, donde get() devuelve ‘100’ sin más).

Esto tiene sentido, dado que async está diseñada para utilizar await dentro de ella. Como los valores que devuelve await siempre son valores definitivos y no promesas, pero realmente el código es asíncrono, es necesario que las funciones declaradas como async, por su parte, devuelvan promesas.

Gestión de errores

La función get(), al estar declarada con la palabra aysnc, automáticamente devuelve su valor como una promesa. Incluso si, como en este caso, devolvía directamente un valor sin ninguna llamada ajax ni nada que tenga que ver con el código típicamente asíncrono.

Esto funciona igualmente cuando hay errores. Si hay algún error dentro de una función declarada como async, éste es automáticamente atrapado y devuelto como una promesa rechazada:

async function throwError(){
        throw new Error('Esto no aparecerá como un Error sino como una promesa rechazada');
    }

    throwError().catch(console.log); //Error: Esto no aparecerá como un error sino como una promesa rechazada...

No solamente los errores se convierten automáticamente en promesas cuando una función declarada con async es llamada:

Además, y de forma inversa, cuando una llamada precedida por await devuelve una promesa rechazada, ésta se convierte en un error:

function rejectedPromise(){
        return new Promise( function(resolve , reject){
            reject('promesa rechazada');
        });
    /*Es mala práctica rechazar promesas con strings. lo adecuado sería:
        reject(new Error('promesa rechazada') );
    En este caso, evito instanciar un Error aquí para que quede claro que es
    async/await quien está generando y atrapando por su cuenta
    un Error en la llamada a get()*/

    }

    async function get(){
        try{
            await rejectedPromise();
        }
        catch(error){
            console.log(error);
        }
    }

    get();//Promesa rechazada

Realmente, no hay mucho más que decir sobre las funcionalidades async/await. Una vez que se entiende cómo async y await se relacionan con las promesas, es realmente sencillo y cómodo utilizar esta sintaxis.

Limitaciones de async/await

Errores

Como el código que generamos para poder utilizar -a día de hoy- async/await es transpilado, el código real interpretado por los navegadores no es el código que hemos escrito, sino el código transpilado. Aunque podemos acceder en el navegador a nuestro propio código original por medio de sourcemaps, podemos tener bastantes problemas con esto:

  • No siempre el código transpilado funcionará bien, o como creemos que funciona. Este tipo de bugs son muy difíciles de entender y detectar.
  • A veces, el navegador puede volverse loco y no traducir bien las líneas entre los scripts al añadir breakpoints. Suelen ser problemas de caché, pero pueden volverte loco hasta que entiendas lo que está pasando.

Al margen del problema de la transpilación, que no durará para siempre (en algún momento todos los navegadores soportarán async/await nativamente), await hace referencia siempre a promesas. Cuando encontremos errores, serán seguramente promesas que han sido rechazadas -pero invocadas con await-, fruto de algún Error real dentro de algún callback. Y al revés, cuando encontremos promesas rechazadas puede que originalmente fueran errores que han sido transformados en promesas por async.

Es importante tener muy presente cómo async y await modifican promesas y errores para poder entender dónde exactamente ha habido un fallo cuando estemos depurando código con async/await, o podemos volvernos más locos que con un callback hell.

Scope

Async/await permite que nuestro código asíncrono se comporte como si fuera síncrono. Pero esto está limitado únicamente al scope de funciones declaradas mediante async.

Cualquier línea de código ejecutada inmediatamente después de una llamada a una función async no esperará a ninguna de las llamadas internas de esa función que utilicen await. Un ejemplo:

async function getMain(){
        var film = await getFilm();
        return await getMain(film);
    };

    getMain().then(console.log)
    console.log('fin del script'); //esta línea se ejecutará antes que el log anterior.

Aquí, la segunda línea dentro de getMain espera a la primera -aunque sea una llamada asíncrona- por el uso de los await dentro de la función getMain, por estar declarada mediante async.

No obstante, los logs posteriores no están dentro de una función async. El then proveniente de getMain() se ejecutará después del log que imprime ‘fin del script’, porque la llamada es asíncrona.

Las alternativas a esto, para que el snippet se comporte como queremos, sería encadenar el último log a la promesa que devuelve getMain:

getMain(console.log).then( function(){ console.log('fin del script') });

O, por qué no, lanzar los logs a su vez dentro de una función async:

(async function(){
        console.log( await getMain() );
        console.log('fin del script'); //Ahora sí que aparecerá al final
    })();

Por cierto, como se ve en este ejemplo, dado que getMain, por ser async, devuelve una promesa, puede a su vez ser invocada mediante await en otra función async.

A parte de todo esto, la limitación más obvia es el uso necesario de las propias palabras reservadas async y await, pero es un mal menor si tenemos en cuenta que las alternativas implican una sintaxis mucho más fea y complicada.

Transpilando async/await

Como hemos dicho más arriba, la especificación async/await no está soportada por ningún navegador. Si utilizas las palabras reservadas async o await en algún navegador actual, éste lanzará un error quejándose de que async y await son palabras reservadas.

Para poder usar esta especificación en código real, debemos usar un transpilador que lea nuestro código que utiliza async/await y lo transforme en código que utilice promesas.

Para ello utilizaremos el transpilador Babel y su plugin transform async to generator.

He creado un repositorio en github a modo de ejemplo de cómo usar webpack y babel para transpilar código que contenga async/await a código legible por navegadores.

Podéis encontrar el repositorio en este enlace.

La clave para que todo funcione es añadir el preset es-2017 en el fichero de configuración de babel y en el de webpack, y no olvidarse de incluír el plugin transform-async-to-generator en la configuración de babel, así cómo el resto de dependencias que webpack y babel necesitan para poder transpilar el código. Podeís ver la lista de plugins necesarios en el package.json.

7. Conclusiones


Por el precio de incluir en nuestro código las palabras especiales async y await, podemos trabajar con código asíncrono de forma mucho más cómoda que utilizando promesas o callbacks. Realmente, una vez transpilado el código, todo son ventajas.

El problema es que es mandatorio transpilarlo para poder hacer algo con nuestro código. Si estás trabajando en un proyecto donde ya se utiliza algún transpilador para utilizar ES6 o alguna otra feature experimental de ES7, añadir async/await no supondrá mucho problema y hacerlo sería altamente recomendable.

Sin embargo, si ese no es el caso, puede que no quieras empezar a transpilar tu código solamente por la posibilidad de usar async/await. Lo cierto es que babel es bastante lento y transpilar varios ficheros de una tacada puede llevar varios segundos en una base de código extensa.

Aunque usar transpiladores está muy ‘a la moda’, la verdad es que son lentos y esto puede afectar bastante a la mecánica de desarrollo, y también es tiempo que hay que sumar a cada build.

Así que, si bien async/await es una mecánica genial, no todos los proyectos pueden incorporarlo sin contrapartidas, y algunos tendrán que esperar a que los navegadores lo soporten.

Por último, que las funciones declaradas con async se transformen automáticamente en promesas, tiene el efecto de que es muy sencillo añadir incrementalmente async/await a una base de código que utiliza promesas.

Sin embargo, esto no es cierto si nuestro código está plagado de callbacks y queremos empezar a introducir async/await poco a poco. Si redefinimos una función que ejecuta un callback para que devuelva una promesa, todos sus clientes deberán actualizarse.

8. Referencias


Algunos de los mejores enlaces que podéis leer sobre async/await en JavaScript son los siguientes:

Creación de entornos con múltiples configuraciones en Vagrant con Ansible

$
0
0

En este tutorial vamos a ver cómo definir varias configuraciones para los roles de Ansible y cómo desplegarlos en Vagrant. Esto puede ser útil cuando tenemos entornos que utilizan un stack tecnológico similar (por ejemplo dos sitios web de WordPress).

Índice de contenidos


1. Introducción

Aquí nos vamos a centrar en qué modificaciones hay que hacer para poder definir múltiples configuraciones. En este otro tutorial podéis ver una explicación detallada sobre cómo instalar y el funcionamiento de Vagrant y Ansible.


2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro 15′ (2 Ghz Intel Core I7, 8GB DDR3).
  • Sistema Operativo: OS X El Capitan 10.11.6
  • Ansible 2.2.0.0
  • Vagrant 1.8.7

3. Host Vars

En nuestro proyecto de Ansible creamos una carpeta a la que llamaremos “host_vars”. En ella vamos a añadir un fichero por cada configuración diferente que queramos tener.

Por ejemplo vamos a ver cómo haríamos para un rol de MySQL.

Configuración 1
mysql_databases:
     - name: baseDeDatos1
       replicate: no

mysql_users:
     - name: usuarioA
       pass: 12345
       priv: "*.*:ALL,GRANT"
Configuración 2
mysql_databases:
     - name: baseDeDatos2
       replicate: no

mysql_users:
     - name: usuarioB
       pass: 546789
       priv: "*.*:ALL,GRANT"
De esta forma ejecutaremos el rol de MySQL cambiando el nombre de los usuarios y de la base de datos según nos interese.
- name: MySQL | Ensure the database's are present
  mysql_db:
    name: "{{ item.name }}"
    collation: "{{ item.collation | default('utf8_general_ci') }}"
    encoding: "{{ item.encoding | default('utf8') }}"
    state: present
  with_items: "{{mysql_databases}}" # Esta variable tomará valores diferentes
  when: mysql_databases|lower() != 'none'

4. Creación de JSON para el Vagrantfile

Una buena forma de añadir y modificar configuraciones de forma clara es crear un archivo JSON para cada configuración donde asignaremos la ip, el mapeo de los puertos, etc.

{
    "nodes" :
    {
        "maquina1" :
        {
            ":node" : "maquina1",
            ":ip" : "192.168.33.10",
            ":host" : "grupoMaquinas.maquina1",
            "ports" : [
                {
                    ":host" : 2222,
                    ":guest" : 22,
                    ":id" : "ssh"
                },
                {
                    ":host" : 33066,
                    ":guest" : 3306,
                    ":id" : "mysql"
                }
            ],
            ":memory" : 2048,
            ":ansible.inventory_path" : "../inventory",
            ":ansible.verbose" : "vvv",
            ":ansible.playbook" : "../playbook.yml"
        },
        "maquina2" :
        {
            ":node" : "maquina2",
            ":ip" : "192.168.33.11",
            ":host" : "grupoMaquinas.maquina2",
            "ports" : [
                {
                    ":host" : 2223,
                    ":guest" : 22,
                    ":id" : "ssh"
                },
                {
                    ":host" : 33067,
                    ":guest" : 3306,
                    ":id" : "mysql"
                }
            ],
            ":memory" : 2048,
            ":ansible.inventory_path" : "../inventory",
            ":ansible.verbose" : "vvv",
            ":ansible.playbook" : "../playbook.yml"
        }
      }
    }

Es importante que los puertos en la máquina anfitriona (host) sean diferentes en cada configuración para evitar conflictos.

Una vez hecho esto, solo es necesario leer el archivo JSON en el Vagrantfile y asignar las variables, iterando sobre cada configuración que hayamos definido.

nodes = (JSON.parse(File.read("nodes.json")))['nodes']

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box     = "ubuntu/trusty64"

  nodes.each do |node|
    name = node[0] # name of node
    node_values = node[1] # content of node

    config.vm.define name do |config|

      # configures all forwarding ports in JSON array
      ports = node_values['ports']
      ports.each do |port|
        config.vm.network :forwarded_port,
          host:  port[':host'],
          guest: port[':guest'],
          id:    port[':id']
      end

      config.vm.hostname = node_values[':node']
      config.vm.network :private_network, ip: node_values[':ip']

      config.vm.provider :virtualbox do |vb|
        vb.customize ["modifyvm", :id, "--memory", node_values[':memory']]
        vb.customize ["modifyvm", :id, "--name", node_values[':node']]
        vb.customize ["modifyvm", :id, "--ioapic", "on"]
        vb.customize ["modifyvm", :id, "--cpus", "2"]
      end

      # Provision config
      config.vm.provision "ansible" do |ansible|
        ansible.inventory_path = node_values[':ansible.inventory_path']
        ansible.verbose = node_values[':ansible.verbose']
        ansible.playbook = node_values[':ansible.playbook']
        #ansible.tags= "vhosts"
      end

    end
  end
end

5. Modificación del Inventory

En este archivo añadimos una entrada por cada configuración que tengamos.

[grupoMaquinas]
maquina1 ansible_ssh_host=127.0.0.1 ansible_ssh_port=2222 ansible_ssh_user='vagrant' ansible_ssh_private_key_file='../maquina1/virtualbox/private_key'

maquina2 ansible_ssh_host=127.0.0.1 ansible_ssh_port=2223 ansible_ssh_user='vagrant' ansible_ssh_private_key_file='../maquina2/virtualbox/private_key'

En este ejemplo “[grupoMaquinas]” hace referencia a ambas configuraciones y “maquina1” y “maquina2” a cada una por separado.

Es importante que el Inventory esté en la misma ruta que el directorio “host_vars” y que el nombre que le demos a cada configuración que definamos (en este caso “maquina1” y “maquina2”) sea el mismo que el nombre de cada fichero dentro de “host_vars”.


6. Desplegando en Vagrant

Ahora es necesario especificar la configuración que queremos usar al introducir los comandos de vagrant. Por ejemplo:

vagrant up maquina1

vagrant provision maquina2

7. Conclusiones

Una vez hayamos modificado estos archivos, podemos seguir añadiendo todas las configuraciones que queramos de forma sencilla.

De esta forma, nos podemos asegurar que sólo cambia la configuración de cada entorno pero que los roles se instalan exactamente igual (mismos paquetes, versión…)


8. Referencias

Userstyles: aplicando tus propios estilos a internet

$
0
0

En este tutorial veremos cómo aplicar nuestros propios estilos a páginas web de terceros haciendo uso de un plugin muy sencillo a nivel del navegador.


0. Índice de contenidos.


1. Introducción

No por simple iba a dejar de explicaros cómo podemos cambiar el modo en el que se muestra, tanto el contenido como la presentación de cualquier página web o aplicación que se ejecuta en un navegador de una manera muy simple.

Mi necesidad surgió el día que empezamos a usar trello en un proyecto para gestionar el kanban del equipo de arquitectura. La interfaz de trello por sencilla es magnífica, pero teníamos un problema más simple aún si cabe, era tal la cantidad de tareas pendientes que nos quedaban por resolver que el scroll se hacía infinito y perdíamos la percepción de a qué nivel debíamos reubicar una tarea cuando priorizábamos.

Jugando con el inspector de chrome o el firebug de firebox éramos capaces de modificar los estilos en caliente en el html para cambiar el modo en el que se visualizaban las columnas.

¿Por qué no encontrar una manera en la que los cambios se mantengan de forma permanente?, nuestra búsqueda comenzó por buscar algún plugin en chrome y, aunque hay muchos, con Stylish encontramos una respuesta a nuestras necesidades.


2. Entorno.

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro 15′ (2.5 GHz Intel Core i7, 16GB DDR3).
  • Sistema Operativo: Mac OS El Capitan 10.11
  • Google Chrome Versión 55.0.2883.95 (64-bit)
  • Stylish 1.5.2

3. Instalación y uso de Stylish.

Stylish es un plugin de chrome que permite asignar estilos personalizados a las páginas que se renderizan en el navegador en función de su nombre de dominio, es decir, que podemos programar la asignación de unos estilos específicos a la página de trello.com o a cualquier página en internet.

La instalación es simple, no tenemos más que buscarlo en las extensiones de chrome e instalarlo.

Una vez hecho esto encontraremos un icono en la barra de extensiones desde el cual podremos empezar a trabajar.

Básicamente tenemos dos opciones:

  • escribir nuestros propios estilos y asignarlos a un dominio,
  • buscar y aplicar los estilos que otros usuarios hayan podido compartir para un dominio o aplicación concreta y esto es precisamente lo que hace realmente interesante a este tipo de plugins.

3.1. Aplicando estilos de terceros.

Estando en la página de trello y desde la opción del menú de Stylish “Buscar más estilos para este sitio”, nos llevará a realizar una búsqueda sobre el site https://userstyles.org/styles/browse/trello donde podemos encontrar estilos que otros usuarios han compartido sobre trello.

Podemos hacer una primera prueba instalando el siguiente estilo (https://userstyles.org/styles/118321/trello-full-width-labels-with-text) que agranda las etiquetas y muestra su valor textual en las cartas de trello

Para ello no tenemos más que pulsar sobre el icono de “Install with Stylish”

Y comprobar el cambio accediendo a la página de trello.

Podeis encontrar estilos de cualquier web, ¿a que estaría bien ver tu periódico favorito sin banners de publicidad?, búscalo en userstyles.org


3.2. Creando nuestros propios estilos.

Para crear un estilo propio no tenemos más que pulsar sobre la opción “Gestionar estilos instalados”, se mostrará una interfaz como esta:

Desde aquí podemos escribir un nuevo estilo e indicar a que sitio se aplicará.

Volviendo a trello a continuación os muestro un par de estilos con los que hemos modificado la visualización de trello para que se muestren las listas en dos columnas

El código CSS es este

@namespace url(http://www.w3.org/1999/xhtml);
.list-wrapper {
    width: 640px;
}
.list-card {
    max-width: 300px;
    min-height: 100px;
    height: 120px;
    float: left;
    min-width: 300px;
    margin-right: 0.5em;
    overflow: auto;
}

Y con el siguiente estilo podéis añadir un fondo de pantalla a vuestros tableros

El código CSS es este

@namespace url(http://www.w3.org/1999/xhtml);
body{
    background-image: url(https://www.autentia.com/wp-content/uploads/2014/03/bandera_Autentia-09-1500x1050.png);
  }

Solo en la versión de pago de trello se pueden añadir fondos de pantalla a su interfaz, con este estilo podemos hacerlo sin más.

Este es el resultado final de todos los estilos aplicados

El icono de Stylish muestra el número de estilos aplicados en el site

Quién piense que añadir un fondo de pantalla a trello es una soberana soplagaitéz, no puedo estar más en desacuerdo con él, el objetivo de un tablero es la gestión visual del estado de las tareas, a medida que vamos trabajando en las mismas deberían ir moviéndose de izquierda a derecha, ¿pensais que podrían incentivar al equipo imágenes sugerentes en la izquierda del fondo de pantalla?, ¿por qué?, ahí lo dejo.


4. Referencias.


5. Conclusiones.

Hemos conseguido personalizar nuestro panel de trello y dentro de un ciclo de entrega podemos, de un solo vistazo, ver cuánto hemos avanzado y, con la combinación de colores de las etiquetas, ver a qué tipo de tareas nos hemos dedicado.

También podemos reportar con un única captura de pantalla, el estado de avance y las tareas pendientes más prioritarias del equipo en trello, mientras atlassian siga manteniendo una versión gratuita del mismo.

Las posibilidades son infinitas: el poder está en tus manos y tu imaginación.

A drisfrutarlo!.

Un saludo.

Jose

Introducción teórica al formato pkpass

$
0
0

En este post os voy a hablar de qué es el formato pkpass, para qué se usa, sus características más llamativas y qué ventajas tiene frente a otros formatos más tradicionales.


0. Índice de contenidos.



1. ¿Qué os voy a contar en esta entrada?

Hace un tiempo, en el proyecto en que trabajo, tuve que encargarme de una historia de usuario relacionada con el cambio de idiomas de ficheros pkpass. Después de haberme peleado con ello vi que había cosas muy curiosas y que no es fácil encontrar información al respecto. Así que he decidido contaros algunas de ellas para que no perdáis tanto tiempo como yo en buscarlo. Hay puntos en los que no he podido entrar en mucha profundidad, pero espero poder resolver las principales dudas que os surgirán al empezar a trabajar.



2. Empecemos viendo qué es pkpass

Con el lanzamiento de iOS 6 en 2012, Apple anunció una aplicación que serviría como cartera virtual para almacenar todo tipo de tarjetas, cupones y tickets: Passbook. El usuario podría tener todos estos archivos (que se llaman pases) en un mismo lugar. Además la inclusión de notificaciones en el dispositivo cuando un pase es importante en una fecha o localización también es una ventaja frente al papel tradicional. Con la llegada de iOS 9, Passbook pasaría a llamarse Wallet, pero lo que nos interesa a nosotros va por otro camino.

El formato abierto (aunque es Apple quien lo creó y lo implementa de forma nativa en sus dispositivos) que unifica los distintos tipos de pases es pkpass. Si los abrimos con una aplicación especializada lo que veremos será una especie de ticket con dos caras: frontal y posterior. Aquí tenéis como ejemplo uno de los bonos culturales que ofrece la Junta de Extremadura para poneros en contexto.

Visualización del fichero pkpass de los bonos culturales

Pero, ¿de qué nos puede servir si hasta ahora el PDF ha sido una solución más que buena? Pkpass es un tipo de archivo que se crea dinámicamente al abrirse. Por ello se adapta mucho mejor a las pantallas de los dispositivos móviles e incluso a los cambios idiomáticos (esto lo veremos más adelante). Además están firmados usando un certificado electrónico, por lo que aporta mayor seguridad sobre su origen de la que tendríamos normalmente con un PDF. Otra posibilidad muy interesante es la de cambiar un pase en el servidor y mandar una notificación push a los dispositivos móviles donde esté instalado para que se conecten y lo actualicen.



3. ¿Cómo está estructurado?

Cuando empecé creía que un archivo pkpass sería algo similar al PDF si lo abríamos, pero no es así. En realidad se trata, en esencia, de un zip. De hecho podéis descomprimir uno para ver su estructura con cualquier herramienta de compresión que tengáis instalada (seguramente tendréis que cambiarle la extensión antes, eso sí).

Dentro de la carpeta que obtenemos al descomprimir podremos encontrar los siguientes objetos:

  • background.png: imagen que sirve de fondo en la cara frontal.
  • footer.png: imagen que se muestra debajo del QR o del código de barras en la cara frontal.
  • icon.png: icono que aparece en las notificaciones y cuando se recibe el fichero por e-mail.
  • logo.png: imagen que aparece en la esquina superior izquierda.
  • manifest.json: JSON que contiene las rutas de todos los archivos que conforman el pkpass con su hash SHA-1 correspondiente. Los únicos que no aparecen listados son el propio manifest.json y el signature.
  • pass.json: JSON donde se define todo el contenido del pkpass, luego veremos qué contiene.
  • signature: manifest.json cifrado con la clave pública.
  • strip.png: imagen que se muestra detrás de los campos primarios en la cara frontal.
  • thumbnail.png: imagen adicional que podemos mostrar en el frontal.
  • Carpetas de personalización regional: en ella tendremos los cambios que contemplamos para los strings y las imágenes cuando abrimos el fichero con otra configuración regional.

Si descomprimimos el fichero pkpass del bono cultural que acabamos de ver arriba, esto es lo que obtenemos:

Estructura interna de un archivo pkpass

Ya veis que no todos los archivos que hemos puesto antes son obligatorios. Además podemos tener dos versiones de cada una de las imágenes que hemos mencionado antes. Al cargarse usando el mismo estándar que los componentes UIImage en iOS, podemos tener las imágenes de mayor resolución añadiendo @2x al final del nombre del fichero.



4. ¿Qué contiene el pass.json?

Vale, sabemos que el contenido del pkpass se define en el pass.json en forma de un diccionario de pares clave-valor. Pero, ¿qué es exactamente lo que tiene dentro y cómo se estructura? Pues se trata de un diccionario con tres niveles de claves, para permitir anidar datos, y que están clasificadas de esta forma:

  • Claves de nivel superior (top-level keys): hacen referencia a la información directa sobre el pkpass.
  • Claves de nivel inferior (lower-level keys): identifican a los elementos que sirven para definir objetos del nivel superior cuando un tipo primitivo no es suficiente.
  • Claves de campos del diccionario (field dictionary keys): definen las propiedades de los objetos del nivel inferior. Como son las últimas claves, todos sus valores serán siempre tipos primitivos.

Os dejo parte del pass.json del bono cultural para que tengáis una referencia un poco más visual.

Fragmento de un pass.json

En este ejemplo las claves formatVersion, barcode y coupon son top-level; message, format, messageEncoding, headerFields, primaryFields y backFields son lower-level, y las repetidas key, label y value son campos de diccionario.

Quiero que os paréis un segundo en el objeto coupon, que es el objeto principal del JSON. Dependiendo del tipo de pase que estamos generando tendrá un nombre u otro (boardingPass, coupon, eventTicket, storeCard o generic), pero todos permiten definir en forma de listas de objetos los campos que se mostrarán. En concreto se utilizan los headerFields, primaryFields, secondaryFields y auxiliaryFields para crear la cara frontal; mientras que para la cara posterior tenemos los backFields. Es importante tener esta estructura de campos en mente cuando queremos crear un pkpass.

Conjuntos de claves usados para cada cara

Si queréis saber más acerca las claves, lo mejor es que echéis un vistazo a la documentación oficial de Apple. Ahí encontraréis todas, para qué se utilizan, el tipo de valor que se le asigna y si son o no obligatorias.



5. Configuración regional

Y por fin hemos llegado al motivo de haber escrito esta entrada. Una de las características que he encontrado más interesantes es que pkpass permite crear un único documento que incluya la información de todos los idiomas a los que queremos traducir el pass. Frente a los formatos más tradicionales aporta mucha flexibilidad, pues normalmente tendríamos que tener una copia del documento por cada idioma al que lo traduzcamos.

Los pkpass más sencillos, como el que hemos visto arriba, asocian un valor único a cada clave que usan. Ahora bien, ¿por qué deberíamos contentarnos con poner el literal directamente en ese json? Es muy común que nuestras aplicaciones estén internacionalizadas y acepten varios idiomas. Parece lógico que intentemos aplicarlo también a los datos que compartimos, y esto es exactamente lo que se ha hecho con pkpass.

A grandes rasgos funciona exactamente igual que una aplicación típica de iOS o Mac (al fin y al cabo fue Apple quien definió el formato). En lugar de introducir todos los literales directamente vamos a tener un directorio asociado a cada idioma para el que tenemos traducciones (con el nombre codigo_idioma.lproj), dentro del cual habrá un fichero pass.strings. En estos ficheros tenemos una especie de diccionario en el que asociamos una clave que para nosotros tiene un significado con su traducción en el lenguaje que corresponda. Cuando escribimos el pass.json no pondremos los literales directamente, sino la clave que hemos definido en nuestros pass.strings. Al abrir el pkpass se cargarán automáticamente los valores del idioma que deba y se le mostrará traducido al usuario de forma completamente transparente para él.

Aquí os pongo un pequeño ejemplo de cómo aplicar esto para traducir las etiquetas de un pase en español e inglés.

Ejemplo de configuración regional

Pero esto no es todo. Dentro de las carpetas de localización también es posible incluir imágenes que sustituirán a las que hay definidas por defecto. Simplemente debemos darles el mismo nombre que las originales.

Aquí os dejo un enlace a la documentación oficial de cómo Apple internacionaliza sus aplicaciones, pues es exactamente el mismo principio. Pero a modo de explicación rápida os diré que al abrir el archivo mira la configuración de región del sistema que lo abre (ordenador, móvil, iPod…) y busca entre los idiomas para los que tengamos traducciones el que ofrezca una mejor correspondencia.



6. Notificaciones en la pantalla de bloqueo

La última de las características que voy a comentar con mayor profundidad es la de poder lanzar una notificación para que aparezca en la pantalla de bloqueo del dispositivo cuando se han cumplido una serie de condiciones. En una entrada de cine, por ejemplo, podríamos configurarla para que se active al acercarnos a unos metros del cine de forma que la tengamos ya preparada en la pantalla cuando la necesitemos para entrar.

Para lograr esto utilizamos las claves de relevancia (relevance keys), que pueden ser de tres tipos y servir para definir bien localizaciones o fechas:

  • beacons: podemos especificar un conjunto de lugares mediante los beacons que haya en ellos. Cuando nos hayamos acercado lo suficiente como para captar su señal, el pkpass será válido y se mostrará en la pantalla de bloqueo.
  • locations: igual que antes, nos sirve para definir un conjunto de lugares, pero esta vez lo haremos en base a sus coordenadas (latitud, longitud y altitud). Podemos añadir también la clave maxDistance para especificar un radio alrededor de estos puntos en los que saltará la notificación.
  • relevantDate: en este caso nos permite indicar una fecha y hora en formato W3C (obligatorio al menos hasta los segundos) a la que deberá aparecernos la notificación.

Os dejo aquí también un pequeño ejemplo, por si verlo en código os ayuda a entenderlo mejor.

Ejemplo de cómo usar las claves de relevancia

7. Lo que no debemos olvidar al trabajar con archivos pkpass

Por último quería tener un apartado en el que agrupar los pequeños detalles que no encajaban del todo en los anteriores y que no eran lo suficientemente extensos (o no he entrado demasiado en profundidad en ellos). Pero como han sido un pequeño dolor de cabeza no quería terminar sin que os sonaran.


7.1. Cabeceras para devolver y pedir

Si queremos que un servicio devuelva un fichero en formato pkpass debemos especificar en la cabecera de la respuesta HTTP que el Media Type es application/vnd.apple.pkpass. En el siguiente trozo de código podéis ver cómo se hace en Java.

final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType("application/vnd.apple.pkpass"));
headers.setContentLength(myFile.length);
final String filename = "myFile.pkpass";
headers.setContentDispositionFormData(filename, filename);

return new ResponseEntity(boardingPass, headers, HttpStatus.OK);

Eso sí, si queremos que todo funcione bien no podemos olvidar indicar el mismo tipo de archivo en la cabecera Accept de nuestra petición HTTP. Si no, lo único que vamos a obtener será un archivo que no seremos capaces de abrir, o como mucho un error que nos avise de esto.


7.2. Qué ocurre si no tenemos un certificado o no es válido

Recordad que originalmente los ficheros pkpass están firmados con un certificado creado por Apple. Si por cualquier motivo el certificado no existe, ha caducado o simplemente no es válido tendremos problemas para abrirlo. En MacOS, hasta Sierra, se abrirá sin problemas pero el código QR aparecerá inactivo y sobre él habrá un texto indicando que algo ha ido mal. Si lo abrimos con Sierra o cualquier versión de iOS (tanto en iPhone como en iPod) simplemente no se abrirá. Si queréis algo más de información al respecto lo mejor es que abráis la consola del sistema y busquéis el error.

Ahora bien, resulta que las aplicaciones para Android solamente miran el contenido en sí de los pases y no comprueban si la firma que hay en el signature es correcta. Esto se traduce en que aunque el certificado sea inválido por cualquier motivo, nuestro fichero se abrirá normalmente sin que se le muestre nada al usuario.


7.3. Un formato para confundirnos a todos: espass

Mientras preparaba esta entrada tuve curiosidad por ver más ejemplos de pkpass y me puse a buscar los pases que tenía guardados. Lo que encontré fue otro formato muy parecido al pkpass, llamado espass. Se trata de un estándar aún en construcción para la generación de pases inteligentes. Lo que se pretende con ellos es mejorar la venta de tickets (cupones, tarjetas de embarque y pases similares), especialmente en materias de seguridad de acceso y contra la reventa de tickets.

Tienen la misma base estructural que los pkpass, por lo que si lo descomprimimos encontraremos unos cuantos archivos de recursos para las imágenes y un main.json en el que se especifica todo el contenido. Si bien el json es mucho más simple en este caso y no cuenta con una estructura de claves tan bien definida como la de los pkpass.

No obstante, aunque en estructura y apariencia sean similares, en un principio no parecen estar pensados para lo mismo (habrá que ver cómo evoluciona espass). Precisamente a esto se debe la falta de complejidad en la definición de los datos y la ausencia de la mayoría de las características importantes que hemos visto aquí. Lo único que quería era que supieseis que hay un estándar de nombre y resultado visual similar y que puede abrirse con las mismas aplicaciones que el pkpass, no plantearlo como una alternativa real.

Desde su web muy parecido al pkpass, llamado podéis descargar un par de ejemplos, pero es posible que lo único que podáis hacer sea descomprimirlo para verlo por dentro. Para visualizarlo parece que la única opción es usar la aplicación PassAndroid (no es necesario que digamos para qué plataforma), que es la que tienen puesta en la misma web. Llevado por la curiosidad, he intentado abrirlos con mi Mac y con el iPhone de un compañero y ninguno de los dos ha reconocido el formato.



8. Conclusiones

Hemos visto que pkpass es un formato muy flexible y que aporta muchas ventajas sobre los tipos más tradicionales como el PDF. Para compartir cualquiera de los tipos de pases que soporta es una opción idónea, y si lo usamos bien nos ayudará mucho, tanto como desarrolladores como de cara al negocio para el que se vayan a implementar.

Pero no os llevéis la impresión equivocada, pkpass no va a ser el sustituto del PDF. Se trata más bien de una alternativa para ofrecer nuestra información al usuario. No olvidemos que está muy limitado en la estructura de los documentos que podemos generar, mientras que PDF permite cualquier cosa que se nos ocurra. Además, aunque en dispositivos de Apple se haga de manera nativa, los usuarios con un teléfono Android deben tener un mínimo conocimiento de la existencia de este formato como para querer descargarse una aplicación que permita abrirlo. Como desarrolladores, creo que la mejor idea sería implementar las dos aproximaciones y dejar que sea el propio usuario el que elija qué formato prefiere.



9. Referencias

Comentado el libro Lean UX de Jeff Ghothelf y Josh Seiden

$
0
0

En este caso voy a mostrar mi asombro porque no he visto las conferencias de agilismo o de UX hablando a todas horas de este libro, y la verdad es que me parece excelente.

Le da linealidad a la definición de producto con Design Thinking, la experiencia de usuario (UX) y las metodología ágiles, que es básicamente a lo que dedico últimamente el tiempo personalmente en clientes.

Os voy a contar los elementos que más me han llamado la atención. Advertencia, son solamente cortes de una obra más amplia que creo que tendríais que leer.

Hay una idea que manda sobre todo esto: si el tamaño de lote es grande, nos estamos equivocando. Un proyecto definido durante meses para ejecutarlo durante años es una tamaño de lote inmenso.

Se basa en unos principios:

  • Equipos multi-funcionales.
  • Equipos pequeños, dedicados y comunicados.
  • Proceso igual a resultados, no entregas de documentación.
  • Equipos centrados en problemas, no en grupos de funciones.
  • Eliminación del despilfarro.
  • Lotes pequeños.
  • Descubrimiento continuo, comprometiendo a los clientes en los procesos de diseño y desarrollo mediante actividades programadas regulares.
  • GOBB (Getting Out Of the Building): los debates sobre las necesidades del usuario no tienen que cerrarse en el mismo lugar en el que se celebran.
  • Entendimiento común que se construye poco a poco.
  • Antimodelos: estrellas, gurus y ninjas. Los egos pueden romper la cohesión del grupo.
  • Exteriorización del trabajo haciendo visible con paneles, pizarras, etc.
  • Hacer en lugar de analizar. Vale más crear la primera versión del producto que discutir sobre él.
  • Aprendizaje en lugar de crecimiento, es difícil concebir una idea y hacerla crecer a la vez.
  • Permiso para equivocarse porque la mayoría de las ideas no funcionan.
  • Escapar de los negocios basados en entregables (documentos).

Sugiere que no se define los proyectos con requisitos sino con suposiciones priorizadas a partir de problemas, que se detallan en hipótesis. Los resultados ayudan a validar hipótesis. Los personales son los modelos teóricos de personas. Funciones son los cambios de productos o mejoras.

La formula de una hipótesis es: Creemos que [declaración cierta]

Sabremos que lo hemos hecho [bien/mal] cuando contemos con el siguiente feedback del mercado: [feedback] o cambio de indicador [kpi].

El diseño debe ser colaborativo y propone la puesta en marcha del estudio de diseño. También el uso de guías de diseño como patrones.

Al crear un Producto Mínimo Viable hay que pensar en tres preguntas:

  • ¿Existe la necesidad de la solución que estoy diseñando?
  • ¿Existe valor en la solución y las funciones?
  • ¿Mi solución es estable?

A través de prototipos de distintas calidades se puede definir un PMV. Hay otras opciones que no requieren un prototipo pero que dan respuesta a las pregunta:

  • ¿Qué trato de aprender?
  • ¿Cuáles son las principales variables que necesito?
  • ¿Cuál es el mejor modo de obtener esas variables?

Habla de que primero es importante la velocidad y luego la estética, sobre todo en prototipos tempranos.

Propone para el análisis de mercado una investigación continua, por partes planificadas en días concretos, en cada Sprint de producto y colaborativa, realizada por el equipo, para no esperar el entregable de una agencia externa.

En la ejecución con metodologías ágiles se cuestiona que el equipo de diseño vaya un Spring por delante del de construcción, con un entregable de por medio. Esto puede ser un modelo de transición hasta una integración mayor.

Propone unificar varios Sprints en un tema en el que tener unas sesiones previas de brainstorming, donde se produce co-creación de todos los miembros. Con un calendario continuo de validación con usuarios ponemos voz al cliente.

Uno de los problemas que trata de evitar es limitar el tiempo de los diseñadores para ser creativos, cosa que tal vez no pueden ser encorchetados en ciclos de 2 semanas.

Habla de los cambios que se requieren en la organización para cambiar el modelo (entre otros): Cambiar documentación por resultados, roles por capacidades, acabar con el diseño up front, cambiar la cultura de las agencias, ser realistas sobre el entorno.

Reclama la responsabilidad de la dirección para aprender y conceder el grado de libertad para que los modelos se pongan en marcha.

Bueno, ya no os cuento más pero creo que es una obra francamente buena y creo que sería buena idea regalársela a muchos clientes.

Viewing all 996 articles
Browse latest View live