Criando um micro-serviço com Spring Boot, Docker e MariaDB na Azure - Parte 1
Tags: tutorial, microservices, springboot, docker
Published: 2023-03-03 02:43:31

> Nota: O código deste tutorial pode ser acessado via https://github.com/obrunogonzaga/Imovel

Você já pensou em criar um micro-serviço escalável e flexível? Se a resposta é sim, você está no lugar certo! Neste guia, vamos criar juntos um micro-serviço usando o poderoso ecossistema de tecnologias Spring Boot, Docker e MariaDB, tudo isso executado na nuvem da plataforma Azure. Acompanhe os passos a seguir e descubra como é fácil criar um aplicativo robusto baseado em micro-serviços.

## Escopo do projeto
O objetivo deste projeto é criar o micro-serviço "Imovel" como parte do sistema "House Flipping", que gerencia as informações dos imóveis disponíveis para compra e venda. O micro-serviço, desenvolvido em Spring Boot e Java 17, se integra com um banco de dados MariaDB e permite aos usuários visualizar, buscar, salvar e criar/editar listas de imóveis para venda. Após ser desenvolvido e testado, o micro-serviço será implantado em um contêiner Docker na Azure, podendo ser integrado a outros micro-serviços e no sistema maior.

## Pré-requisitos
Para seguir este tutorial e criar um micro-serviço de gerenciamento de imóveis usando Spring Boot e Java 17, configurar um banco de dados MariaDB em um contêiner Docker dentro da Azure, e implantar a aplicação em um contêiner Docker na Azure, você precisa ter conhecimentos básicos em programação em Java, desenvolvimento web e API REST, contêineres e sua utilização em desenvolvimento e implantação de aplicativos, SQL e bancos de dados relacionais como o MariaDB, além de familiaridade com o uso de um sistema de controle de versão, como o Git, para gerenciar o código-fonte da aplicação. Além disso, é necessário ter as seguintes ferramentas: Java 17, Maven, Docker, Azure CLI e MariaDB.

## Objetivos
O objetivo desta parte é criar um micro-serviço chamado Imovel utilizando Spring Boot e Maven. Para isso, será necessário criar uma entidade "Imovel" com as características de um imóvel, um repositório para realizar as operações de CRUD na entidade, um serviço para implementar a lógica de negócios relacionada a imóveis e um controlador para expor os serviços REST da aplicação.

## Configurando o banco de dados
Agora, vamos configurar o banco de dados MariaDB. Para isso, você pode criar um container Docker com a imagem oficial do MariaDB. Abra um terminal e execute o seguinte comando:

```bash
docker run -p 3306:3306 --name mariadb 
-e MYSQL_ROOT_PASSWORD=senha -d mariadb
```

Esse comando irá baixar a imagem do MariaDB e criar um container com o nome "mariadb". O parâmetro "-p" indica que a porta 3306 do container será mapeada para a porta 3306 da máquina local. O parâmetro "-e" define a senha do usuário root do banco de dados.

Para verificar se o container foi criado com sucesso, execute o comando:

```bash
docker ps
```

Você deverá ver a linha referente ao container do MariaDB com o status "Up".

## Criando um projeto Spring Boot
Para criar um projeto Spring Boot para gerenciar imóveis, vamos utilizar o Spring Initializr. Acesse o site https://start.spring.io/ e preencha as informações do projeto, como o nome, o tipo de projeto e a versão do Spring Boot. Além disso, adicione as seguintes dependências:
- Spring Web
- Spring Data JPA
- Spring Cloud Azure

Após preencher as informações do projeto, clique em "Generate" para baixar um arquivo ZIP contendo o projeto inicial.

Para gerenciar imóveis, vamos criar uma classe de `Application` que inicializa a aplicação Spring Boot. Por exemplo, crie a classe `ImovelApplication` no pacote `br.com.houseflipping.imoveis`:

```java
package br.com.houseflipping.imoveis;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ImovelApplication {

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

}
```

A anotação `@SpringBootApplication` indica que esta é uma classe de configuração para o Spring Boot. Ela inclui as seguintes anotações:

- `@Configuration`: indica que esta classe é uma classe de configuração Spring.
- `@EnableAutoConfiguration`: permite que o Spring Boot configure automaticamente a aplicação com base nas dependências adicionadas ao projeto.
- `@ComponentScan`: instrui o Spring a procurar e carregar todos os componentes anotados com `@Component` no pacote atual e em seus subpacotes. Note que a classe `ImovelApplication` é definida no pacote `br.com.houseflipping.imoveis`. Isso significa que todas as classes do projeto devem ser definidas dentro do pacote `br.com.houseflipping.imoveis` ou em subpacotes deste.

