Spring Boot Um Exemplo Completo - IMasters
Spring Boot Um Exemplo Completo - IMasters
Spring Boot Um Exemplo Completo - IMasters
br/)
we are developers
Certificações (h p://certificacao.imasters.com.br)
POWERED
(/)
BY:
(HTTP://DEVELOPERS.TOTVS.COM/) (h ps://www.facebook.com/PortaliMasters) (h ps://twi er.c
Back-End(https://imasters.com.br/back-end)
Mobile(https://imasters.com.br/mobile)
Front End(https://imasters.com.br/front-end)
DevSecOps(https://imasters.com.br/devsecops)
Design & UX(https://imasters.com.br/design-ux)
Data(https://imasters.com.br/data)
APIs e Microsserviços(https://imasters.com.br/apis-microsservicos)
IoT e Makers(https://imasters.com.br/iot-makers)
DESENVOLVIMENTO
1 AGO, 2017
FELIPE ADORNO
(HTTPS://IMASTERS.COM.BR/PERFIL/FELIPEADORNO)
Tem 7 artigos publicados com 12692
visualizações desde 2017
PUBLICIDADE
É engenheiro de so ware na Monkey Exchange, onde trabalha fortemente com Spring Cloud e possui mais de 10 anos de experiência no mundo da
tecnologia. É ativo na comunidade Java, onde sempre tem dado palestras e participado de debates sobre os mais variados assuntos. Além disso tem
contribuído em alguns frameworks como Apache Commons, FF4J, Netflix Ribbon e outros.
22 AGO, 2017
Spring cloud contract – Trabalhando com microsserviços (h ps://imasters.com.br/apis-microsservicos/spring-cloud-contract-trabalhando-com-
microsservicos)
18 AGO, 2017
7Masters Spring – Spring Cloud Contract (h ps://imasters.com.br/desenvolvimento/7masters-spring-spring-cloud-contract)
25 JUL, 2017
Hystrix circuit breaker (h ps://imasters.com.br/desenvolvimento/hystrix-circuit-breaker)
O lá, hoje quero abordar um assunto que acho que vai tirar as dúvidas de muitas pessoas, pois sempre que criamos um projeto
surgem algumas perguntas:
Como vou criar o projeto?
Como vou organizar os pacotes?
Onde ficam as regras de negócio?
E, se for uma API RestFul, a coisa fica ainda pior e outras perguntas surgem:
(https://static.imasters.com.br/wp-content/uploads/2017/08/01-
SpringBoot.jpg)
h ps://start.spring.io/
As dependências utilizadas são: Web, JPA, H2 (Para eu não configurar um banco de dados), Hateoas e Lombok. Pra quem não
conhece o Lombok, ele possui algumas anotações que evitam que tenhamos que escrever getters, setter, construtores, equals,
hashcode, builders e etc.
Agora que o projeto já está criado, é hora de começar a organizar as coisas. Já vi pessoas que gostam de dividir o projeto em
módulos, e isso normalmente deixa o projeto com o módulo de domain (ORM e acesso a dados), core (Classes de negócio) e um
módulo onde ficam os serviços web. A primeira vista isso parece legal, mas acaba onerando muito tendo que criar um pom pai e
orquestrar todas as dependências do maven e todos os plugins e etc.
Prefiro fazer apenas um projeto e separar tudo isso em pacotes, assim o meu projeto fica como exibido na imagem abaixo:
(https://static.imasters.com.br/wp-content/uploads/2017/08/02-SpringBoot.jpg)
Service: Todos os serviços onde estão as regras de negócio, validações e o que mais for preciso.
A classe de start do spring eu deixo fora de qualquer pacote, porque ela sempre é usada e não é legal ter que ficar procurado por
ela o tempo todo.
As regras de negócio são o core do business e elas devem ser fáceis de alterar, fáceis de encontrar e fáceis de entender. Vejo
pessoas que divagam na abstração e fazem coisas que nem mesmo elas, depois de dois dias, são capazes de entender, então faça
um código que resolva o problema, e, caso ele precise ser mais abstrato ou mais extensível, aí então você se preocupa com isso.
Lembre-se que entregar funcionalidade ao usuário é o que mais importa, você é um desenvolvedor e quer que as pessoas usem
coisas que você criou.
Os serviços são @Service para o Spring e devem ter uma interface. Eu normalmente coloco o nome da interface como BookService
e o nome da implementação como BookServiceProvider. O serviço deve receber dados da api, passar pelas suas regras e armazenar
ou consultar na base de dados.
Os serviços acessam os repositories, ORM’s e Specifications que estão no pacote domain, então vamos criar uma ORM e um
repository, para isso crie um pacote orm e um pacote repository dentro do pacote domain e então crie as classes abaixo:
Book orm:
1 package br.com.sample.domain.orm;
2
3 import lombok.Builder;
4 import lombok.Getter;
5 import lombok.NoArgsConstructor;
6
7 import javax.persistence.GeneratedValue;
8 import javax.persistence.Id;
9
10 @Entity
11 @Getter
12 @Builder
13 @NoArgsConstructor
14 @AllArgsConstructor
15 public class Book {
16
17 @Id
18 @GeneratedValue
19 private Integer id;
Book Repository:
1 package br.com.sample.domain.repository;
2
3 import br.com.sample.domain.orm.Book;
4 import org.springframework.data.repository.CrudRepository;
5 import org.springframework.stereotype.Repository;
6
7 @Repository
Perfeito, feito isso é hora de criar a interface e a implementação do serviço e elas devem ficar como o exemplo abaixo:
A interface:
1 package br.com.sample.service;
2
3 import br.com.sample.domain.orm.Book;
4
5 public interface BookService {
6
7 Book getById(Integer id);
8 Book save(Book book);
9 }
A implementação:
1 package br.com.sample.service;
2
3 import br.com.sample.domain.orm.Book;
4 import br.com.sample.domain.repository.BookRepository;
5 import lombok.AllArgsConstructor;
6 import org.springframework.stereotype.Service;
7
8 import static java.util.Objects.isNull;
9
10 @Service
11 @AllArgsConstructor
12 public class BookServiceImpl implements BookService {
13
14 private final BookRepository bookRepository;
15
16 @Override
19 if(isNull(book)) {
20 throw new RuntimeException("Book not found!");
21 }
22 return book;
23 }
24
25 @Override
26 public Book save(Book book) {
27 //QUALQUER REGRA DE NEGÓCIO OU VALIDAÇÃO ANTES DE SALVAR
28 return bookRepository.save(book);
29 }
30 }
Lembre-se todas as regras de negócio devem ficar no serviço e devem ser legíveis, bem escritas e bem testadas.
Eu particularmente prefiro fazer o versionamento via URL, fica claro e fácil de saber qual a versão do resource você está acessando
então as urls ficam v1/books, v2/books e etc. E no projeto isso fica versionado em um pacote, então crie um pacote v1 dentro do
seu pacote api e pronto sua API já tem versionamento. Feito isso é hora de criar o endpoint RestFul, para isso crie uma classe
dentro do pacote api.v1 chamada BookResource e ela quem vai ser o retorno da API, justamente para não devolvermos a ORM, a
classe deve ficar como a do exemplo abaixo:
1 package br.com.sample.api.v1;
2 import lombok.Data;
3 import lombok.EqualsAndHashCode;
4 import org.springframework.hateoas.ResourceSupport;
5 import org.springframework.hateoas.core.Relation;
6 @Data
7 @EqualsAndHashCode(callSuper = true)
8 @Relation(value="book", collectionRelation="books")
9 class BookResource extends ResourceSupport {
10 private String title;
11 private String author;
12 private Integer pages;
13 }
Pode parecer estranho criar uma classe igual a ORM para ser devolvida via API, é estranho sim nesse nosso caso, mas e se você
colocar mais informações na ORM e não quer que elas sejam expostas como você vai fazer? Dessa maneira o seu problema já foi
solucionado antes de acontecer.
Criado o resource é hora de criar o endpoint serviço rest que vai receber as chamadas, para isso criar uma class chamada
BooRestService como a do exemplo abaixo:
1 package br.com.sample.api.v1;
2
3 import br.com.sample.service.BookService;
4 import lombok.AllArgsConstructor;
5 import org.springframework.http.HttpStatus;
6 import org.springframework.web.bind.annotation.*;
7 import sun.reflect.generics.reflectiveObjects.NotImplementedException;
8
9 @RestController
10 @AllArgsConstructor
11 @RequestMapping("v1/books")
26 }
27
28 }
Bom, você deve ter notado que a classe ainda não tem implementação dos métodos e isso acontece porque eu preciso criar o
assembler que ira resolver os próximos dois problemas que são “Onde eu coloco os links dos meus resource?” e “Vou devolver a
minha entidade toda na API?”.
O AssemblerResource é onde são adicionados os links e a conversão de Domain -> Resource e Resource -> Domain é realizada. O
resource deve ser como o exemplo abaixo:
1 package br.com.sample.api.v1;
2
3 import br.com.sample.domain.orm.Book;
4 import org.springframework.hateoas.Link;
5 import org.springframework.hateoas.mvc.ResourceAssemblerSupport;
6 import org.springframework.stereotype.Component;
7
8 @Component
9 public class BookResourceAssembler extends ResourceAssemblerSupport<Book, BookResource> {
10
11 public BookResourceAssembler() {
12 super(BookRestService.class, BookResource.class);
13 }
14
15 @Override
16 public BookResource toResource(Book book) {
17 BookResource bookResource = createResourceWithId(book.getId(), book);
18 bookResource.setAuthor(book.getAuthor());
19 bookResource.setPages(book.getPages());
20 bookResource.setTitle(book.getTitle());
21 addLinks(bookResource);
22 return bookResource;
23 }
24
25 public Book toDomain(BookResource bookResource) {
26 return Book.builder()
27 .author(bookResource.getAuthor())
28 .pages(bookResource.getPages())
29 .title(bookResource.getTitle())
30 .build();
31 }
32
33 private void addLinks(BookResource bookResource) {
34 //Links de exemplo você pode usar o linkTo(methodOn())
35 bookResource.add(new Link("http://localhost:8080/v1/foo", "foo"));
36 bookResource.add(new Link("http://localhost:8080/v1/bar", "bar"));
37 }
38 }
O assembler cria automaticamente o link self poupando o seu trabalho. Agora é possível terminar a implementação do rest service
e deve ficar como o exemplo:
1 package br.com.sample.api.v1;
2
3 import br.com.sample.service.BookService;
4 import lombok.AllArgsConstructor;
5 import org.springframework.http.HttpStatus;
6 import org.springframework.web.bind.annotation.*;
7
8 @RestController
9 @AllArgsConstructor
10 @RequestMapping("v1/books")
11 public class BookRestService {
12
13 private final BookService bookService;
14 private final BookResourceAssembler bookResourceAssembler;
15
16 @ResponseStatus(HttpStatus.OK)
17 @RequestMapping(value = "/{id}", method = RequestMethod.GET)
18 public BookResource getById(@PathVariable Integer id) {
19 return bookResourceAssembler.toResource(bookService.getById(id));
20 }
21
22 @ResponseStatus(HttpStatus.CREATED)
23 @RequestMapping(method = RequestMethod.POST)
24 public BookResource save(@RequestBody BookResource bookResource) {
25 return bookResourceAssembler.toResource(bookService.save(bookResourceAssembler.toDomain(bookResource)));
26 }
27
28 }
Pronto, agora temos um projeto bem organizado e de fácil manutenção. Para ver o projeto que foi criado é só acessar o link
(https://github.com/FelipeAdorno/spring-boot-sample).
0 1 2 3 4 5 6 7 8 9 10