Como crear un microservicio o servicio web REST con Spring Boot (Parte 2)

Continuando con el microservicio construido en la parte 1 de este post, en esta segunda parte agregaremos el acceso a datos mediante consultas y actualizaciones a una base de datos relacional. En la parte final lo enriqueceremos con algunas de las características que hacen tan poderoso a Spring Framework y corregiremos fallas de diseño que ya tiene nuestro servicio.

Como recordarás, en la primera parte de este post dividimos la aplicación en las tres capas lógicas del siguiente diagrama y construimos la capa de interfaz.

En esta entrega construiremos la capa de lógica de negocio y la de  de acceso a datos.

Creando el modelo de datos

Spring Boot nos ofrece múltiples formas de almacenar nuestros datos mediante un gran número de integraciones con repositorios populares como bases de datos no relacionales (MongoDB, Cassandra, etc), directorios LDAP y bases de datos relacionales tradicionales (MySQL, PostgreSQL, Oracle, etc) utilizando JDBC y la especificación JPA.

Si seguiste la primera parte de este post, ya tienes incluidas las librerías de JPA, Hibernate y el driver JDBC para la base de datos (relacional) H2 en tu archivo pom.xml, lo que resta es crear la entidad que representará a una tabla de la base de datos. Para esto utilizaremos únicamente anotaciones estándar de JPA  y usaremos nuestra clase Contact para convertirla en un entidad, así:

// (1)
import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

// (2)
@Entity 
public class Contact implements Serializable {

	// (3) 
	private static final long serialVersionUID = 4894729030347835498L;

	// (4)
	@Id
	@GeneratedValue
	private Long id;
	private String firstName;
	private String lastName;
	private String phoneNumber;
	private String email;
	...
  1. Imports nuevos para agregar anotaciones de JPA
  2. Indicamos que esta clase representa a una tabla de base de datos con el mismo nombre. Para cambiar el nombre de la tabla podemos usar la anotación @Table
  3. Es buena práctica hacer las entidades serializables. Algunos proveedores de persistencia (Hibernate, EclipseLink, etc.) pueden presentar excepciones si en algunos casos particulares la entidad no es serializable.
  4. La anotación @Id indica que este atributo será la clave primaria y @GeneratedValue indica la forma en que se generarán los valores de la clave primaria. En este caso se usará el valor por defecto que hace que se use el valor que genere la base de datos. El comportamiento se puede cambiar por ejemplo, a una secuencia de Oracle, usando el parámetro «strategy»

Capa de acceso a datos

Spring Data JPA, que ya está entre nuestras dependencias, facilita mucho la creación de clases que guarden y recuperen datos de la base de datos usando JPA. Tradicionalmente debíamos crear manualmente la conexión a base de datos (datasource), configurar entity manager/unidad de persistencia y crear todo el código CRUD en una clase auxiliar. Este es un trabajo repetitivo y propenso a errores por lo que Spring Data JPA se encarga de estas configuraciones automáticamente, atendiendo convenciones que podemos cambiar por configuración, de llegar a requerirlo.

La forma más rápida y segura de crear una clase de acceso a datos es usar la intefaz JpaRepository de Spring Data JPA. Para esto, crea una interfaz vacía en el paquete dao que la extienda, así:

package com.sinbugs.contacts.dao;

import org.springframework.data.jpa.repository.JpaRepository;

import com.sinbugs.contacts.dto.Contact;

public interface ContactRepository extends JpaRepository<Contact, Long> {

}

Fíjate que esta interfaz extiende la interfaz JpaRepository. Durante la inicialización de la aplicación, Spring Data busca estas interfaces y crea  clases que las implementan, ofreciendo automáticamente los métodos típicos para Crear, Actualizar o Eliminar (CRUD) una entidad. Los parámetros de esta interfaz son: la entidad a gestionar (Contact) y el tipo de dato de su clave primaria (Long).

Capa de lógica

En este caso esta capa es muy simple pues no requerimos hacer operaciones con reglas de negocio o cálculos complejos y por ahora, sólo vamos a guardar los datos tal como nos llegan. En un servicio más complejo, es aquí en donde debemos dejar todas las operaciones que implementen la lógica de negocio, por ejemplo, consultar servicios externos, hacer validaciones complejas, calcular otros campos, etc., haciendo que nuestro @RestController tenga la menor cantidad de lógica posible.

Crea la clase «ContactService» en el paquete «service» así:

// (1)
@Service
public class ContactService {
	// (2)
	@Autowired
	ContactRepository dao;
	
	// (3)
	public Contact save(Contact contact){
		return dao.saveAndFlush(contact);
	}
}
  1. La anotación o estereotipo «@Service» indica a Spring que cree una instancia de esta clase (bean) que podremos usar en otras instancias
  2. @Autowired es equivalente a @Inject que se usa en ambientes JavaEE, incluso esta última funciona con Spring también. Se utiliza para indicar a Spring que queremos que asigne una instancia de un bean a la variable que tiene la anotación @Autowired
  3. Nuestro método de lógica de negocio. En este caso la lógica es mínima pero es en este tipo de métodos y clases en donde debemos concentrar las operaciones, condiciones, reglas y demás acciones de lógica de negocio de nuestra aplicación

Usando la lógica desde la interfaz

Ya tenemos la lógica de negocio, como la usamos desde nuestra capa de interfaz? Debemos modificar la clase ContactsApi para que haga uso de nuestra clase de lógica de negocio, así:

...
// (1)
@Autowired
ContactService contactService;

// (2)
@RequestMapping(value="/contact", method=RequestMethod.POST)
public Contact updateOrSave(@RequestBody Contact contact){
	// (3)
	return contactService.save(contact);
}
...
  1. Indicamos a Spring que debe inyectar una instancia de nuestra clase de lógica de negocio
  2. Creamos un método REST que mediante una petición HTTP POST en la url http://localhost:8080/contact. Fíjate en la anotación @RequestBody antes de la variable «contact». Esta anotación indica que la variable debe ser creada con los valores que lleguen en el cuerpo de la petición HTTP, para lo cual usaremos una estructura JSON
  3. Invocamos lógica de negocio contenida en nuestra clase ContactService

Para hacer la petición POST podemos utilizar herramientas como SoapUI o Postman. En la siguiente imagen se muestra una petición utilizando Postman (3, en la parte superior) y la respuesta del servicio (4, en la parte inferior):

  1. Url de nuestro servicio web y método POST seleccionado
  2. Encabezado de la petición que indica el tipo de petición (Content-Type: application/json)
  3. Cuerpo de la petición. Es una estructura JSON que representa un contacto. Observa que no se ha indicado el valor del campo Id ya que este campo es generado por la base de datos, según las anotaciones de nuestra entidad
  4. Respuesta del servicio. Observa que el servicio nos retorna la entidad con un valor númerico autogenerado en el campo Id

Advertencia!

Ya tienes un servicio web REST básico funcionando, integrado con una base de datos relacional en memoria. Ha sido muy fácil y rápido construirlo y no es algo muy diferente a lo que puedes encontrar en tutoriales en internet. Lo hemos hecho de este modo para facilitar la comprensión de los conceptos básicos a los principiantes, sin embargo este servicio no está listo para ser usado en una aplicación real porque tiene fallas de diseño que dificultarán su evolución, las pruebas automatizadas y van en contra del bajo acoplamiento que debe tener un buen diseño. Corregiremos estos problemas en nuestro próximo post.

No olvides que puedes encontrar el código del tutorial completo en nuestro repositorio en GitHub

Revisa las otras partes de este post:

« »