Introducción a Spring Boot: Creación de un microservicio - Parte I

Estamos en esa primera fase en el desarrollo de un nuevo proyecto y tenemos que elegir el modelo de arquitectura que utilizaremos durante su desarrollo. Tenemos en principio dos opciones: el archiconocido modelo monolítico con el que nos sentimos familiarizados o el (ya no tan nuevo) modelo de microservicios.

En ese momento en la mente de cualquier desarrollador surge la misma pregunta ¿Por qué debería dejar el modelo monolítico con el que me siento tan cómodo y pasarme a la arquitectura de microservicios?

La respuesta a esta pregunta es sencilla: Porque aunque al principio cueste a la larga verás que los proyectos creados con este modelo son más sencillos de implementar y de mantener en entornos productivos al permitirnos escalar el servicio o separar las responsabilidades de cada microservicio. Además el software que creemos será más resiliente que el creado con el modelo monolítico.

Frente al tradicional modelo monolítico donde todos los servicios que ofrece el sistema son proporcionados en un único “paquete” gigante, en el modelo de microservicios ese “paquete” se divide en múltiples “paquetes” más pequeñitos independientes entre sí pero que pueden comunicarse entre ellos mediante API.

Eso permite a los microservicios:

  • Ejecutarse de manera autónoma.
  • Comunicarse entre sí.
  • Centrarse en una única área de negocio.
  • Desplegarse y redimensionarse sin afectar a los demás microservicios.
  • Estar escritos en distintos lenguajes de programación ya que se comunican a través de API.

Todas estas características otorgan a los microservicios las siguientes cualidades:

  • Permiten una gestión ágil del ciclo de desarrollo y operación.
  • Evitan que el mantenimiento de las aplicaciones sea costoso debido a la poca reutilización del código.
  • Un fallo queda localizado en un microservicio y no tiene por qué afectar a todo el sistema.
  • Facilitan los despliegues ya que permiten actualizar los microservicios individualmente sin necesidad de dejar inoperativo todo el sistema. Además, al ser componentes pequeños su tiempo de arranque es relativamente corto ayudando a agilizar los rollouts de versiones.
  • Utilizando docker y desplegando el microservicio en gestores de contenedores como Openshift es posible hacer un reescalado software en tiempo de ejecución y sin parar el sistema.

¿Cómo puedo empezar y crear mi primer microservicio? Dado que un microservicio es independiente del lenguaje de programación que utilices, existen multitud de formas de crearlos. Nosotros, en este manual, utilizaremos Spring Boot como framework de desarrollo y Java 11 como lenguaje de programación.

Nuestro primer microservicio publicará un servicio REST que mostrará la información sobre vuelos y pasajeros de la aerolínea SpringAirline. NOTA: Si quieres saltarte toda la explicación que viene a continuación y pasar directamente al código puedes descargártelo de github en el siguiente enlace.

Utilizaremos la última versión estable de Spring Boot que existe en el momento de escribir este tutorial.

Una vez descargado el proyecto que nos ha creado Spring Initializr e importado en Eclipse o el IDE que utilices habitualmente vemos que tenemos una estructura de ficheros como la que te muestro a continuación:

 

Si abrimos el fichero pom.xml vemos que Spring Initializr ha determinado que nuestro proyecto tiene que tener como padre:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.0</version>
    <relativePath/> 
</parent>

Normalmente estábamos acostumbrados a importar una a una como dependencias en nuestro proyecto maven todas las librerías que íbamos a necesitar durante el desarrollo. Spring Boot nos facilita ese trabajo con el concepto de starter. Cada starter incluye automáticamente una serie de librerías que vamos a necesitar para desarrollar una funcionalidad específica proporcionada por Spring Boot. spring-boot-starter-parent identifica nuestro proyecto como un microservicio de tipo Spring Boot y permite gestionar de forma centralizada las distintas versiones de dependencias transitivas que se traen cada uno de los Iromerob adiconales de Spring Boot que puedan incluirse en el pom. Nosotros vamos a incluir un starter más:

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

 

Este starter nos permitirá importar todas las librerías necesarias para crear una api REST.

Por último vamos a integrar nuestro microservicio con Swagger para que documente la api que vamos a crear con OpenAPI 3.0 y de esta forma podamos disponer de un entorno gráfico para realizar las pruebas. Para ello vamos a incluir la siguiente dependencia en el pom:

