404 Not Found

404 Not Found


nginx

Exercício Abrangente da Fase 1: Construindo o Framework Básico do OrderFlow

Colocando o Conhecimento em Prática—Esta aula reúne todos os conceitos abordados nas primeiras cinco aulas para construir o framework fundamental completo para o sistema de gerenciamento de pedidos de e-commerce OrderFlow.

1. O Que Você Vai Aprender


2. Uma História Real de Trabalho em Equipe

(1) Ponto de Dor: Conhecimento fragmentado e não pode ser colocado em prática

Alice havia terminado de aprender sobre auto-configuração do Spring Boot, APIs REST, gerenciamento de configuração e outros tópicos, mas quando chegou a hora de realmente construir o projeto OrderFlow, ela não sabia por onde começar. Cada tópico foi ensinado isoladamente, então ela não tinha certeza de como alternar entre H2 para o ambiente dev e MySQL para o ambiente prod, ou como integrar APIs REST com gerenciamento de configuração.

(2) Soluções para os Exercícios Abrangentes

Esta aula une todos os conceitos em um projeto completo—desde criar o projeto, configurar o ambiente e escrever APIs até personalizar o Starter—para simular um fluxo de trabalho de desenvolvimento do mundo real.

(3) Resultado

Depois que Alice completou os exercícios abrangentes, o esqueleto do projeto OrderFlow pode servir diretamente como base para cursos subsequentes, impulsionando a eficiência de desenvolvimento em três vezes.


3. Criando um Projeto e Configurando o Ambiente

(1) Inicialização do Projeto

(1) ▶ Exemplo: Criando um Projeto Usando o Spring Initializr

BASH
curl https://start.spring.io/starter.zip \
  -d type=maven-project \
  -d language=java \
  -d bootVersion=3.2.5 \
  -d groupId=com.orderflow \
  -d artifactId=orderflow-service \
  -d packageName=com.orderflow \
  -d javaVersion=17 \
  -d dependencies=web,data-jpa,h2,mysql,validation,lombok \
  -o orderflow-service.zip && unzip orderflow-service.zip

Saída:

TEXT
{"status":"ok","data":{}}

(2) Configuração de Múltiplos Ambientes

100%
graph TD
    A["application.yml<br/>Comum"] --> B["application-dev.yml<br/>H2 + Debug"]
    A --> C["application-prod.yml<br/>MySQL + WARN"]
    B --> D["Mesclado: profile dev"]
    C --> E["Mesclado: profile prod"]
Item de Configuração dev prod
Banco de Dados H2 Em Memória MySQL
ddl-auto create-drop validate
Nível de Log DEBUG WARN
Porta 8080 8080

4. API REST de Produtos e Pedidos

(1) Estrutura de Pacotes do Projeto

TEXT
com.orderflow/
├── OrderFlowApplication.java
├── config/
│   └── OrderFlowProperties.java
├── controller/
│   ├── ProductController.java
│   └── OrderController.java
├── model/
│   ├── Product.java
│   └── Order.java
└── repository/
    ├── ProductRepository.java
    └── OrderRepository.java

(1) ▶ Exemplo: DTO de Produto e Controller

JAVA
// model/Product.java
package com.orderflow.model;

public record Product(
    Long id,
    String name,
    BigDecimal price,
    Integer stock
) {}

// controller/ProductController.java
package com.orderflow.controller;

import com.orderflow.model.Product;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

@RestController
@RequestMapping("/api/v1/products")
public class ProductController {

    private final Map<Long, Product> store = new ConcurrentHashMap<>();
    private final AtomicLong seq = new AtomicLong(1);

    @PostMapping
    public ResponseEntity<Product> create(@RequestBody Product product) {
        Long id = seq.getAndIncrement();
        Product saved = new Product(id, product.name(), product.price(), product.stock());
        store.put(id, saved);
        return ResponseEntity.status(HttpStatus.CREATED).body(saved);
    }

    @GetMapping
    public Collection<Product> list() {
        return store.values();
    }

