A Order API gerencia os pedidos do domínio store, permitindo criar e consultar ordens associadas ao usuário autenticado. Ela segue o padrão adotado no projeto: interface (order) e service (order-service) atrás do gateway e protegidos por JWT.
Trusted layer e segurança
Toda requisição externa entra pelo gateway. As rotas /order/** são protegidas: é obrigatório enviar Authorization: Bearer <jwt>.
Visão geral
Interface (order): define o contrato (DTOs e Feign) consumido por outros módulos/fronts.
Service (order-service): implementação REST, regras de negócio, persistência (JPA), e migrações (Flyway).
classDiagram
namespace order {
class OrderController {
+create(OrderIn orderIn): OrderOut
+findAll(): List<OrderOut>
+findById(String id): OrderOut
}
class OrderIn {
-List<OrderItemIn> items
}
class OrderItemIn {
-String idProduct
-int quantity
}
class OrderOut {
-String id
-String date
-List<OrderItemOut> items
-Double total
}
class OrderItemOut {
-String id
-ProductOut product
-int quantity
-Double total
}
}
namespace order-service {
class OrderResource {
+create(OrderIn orderIn): OrderOut
+findAll(): List<OrderOut>
+findById(String id): OrderOut
}
class OrderService {
+create(OrderIn orderIn): OrderOut
+findAll(): List<OrderOut>
+findById(String id): OrderOut
}
class OrderRepository {
+save(Order order): Order
+findAll(): List<Order>
+findById(String id): Optional<Order>
}
class Order {
-String id
-String date
-List~OrderItem~ items
}
class OrderItem {
-String id
-String idProduct
-int quantity
-Double total
}
class OrderModel {
+toEntity(OrderIn orderIn): Order
+toOut(Order order): OrderOut
}
}
<<Interface>> OrderController
OrderController ..> OrderIn
OrderController ..> OrderOut
<<Interface>> OrderRepository
OrderController <|-- OrderResource
OrderResource *-- OrderService
OrderService *-- OrderRepository
OrderService ..> Order
OrderService ..> OrderModel
OrderRepository ..> Order
Order ..> OrderItem
OrderOut ..> OrderItemOut
OrderItemOut ..> ProductOut
Estrutura da requisição
flowchart LR
subgraph api [Trusted Layer]
direction TB
gateway --> account
gateway --> auth
account --> db@{ shape: cyl, label: "Database" }
auth --> account
gateway --> product
gateway e6@==> order:::red
product --> db
order e3@==> db
order e4@==> product
end
internet e1@==>|request| gateway
e1@{ animate: true }
e3@{ animate: true }
e4@{ animate: true }
e6@{ animate: true }
classDef red fill:#fcc
click order "#order-api" "Order API"
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.5.5</version><relativePath/></parent><groupId>store</groupId><artifactId>order</artifactId><version>1.0.0</version><properties><java.version>21</java.version><spring-cloud.version>2025.0.0</spring-cloud.version><maven.compiler.proc>full</maven.compiler.proc></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!-- Reuso de DTO de produto no OrderItemOut --><dependency><groupId>store</groupId><artifactId>product</artifactId><version>${project.version}</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement></project>
packagestore.order;importjava.util.ArrayList;importjava.util.List;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.http.HttpStatus;importorg.springframework.stereotype.Service;importorg.springframework.transaction.annotation.Transactional;importorg.springframework.web.server.ResponseStatusException;importstore.product.ProductController;importstore.product.ProductOut;@ServicepublicclassOrderService{@AutowiredprivateOrderRepositoryorderRepository;@AutowiredprivateProductControllerproductController;@TransactionalpublicOrderOutcreate(OrderInin,StringidAccount){if(in==null||in.items()==null||in.items().isEmpty()){thrownewResponseStatusException(HttpStatus.BAD_REQUEST,"Items are mandatory");}List<ProductOut>products=newArrayList<>();in.items().forEach(it->{ProductOutp=productController.findById(it.idProduct()).getBody();if(p==null){thrownewResponseStatusException(HttpStatus.BAD_REQUEST,"Product not found: "+it.idProduct());}products.add(p);});OrderModelom=OrderParser.toModel(in,idAccount);doubleorderTotal=0.0;for(inti=0;i<in.items().size();i++){varinItem=in.items().get(i);varproduct=products.get(i);doubletotal=product.price()*inItem.quantity();OrderItemModelim=newOrderItemModel();im.setOrder(om);im.setIdProduct(inItem.idProduct());im.setQuantity(inItem.quantity());im.setTotal(total);om.getItems().add(im);orderTotal+=total;}om.setTotal(orderTotal);OrderModelsaved=orderRepository.save(om);returnOrderParser.toOut(saved,products);}publicList<OrderOut>findAll(StringidAccount){varlist=orderRepository.findAllByIdAccount(idAccount);// Para listar rápido, não precisamos montar os produtos: retornamos sem detalhes de produtoreturnlist.stream().map(om->OrderOut.builder().id(om.getId()).date(om.getDate().toString()).items(null)// lista resumida.total(om.getTotal()).build()).toList();}publicOrderOutfindById(Stringid,StringidAccount){OrderModelom=orderRepository.findByIdAndIdAccount(id,idAccount).orElseThrow(()->newResponseStatusException(HttpStatus.NOT_FOUND,"Order not found"));// carrega os produtos dos itens desse pedidoList<ProductOut>products=om.getItems().stream().map(it->productController.findById(it.getIdProduct()).getBody()).toList();returnOrderParser.toOut(om,products);}}