<dependency>
  <groupId>org.springdoc</groupId>
  <artifactId>springdoc-openapi-ui</artifactId>
  <version>1.5.2</version>
</dependency>

Spring Initializr nos ha creado también la clase SpringAirlineApplication. Esta clase es la clase principal de nuestro proyecto, la que vamos a utilizar para arrancar el microservicio cuando esté terminado.

@SpringBootApplication
public class SpringAirlineApplication {

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

}

 

Esta clase está anotada con @SpringBootApplication. Esta anotación agrupa las anotaciones @SpringBootConfiguration, @EnableAutoConfiguration y @ComponentScan y básicamente le indica a Spring dónde y cómo puede encontrar la configuración para levantar correctamente el microservicio.

Por último Spring Initializr ha creado también el fichero application.properties. Este fichero puede ser sustituido sin ningún problema por application.yml y es el que almacena la configuración de las propiedades del proyecto. Inicialmente viene vacío pero nosotros vamos a modificarlo más adelante. 

Como decíamos, Spring Initializr crea un proyecto Spring Boot totalmente funcional aunque vacío. Si quisiéramos podríamos arrancarlo ya y no debería mostrarnos ningún mensaje de error. Para ello debemos arrancarlo como un proyecto java estándar indicando que la clase principal es SpringAirlineApplication.

 

Tal y como se observa en la imagen anterior, toda aplicación Spring Boot tiene un tomcat embebido que se arranca automáticamente. Este tomcat por defecto se arranca en el puerto 8080 y con el context path vacío.

Nuestra intención al crear este microservicio era crear una api REST que publique información sobre los vuelos y pasajeros de la aerolínea SpringAirline. Como el código completo puede descargarse de github nos vamos a limitar a explicar los detalles más importantes de esta api REST.

En el paquete com.everis.dar.springairline.model hemos creado el modelo de información de nuestra aerolínea. De todas estas clases la más importante es la clase AircraftFleet que almacena un mapa con todos los vuelos de la compañía y sus pasajeros. Esta clase la vamos a declarar como bean ya que contendrá toda la información de nuestra aerolínea. Para ello creamos en el paquete com.everis.dar.springairline.configuration la clase springAirlineconfiguration y la anotamos con @Configuration. Esto indicará a Spring que esta clase en concreto contiene definiciones de beans.

@Configuration
public class SpringAirlineConfiguration {
	
	@Bean
	public AircraftFleet aircraftFleet() {
		return new AircraftFleet();
	}
}

Como podemos ver para declarar un bean del tipo AircarftFleet simplemente tenemos que crearlo haciendo uso de su constructor por defecto y anotar el método con @Bean.

En el paquete com.everis.dar.springairline.rest tenemos la clase FlightsRestService. Esta clase es la que publica la api REST que informará sobre los vuelos y pasajeros de la aerolínea.

Para que Spring sepa que esta clase se corresponde a una api REST y la publique correctamente al exterior al inicio le ponemos la anotación @RestController. Y cómo necesitamos que tenga acceso a toda la información sobre los vuelos le inyectamos el bean del tipo AircraftFleet con la anotación @Autowired. @Tag nos permite crear el tag que swagger utilizará para agrupar todas las operaciones de la api REST en su interfaz gráfica.