    @GetMapping("/{id}")
    public ResponseEntity<Product> get(@PathVariable Long id) {
        Product p = store.get(id);
        return p != null ? ResponseEntity.ok(p) : ResponseEntity.notFound().build();
    }
}

Saída:

TEXT
// Execução bem-sucedida

(2) ▶ Exemplo: DTO de Pedido e Controller

JAVA
// model/Order.java
package com.orderflow.model;

import java.time.Instant;

public record Order(
    Long id,
    Long productId,
    Integer quantity,
    String status,
    Instant createdAt
) {}

// controller/OrderController.java
package com.orderflow.controller;

import com.orderflow.model.Order;
import com.orderflow.config.OrderFlowProperties;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

@RestController
@RequestMapping("/api/v1/orders")
public class OrderController {

    private final Map<Long, Order> store = new ConcurrentHashMap<>();
    private final AtomicLong seq = new AtomicLong(1);
    private final OrderFlowProperties props;

    public OrderController(OrderFlowProperties props) {
        this.props = props;
    }

    @PostMapping
    public ResponseEntity<Order> create(@RequestBody OrderRequest req) {
        if (req.quantity() > props.maxItemsPerOrder()) {
            return ResponseEntity.badRequest().build();
        }
        Long id = seq.getAndIncrement();
        Order order = new Order(id, req.productId(), req.quantity(), "PENDING", Instant.now());
        store.put(id, order);
        return ResponseEntity.status(HttpStatus.CREATED).body(order);
    }

    @GetMapping("/{id}")
    public ResponseEntity<Order> get(@PathVariable Long id) {
        Order o = store.get(id);
        return o != null ? ResponseEntity.ok(o) : ResponseEntity.notFound().build();
    }

    public record OrderRequest(Long productId, Integer quantity) {}
}

Saída:

TEXT
// Execução bem-sucedida

5. Configuração de Negócio com ConfigurationProperties

(1) ▶ Exemplo: OrderFlowProperties

JAVA
// config/OrderFlowProperties.java
package com.orderflow.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import java.time.Duration;

@ConfigurationProperties(prefix = "orderflow")
public record OrderFlowProperties(
    int maxItemsPerOrder,
    Duration orderTimeout,
    String defaultCurrency
) {}

Saída:

TEXT
// Execução bem-sucedida
YAML
# application.yml
spring:
  application:
    name: orderflow-service
  profiles:
    active: dev

orderflow:
  max-items-per-order: 100
  order-timeout: 30m
  default-currency: USD

6. Excluindo Auto-Configuração e Starters Personalizados

(1) ▶ Exemplo: Excluindo Auto-Configurações Indesejadas

JAVA
@SpringBootApplication(exclude = {
    org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration.class
})
@EnableConfigurationProperties(OrderFlowProperties.class)
public class OrderFlowApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderFlowApplication.class, args);
    }
}

Saída:

TEXT
// Execução bem-sucedida
⚠️ Nota: DataSource é excluído nesta fase porque não estamos usando um banco de dados real por enquanto. Esta exclusão deve ser removida após a introdução do JPA na Fase 2.


7. Organizando Postman Collections

(1) ▶ Exemplo: Script de Teste Postman

BASH
# Testes da API de Produtos
curl -X POST http://localhost:8080/api/v1/products \
  -H "Content-Type: application/json" \
  -d '{"name":"Laptop","price":999.99,"stock":50}'

curl http://localhost:8080/api/v1/products

curl http://localhost:8080/api/v1/products/1

# Testes da API de Pedidos
curl -X POST http://localhost:8080/api/v1/orders \
  -H "Content-Type: application/json" \
  -d '{"productId":1,"quantity":3}'

curl http://localhost:8080/api/v1/orders/1

# Verificação de configuração
curl http://localhost:8080/api/v1/health

Saída:

TEXT
{"status":"ok","data":{}}
Cenário de Teste API Código de Status Esperado
Criar Produto POST /api/v1/products 201
Consultar Lista de Produtos GET /api/v1/products 200
Consultar um Único Produto GET /api/v1/products/1 200
Criar Pedido POST /api/v1/orders 201
Pedido Excessivo POST /api/v1/orders (qty=200) 400