Por fim, adicione as seguintes dependências ao arquivo pom.xml para incluir as dependências do Spring Web, Spring Data JPA e Spring Cloud Azure:

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

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

    <dependency>
        <groupId>com.microsoft.azure</groupId>
        <artifactId>spring-cloud-azure-appconfiguration-config</artifactId>
        <version>2.4.1</version>
    </dependency>

    <dependency>
        <groupId>com.microsoft.azure</groupId>
        <artifactId>azure-storage</artifactId>
        <version>8.6.6</version>
    </dependency>
    
    <dependency>
        <groupId>org.mariadb.jdbc</groupId>
        <artifactId>mariadb-java-client</artifactId>
        <version>2.7.2</version>
   </dependency>

</dependencies>
```

Com essas dependências adicionadas, você estará pronto para começar a desenvolver o seu projeto para gerenciar imóveis utilizando o Spring Boot e a Azure.

## Criando a entidade Imovel
Agora, vamos criar a entidade `Imovel` com os principais atributos de um imóvel. Crie uma nova classe chamada `Imovel` na pasta `src/main/java` com o seguinte código:

```java
package br.com.houseflipping.entity;

import javax.persistence.*;

@Entity
@Table(name = "imoveis")
public class Imovel {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String tipo;

    private String endereco;

    private Integer numeroQuartos;

    private Integer numeroBanheiros;

    private Double area;

    private Double preco;

    // getters e setters
}
```

Essa classe representa a tabela `imoveis` no banco de dados. As anotações `@Entity` e `@Table` definem a tabela correspondente e a anotação `@Id` define o campo `id` como chave primária com auto-incremento. Os demais atributos correspondem às colunas da tabela.

Os novos atributos incluem:

- `tipo`: o tipo de imóvel (casa ou apartamento, por exemplo)
- `endereco`: o endereço do imóvel
- `numeroQuartos`: o número de quartos do imóvel
- `numeroBanheiros`: o número de banheiros do imóvel
- `area`: a área do imóvel em metros quadrados
- `preco`: o preço do imóvel em reais

Esses atributos representam características físicas e financeiras do imóvel e podem ser utilizados para filtrar e buscar imóveis por diferentes critérios. Vamos adicionar outros atributos, como número de vagas na garagem, área externa, piscina, etc., de acordo com as necessidades do projeto.

## Criando o repositório
O repositório é responsável por manipular as entidades em um banco de dados. No Spring Boot, é possível criar um repositório facilmente utilizando a interface JpaRepository do Spring Data JPA. Essa interface define operações básicas de CRUD (_Create_, _Read_, _Update_, _Delete_) para a entidade correspondente.

Para criar um repositório para a entidade `Imovel`, vamos criar uma nova interface chamada `ImovelRepository`. Essa interface deve estender a interface JpaRepository e informar o tipo da entidade e o tipo da chave primária. No nosso caso, a entidade é `Imovel` e a chave primária é do tipo "Long".

```java
@Repository
public interface ImovelRepository extends JpaRepository<Imovel, Long> {
}
```

A anotação `@Repository` indica que essa interface é um componente Spring e será gerenciada pelo contêiner. Com essa interface, já é possível utilizar as operações básicas de CRUD no banco de dados para a entidade `Imovel`. Por exemplo, para salvar um novo objeto `Imovel`, basta chamar o método "save" do repositório:

```java
Imovel imovel = new Imovel();
imovel.setTipo("Casa");
imovel.setEndereco("Rua dos Bobos, 0");
imovel.setPreco(100000.0);