@RestController
@Tag(name="FlightsRestService", description="The Flights api")
public class FlightsRestService {

@Autowired
private AircraftFleet aircraftFleet;

Nuestra api va a ser lo más completa posible y para ello vamos a publicar operaciones de tipo GET, POST, PUT y DELETE. De esta forma veremos los distintos tipos de operaciones y cómo deben declararse con Spring Boot.

Operación de tipo GET

Dentro de nuestra aerolínea queremos que nuestros usuarios puedan obtener información de un vuelo en concreto. Como esta información es pública vamos a publicarla con un método de tipo GET como se muestra a continuación:

@Operation(summary = "Info", description = "Flight info", tags = { "FlightsRestService" })
@GetMapping("/info/{id}")
public @ResponseBody String getfligthInfo( 
@Parameter(description="Flight id", required = true, example="1", in = ParameterIn.PATH) @PathVariable(value = "id") Long id) {
		…
}

La anotación @GetMapping le indica a Spring Boot que esta operación debe publicarse como GET. Junto con esta anotación indicamos la url relativa que tendrá esta operación dentro de la api REST publicada por el microservicio. La anotación @PathVariable nos permite pasar como parámetro de entrada la variable introducida dentro de la url.

Para configurar Swagger utilizamos la anotación @Operation que describe al usuario la operación y @Parameter para describir cada parámetro de entrada.

Operación de tipo POST

Entre la información que vamos a publicar vamos a indicar a través del nif de un pasajero a qué vuelo está asignado. En este caso como mandaremos en la llamada información sensible utilizaremos un método de tipo POST:

@Operation(summary = "Passenger info", description = "Get the passengers info through their id", tags = { "FlightsRestService" })
@PostMapping("/passenger")
public @ResponseBody String postPassenger(
	@Parameter(description="Passenger id", required = true, example="23574660M", in = ParameterIn.QUERY ) @RequestParam(name = "id") String id) {

	…..
}

 

La anotación @PostMapping le indica a Spring Boot que esta operación debe publicarse como POST. Junto con esta anotación indicamos la url relativa que tendrá esta operación dentro de la api REST publicada por el microservicio. En este caso la anotación @RequestParam es la que nos permite recoger como parámetro de entrada el nif del pasajero que queremos buscar.

Operación de tipo PUT

Pensemos que una persona ha comprado un billete para un vuelo en concreto, necesitaríamos incluirla en la lista de pasajeros por lo tanto crearemos una operación de tipo PUT para ello:

@Operation(summary = "Add passenger", description = "Add a passenger in an exists flight", tags = { "FlightsRestService" })
@PutMapping(path = "/addpassenger")
public String putPassenger(
@Parameter(description="Flight id", required = true, example="1", in = ParameterIn.QUERY ) @RequestParam(name = "fid") Long fid, 
@Parameter(description="Passenger id", required = true, example="27089437Z", in = ParameterIn.QUERY ) @RequestParam(name = "pid") String pid,
@Parameter(description="Passenger name", required = true, example="Roberto", in = ParameterIn.QUERY ) @RequestParam(name = "name") String name, 
@Parameter(description="Passenger surname", required = true, example="Maldomingo Fasch", in = ParameterIn.QUERY ) @RequestParam(name = "surname") String surname) {
…..

}

La anotación @PutMapping le indica a Spring Boot que esta operación debe publicarse como PUT. Junto con esta anotación indicamos la url relativa que tendrá esta operación dentro de la api REST publicada por el microservicio. La anotación @RequestParam es la que nos permite recoger como parámetro de entrada los datos del pasajero y el vuelo.

Operación de tipo DELETE

Finalmente tenemos una operación de tipo DELETE que nos permitirá eliminar un vuelo completo si este se ha visto cancelado. Como esta operación no envía información sensible la pasaremos dentro de la url. Si no fuese así el identificador del vuelo podría mandarse como @RequestParam igual que se ha hecho en la operación de tipo POST y de tipo PUT

@Operation(summary = "Delete flight", description = "Delete an exists flight", tags = { "FlightsRestService" })
@DeleteMapping(value = "/delflight/{id}")
public String deleteFlight(
	@Parameter(description="Flight id", required = true, example="2", in = ParameterIn.PATH ) @PathVariable Long id) {
….
}

La anotación @DeleteMapping le indica a Spring Boot que esta operación debe publicarse como DELETE. Junto con esta anotación indicamos la url relativa que tendrá esta operación dentro de la api REST publicada por el microservicio.

Con esto ya tenemos finalizada nuestra api REST. Por último solo nos queda configurarla para que se publique en el puerto que queramos y bajo un context path concreto.

Esta configuración se realiza en el fichero application.properties y es la siguiente:

server.servlet.contextPath=/airline
server.port=8085

Estas propiedades las ofrece por defecto spring boot para que configuremos nuestro microservicio y solo hay que definirla en este fichero para que las tenga en cuenta durante el arranque y la ejecución. Existen muchas más propiedades como esta, puedes consultar el listado correcto en el siguiente enlace.

Finalizada toda la implementación va siendo hora de que arranquemos nuestro microservicio:

Una vez arrancado accederemos a la url donde se ha publicado la api con swagger http://localhost:8085/airline/swagger-ui/index.html y verificamos que Explore tenga como valor /airline/v3/api-docs

Como hemos configurado valores por defecto para cada llamada la api puede probarse utilizando estos valores de prueba.

NOTA: Para utilizar valores lo más reales posibles y no violar la LOPD los datos se han extraido del banco de datos de prueba de la TGSS que puede descargarse en la siguiente url.

 

 

Guía de Posibilidades Profesionales en el Ecosistema de Java