8. Exemplo Abrangente: Framework Básico Completo do OrderFlow

JAVA
// OrderFlowApplication.java
package com.orderflow;

import com.orderflow.config.OrderFlowProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

@SpringBootApplication
@EnableConfigurationProperties(OrderFlowProperties.class)
public class OrderFlowApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderFlowApplication.class, args);
    }
}

// OrderFlowProperties.java
package com.orderflow.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import java.time.Duration;

@ConfigurationProperties(prefix = "orderflow")
public record OrderFlowProperties(
    int maxItemsPerOrder,
    Duration orderTimeout,
    String defaultCurrency
) {}
YAML
# application.yml
spring:
  application:
    name: orderflow-service
  profiles:
    active: dev

orderflow:
  max-items-per-order: 100
  order-timeout: 30m
  default-currency: USD

---
# application-dev.yml
spring:
  datasource:
    url: jdbc:h2:mem:orderflow_dev
    username: sa
    password:
  h2:
    console:
      enabled: true
  jpa:
    hibernate:
      ddl-auto: create-drop
    show-sql: true

logging:
  level:
    com.orderflow: DEBUG

---
# application-prod.yml
spring:
  datasource:
    url: jdbc:mysql://${DB_HOST:localhost}:3306/orderflow
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: false

logging:
  level:
    com.orderflow: WARN
💻 Saída:

TEXT
# Inicialização no modo dev
$ java -jar orderflow-service.jar --spring.profiles.active=dev
Started OrderFlowApplication in 3.2 seconds

# Inicialização no modo prod
$ java -jar orderflow-service.jar --spring.profiles.active=prod
Started OrderFlowApplication in 5.1 seconds

❓ Perguntas Frequentes

P A estrutura do projeto dos exercícios da Fase 1 pode ser usada nas aulas subsequentes?
R Sim. O framework construído nesta aula serve como base para as Fases 2-5. A Fase 2 adicionará JPA, camada de serviço, validação e mais, aprimorando gradualmente o projeto.
P Por que a auto-configuração do DataSource é temporariamente excluída?
R Nesta fase, estamos usando um mapa em memória para simular armazenamento de dados. Uma vez que Spring Data JPA for introduzido na Fase 2, uma fonte de dados real será necessária; nesse ponto, você pode remover a exclusão.
P Como as diferenças de configuração entre dev e prod devem ser gerenciadas?
R Coloque configurações comuns em application.yml e configurações específicas do ambiente em application-{profile}.yml. Sempre injete informações sensíveis (senhas, chaves) usando variáveis de ambiente.
P Como posso verificar se o Profile está ativo?
R The following 1 profile is active: "dev" será impresso no log de inicialização. Você também pode injetar @Value("${spring.profiles.active}") no Controller e retorná-lo.
P Quais são as limitações de usar record como DTO?
R record é imutável e não é adequado para entidades JPA (que requerem construtor sem argumentos e campos mutáveis). No entanto, é muito adequado para uso como DTOs de requisição/resposta na camada de controller.
P Como uma Postman Collection deve ser organizada?
R Agrupe recursos por categoria (Produtos, Pedidos), adicione asserções (códigos de status, campos de resposta) a cada requisição, e use variáveis de ambiente para gerenciar a URL base.

📖 Resumo


📝 Exercícios

  1. Exercício Básico (Dificuldade: ⭐): Complete todo o código desta aula, garanta que o ambiente dev inicie com sucesso e verifique que todos os comandos de teste curl passam.

  2. Exercício Avançado (Dificuldade ⭐⭐): Adicione os endpoints PUT /api/v1/products/{id} e DELETE /api/v1/products/{id}, adicione o endpoint de lista GET /api/v1/orders ao OrderController, e implemente filtragem usando o parâmetro de consulta status.

  3. Desafio (Dificuldade: ⭐⭐⭐): Crie um orderflow-spring-boot-starter que envolva o endpoint de health check do OrderFlow (que retorna o nome da aplicação, versão, profile e informações da JVM), integre-o no projeto OrderFlow e verifique que a auto-configuração entra em vigor.

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%