ImovelRepository imovelRepository = ...; // obter instância do repositório
Imovel imovelSalvo = imovelRepository.save(imovel);
```

O método `save` retorna o objeto `Imovel` salvo no banco de dados, com o ID gerado automaticamente pelo banco (se a estratégia de geração de ID for "_IDENTITY_" ou "_SEQUENCE_").

Além das operações básicas de CRUD, é possível definir outras consultas personalizadas no repositório utilizando o recurso de Query Methods do Spring Data JPA. Por exemplo, para buscar todos os objetos "_Imovel_" cujo tipo seja "_Casa_", basta definir um método na interface com o nome adequado:

```java
@Repository
public interface ImovelRepository extends JpaRepository<Imovel, Long> {
    List<Imovel> findByTipo(String tipo);
}
```

Esse método retorna uma lista de objetos "_Imovel_" cujo atributo `tipo` seja igual ao parâmetro informado. Para utilizá-lo, basta chamar o método `findByTipo` do repositório:

```java
ImovelRepository imovelRepository = ...; // obter instância do repositório
List<Imovel> imoveisCasa = imovelRepository.findByTipo("Casa");
```

Para configurar o banco de dados MariaDB no Spring Boot, você pode adicionar as propriedades de configuração do banco de dados no arquivo application.properties. A seção de configuração do banco de dados pode ficar assim:

```bash
# Configurações do banco de dados
spring.datasource.url=jdbc:mariadb://localhost:3306/imoveis
spring.datasource.username=seu_usuario
spring.datasource.password=sua_senha
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver

# Configurações do Hibernate
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect
```

## Criando o serviço
O serviço é a camada responsável por orquestrar as chamadas ao repositório e implementar regras de negócio específicas. No nosso caso, o serviço será responsável por manipular os objetos `Imovel` e fazer a integração com o banco de dados.

Para criar o serviço, crie uma nova classe chamada `ImovelService` na pasta `src/main/java` com o seguinte código:

```java
@Service
public class ImovelService {

    @Autowired
    private ImovelRepository imovelRepository;

    public Imovel salvar(Imovel imovel) {
        return imovelRepository.save(imovel);
    }

    public List<Imovel> listar() {
        return imovelRepository.findAll();
    }

    public Imovel buscarPorId(Long id) {
        Optional<Imovel> imovel = imovelRepository.findById(id);
        if (imovel.isPresent()) {
            return imovel.get();
        } else {
            throw new RuntimeException("Imóvel não encontrado");
        }
    }

    public void excluir(Long id) {
        imovelRepository.deleteById(id);
    }
}
```

Nesse código, utilizamos a anotação `@Service` para indicar que essa classe é um componente Spring que será gerenciado pelo contêiner. Também utilizamos a anotação `@Autowired` para injetar o repositório `ImovelRepository`.

O método "salvar" recebe um objeto `Imovel` como parâmetro e utiliza o método `save` do repositório para salvar o objeto no banco de dados. O método `listar` utiliza o método `findAll` do repositório para listar todos os objetos `Imovel` no banco de dados.

O método `buscarPorId` recebe um ID como parâmetro e utiliza o método `findById` do repositório para buscar um objeto `Imovel` no banco de dados. Caso o objeto não seja encontrado, lançamos uma exceção personalizada.

O método `excluir` recebe um ID como parâmetro e utiliza o método `deleteById` do repositório para excluir o objeto correspondente no banco de dados.

Com o serviço implementado, agora podemos utilizá-lo no controlador para expor os endpoints REST.

## Criando o controlador
Um controlador é responsável por mapear as requisições HTTP para métodos Java que processam essas requisições e retornam uma resposta. No nosso caso, vamos criar um controlador para expor os serviços REST da nossa aplicação.

Para criar um controlador em Spring Boot, você pode criar uma classe Java anotada com `@RestController`. Essa anotação indica que a classe é um controlador Spring que retorna objetos em formato JSON.

Além disso, você pode usar outras anotações para definir as URLs dos endpoints e os métodos HTTP suportados. Algumas anotações comuns são `@RequestMapping`, `@PostMapping`, `@GetMapping`, `@PutMapping` e `@DeleteMapping`.

Vamos criar um controlador para expor os serviços REST da nossa entidade `Imovel`. Abra uma nova classe chamada `ImovelController` na pasta `src/main/java` com o seguinte código:

```java
@RestController
@RequestMapping("/imoveis")
public class ImovelController {
    @Autowired
    private ImovelService imovelService;

    @PostMapping
    public Imovel salvar(@RequestBody Imovel imovel) {
        return imovelService.salvar(imovel);
    }

    @GetMapping
    public List<Imovel> listar() {
        return imovelService.listar();
    }

    @GetMapping("/{id}")
    public Imovel buscarPorId(@PathVariable Long id) {
        return imovelService.buscarPorId(id);
    }

    @DeleteMapping("/{id}")
    public void excluir(@PathVariable Long id) {
        imovelService.excluir(id);
    }
}
```

Vamos entender o que esse código faz:

- A anotação `@RestController` indica que essa classe é um controlador Spring que retorna objetos em formato JSON.
- A anotação `@RequestMapping` define o prefixo da URL para acessar os endpoints.
- A anotação `@Autowired` injeta a dependência do serviço `ImovelService` criado anteriormente.
- O método `salvar` é mapeado para a URL `/imoveis` com o método HTTP POST. Ele recebe um objeto `Imovel` no formato JSON no corpo da requisição e chama o método `salvar` do serviço `ImovelService`.
- O método `listar` é mapeado para a URL `/imoveis` com o método HTTP GET. Ele chama o método "listar" do serviço `ImovelService` e retorna a lista de objetos `Imovel` no formato JSON.
- O método `buscarPorId` é mapeado para a URL `/imoveis/{id}` com o método HTTP GET. Ele recebe um parâmetro `id` na URL e chama o método `buscarPorId` do serviço `ImovelService` para buscar o objeto `Imovel` correspondente. Se o objeto não for encontrado, retorna uma resposta HTTP com código 404.
- O método `excluir` é mapeado para a URL `/imoveis/{id}` com o método HTTP DELETE. Ele recebe um parâmetro `id` na URL e chama o método `excluir` do serviço `ImovelService` para excluir o objeto `Imovel` correspondente. Se o objeto não for encontrado, retorna uma resposta HTTP com código 404.

Agora, podemos testar nossa aplicação acessando os endpoints criados por esse controlador. Para isso, abra um navegador ou um cliente REST (como o Postman) e acesse as URLs:

- POST http://localhost:8080/imoveis - para salvar um novo objeto `Imovel`
GET http://localhost:8080/imoveis - para listar todos os objetos `Imovel`
GET http://localhost:8080/imoveis/{id} - para buscar um objeto `Imovel` por ID
DELETE http://localhost:8080/imoveis/{id} - para excluir um objeto `Imovel` por ID
Lembre-se de substituir "localhost" pelo endereço IP da máquina onde a aplicação está rodando.

Por exemplo, para salvar um novo objeto `Imovel` via REST, você pode enviar um POST com o seguinte corpo:

```json
{
  "tipo": "Apartamento",
  "endereco": "Rua A, 123",
  "numeroQuartos": 3,
  "numeroBanheiros": 2,
  "area": 75.0,
  "preco": 100000.0
}
```
E receber uma resposta no formato JSON com o objeto salvo:
```json
{
  "id": 1,
  "tipo": "Apartamento",
  "endereco": "Rua A, 123",
  "numeroQuartos": 3,
  "numeroBanheiros": 2,
  "area": 75.0,
  "preco": 100000.0
}
```
Para listar todos os objetos `Imovel`, você pode enviar um GET para a URL `/imoveis` e receber uma resposta no formato JSON com uma lista de objetos `Imovel`.

Para buscar um objeto `Imovel` por ID, você pode enviar um GET para a URL `/imoveis/{id}` e receber uma resposta no formato JSON com o objeto correspondente. Se o objeto não for encontrado, você receberá uma resposta com código HTTP 404.

Para excluir um objeto `Imovel` por ID, você pode enviar um DELETE para a URL `/imoveis/{id}` e receber uma resposta vazia com código HTTP 204. Se o objeto não for encontrado, você receberá uma resposta com código HTTP 404.

Com esses endpoints, você pode manipular os objetos `Imovel` da sua aplicação por meio de uma API RESTful simples e eficiente.

## Conclusão
Neste post, vimos como criar um microserviço para gerenciar imóveis utilizando o Spring Boot e a Azure. Começamos criando um projeto Spring Boot com as dependências necessárias para a nossa aplicação. Em seguida, criamos a classe Imovel para representar um imóvel e mapeamos essa classe para o banco de dados usando o Spring Data JPA.

Depois, criamos o controlador ImovelController para lidar com as requisições HTTP e acessar o repositório ImovelRepository para realizar as operações no banco de dados. Por fim, configuramos o banco de dados MariaDB no Spring Boot e vimos como criar um repositório utilizando a interface JpaRepository do Spring Data JPA.

Com a nossa aplicação funcionando corretamente, podemos agora fazer o deploy em um container Docker na Azure e deixar nosso microserviço disponível na nuvem para acesso externo. O que iremos fazer na parte 2 desse tutorial.