Initial commit
This commit is contained in:
89
store-application/pom.xml
Normal file
89
store-application/pom.xml
Normal file
@@ -0,0 +1,89 @@
|
||||
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>store-application</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<artifactId>hexagonal-architecture</artifactId>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<relativePath>../../hexagonal-architecture</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>store-core</artifactId>
|
||||
<version>${store.core.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>store-persistence</artifactId>
|
||||
<version>${store.core.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>store-email-sender</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${maven-compiler-plugin.version}</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<properties>
|
||||
<spring.boot.version>2.1.3.RELEASE</spring.boot.version>
|
||||
<store.core.version>1.0-SNAPSHOT</store.core.version>
|
||||
<store.persistence.version>1.0-SNAPSHOT</store.persistence.version>
|
||||
</properties>
|
||||
</project>
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.baeldung.hexagonal.store.application;
|
||||
|
||||
import com.baeldung.hexagonal.store.emailsender.EmailSenderConfig;
|
||||
import com.baeldung.hexagonal.store.persistence.PersistenceConfig;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
@SpringBootApplication
|
||||
@ComponentScan(basePackages = {"com.baeldung.hexagonal.store.application","com.baeldung.hexagonal.store.core"})
|
||||
@Import({PersistenceConfig.class, EmailSenderConfig.class})
|
||||
public class StoreApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(StoreApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.baeldung.hexagonal.store.application.base;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public interface Mapper<F, T> {
|
||||
T map(F source);
|
||||
|
||||
default List<T> mapList(List<F> sourceList) {
|
||||
return sourceList.stream().map(this::map).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.baeldung.hexagonal.store.application.controller;
|
||||
|
||||
import com.baeldung.hexagonal.store.application.base.Mapper;
|
||||
import com.baeldung.hexagonal.store.application.dto.request.OrderCreateRequestDto;
|
||||
import com.baeldung.hexagonal.store.application.dto.response.OrderResponseDto;
|
||||
import com.baeldung.hexagonal.store.core.context.customer.exception.OrderNotFoundException;
|
||||
import com.baeldung.hexagonal.store.core.context.order.entity.Order;
|
||||
import com.baeldung.hexagonal.store.core.context.order.service.OrderService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/orders")
|
||||
public class OrderController {
|
||||
Mapper<Order, OrderResponseDto> orderResponseDtoMapper;
|
||||
OrderService orderService;
|
||||
|
||||
@Autowired
|
||||
public OrderController(OrderService orderService, Mapper<Order, OrderResponseDto> orderResponseDtoMapper) {
|
||||
this.orderService = orderService;
|
||||
this.orderResponseDtoMapper = orderResponseDtoMapper;
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<OrderResponseDto> getOrder(@PathVariable("id") int id) {
|
||||
Optional<Order> order = this.orderService.getOrderById(id);
|
||||
if (order.isPresent()) {
|
||||
return new ResponseEntity<>(this.orderResponseDtoMapper.map(order.get()), HttpStatus.OK);
|
||||
} else {
|
||||
throw new OrderNotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/customer/{customerId}")
|
||||
public ResponseEntity<OrderResponseDto> createOrder(@PathVariable("customerId") int customerId, @RequestBody OrderCreateRequestDto requestDto) {
|
||||
Optional<Order> order = this.orderService.processNewCustomerOrder(customerId, requestDto.getProductQuantityMap());
|
||||
return new ResponseEntity<>(this.orderResponseDtoMapper.map(order.get()), HttpStatus.OK);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.baeldung.hexagonal.store.application.controller.exception;
|
||||
|
||||
import com.baeldung.hexagonal.store.core.context.customer.exception.CustomerNotFoundException;
|
||||
import com.baeldung.hexagonal.store.core.context.customer.exception.NotEnoughFundsException;
|
||||
import com.baeldung.hexagonal.store.core.context.customer.exception.OrderNotFoundException;
|
||||
import com.baeldung.hexagonal.store.core.context.order.exception.ProductNotFoundException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
|
||||
|
||||
@RestControllerAdvice
|
||||
public class RestResponseEntityExceptionHandler
|
||||
extends ResponseEntityExceptionHandler {
|
||||
|
||||
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||
@ExceptionHandler(value = {ProductNotFoundException.class, CustomerNotFoundException.class})
|
||||
protected ResponseEntity<JsonResponse> handleNotFoundException(
|
||||
RuntimeException ex, WebRequest request) {
|
||||
return new ResponseEntity<>(new JsonResponse(ex.getMessage()), HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
@ExceptionHandler(value = {OrderNotFoundException.class, NotEnoughFundsException.class})
|
||||
protected ResponseEntity<JsonResponse> handleBadRequestException(
|
||||
RuntimeException ex, WebRequest request) {
|
||||
return new ResponseEntity<>(new JsonResponse(ex.getMessage()), HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
private static class JsonResponse {
|
||||
String message;
|
||||
|
||||
public JsonResponse() {
|
||||
}
|
||||
|
||||
public JsonResponse(String message) {
|
||||
super();
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.baeldung.hexagonal.store.application.dto.entity;
|
||||
|
||||
public class CustomerDto {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.baeldung.hexagonal.store.application.dto.mapper;
|
||||
|
||||
import com.baeldung.hexagonal.store.application.base.Mapper;
|
||||
import com.baeldung.hexagonal.store.application.dto.response.OrderProductResponseDto;
|
||||
import com.baeldung.hexagonal.store.application.dto.response.OrderResponseDto;
|
||||
import com.baeldung.hexagonal.store.core.context.order.entity.Order;
|
||||
import com.baeldung.hexagonal.store.core.context.order.entity.OrderProduct;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class OrderEntityToDtoMapper implements Mapper<Order, OrderResponseDto> {
|
||||
|
||||
@Autowired
|
||||
Mapper<OrderProduct, OrderProductResponseDto> orderProductResponseDtoMapper;
|
||||
|
||||
@Override
|
||||
public OrderResponseDto map(Order source) {
|
||||
OrderResponseDto responseDto = new OrderResponseDto();
|
||||
responseDto.setId(source.getId());
|
||||
responseDto.setStatus(source.getStatus());
|
||||
responseDto.setOrderProducts(orderProductResponseDtoMapper.mapList(source.getOrderProducts()));
|
||||
return responseDto;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.baeldung.hexagonal.store.application.dto.mapper;
|
||||
|
||||
import com.baeldung.hexagonal.store.application.base.Mapper;
|
||||
import com.baeldung.hexagonal.store.application.dto.response.OrderProductResponseDto;
|
||||
import com.baeldung.hexagonal.store.application.dto.response.ProductResponseDto;
|
||||
import com.baeldung.hexagonal.store.core.context.order.entity.OrderProduct;
|
||||
import com.baeldung.hexagonal.store.core.context.order.entity.Product;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class OrderProductEntityToDtoMapper implements Mapper<OrderProduct, OrderProductResponseDto> {
|
||||
final Mapper<Product, ProductResponseDto> productResponseDtoMapper;
|
||||
|
||||
@Autowired
|
||||
public OrderProductEntityToDtoMapper(Mapper<Product, ProductResponseDto> productResponseDtoMapper) {
|
||||
this.productResponseDtoMapper = productResponseDtoMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OrderProductResponseDto map(OrderProduct source) {
|
||||
OrderProductResponseDto orderProductResponseDto = new OrderProductResponseDto();
|
||||
orderProductResponseDto.setProduct(this.productResponseDtoMapper.map(source.getProduct()));
|
||||
orderProductResponseDto.setQuantity(source.getQuantity());
|
||||
return orderProductResponseDto;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.baeldung.hexagonal.store.application.dto.mapper;
|
||||
|
||||
import com.baeldung.hexagonal.store.application.base.Mapper;
|
||||
import com.baeldung.hexagonal.store.application.dto.response.ProductResponseDto;
|
||||
import com.baeldung.hexagonal.store.core.context.order.entity.Product;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ProductEntityToDtoMapper implements Mapper<Product, ProductResponseDto> {
|
||||
|
||||
@Override
|
||||
public ProductResponseDto map(Product source) {
|
||||
ProductResponseDto responseDto = new ProductResponseDto();
|
||||
responseDto.setId(source.getId());
|
||||
responseDto.setName(source.getName());
|
||||
responseDto.setPrice(source.getPrice());
|
||||
return responseDto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.baeldung.hexagonal.store.application.dto.request;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class OrderCreateRequestDto {
|
||||
List<OrderProductDescriptor> productIds;
|
||||
|
||||
public List<OrderProductDescriptor> getProductIds() {
|
||||
return productIds;
|
||||
}
|
||||
|
||||
public void setProductIds(List<OrderProductDescriptor> productIds) {
|
||||
this.productIds = productIds;
|
||||
}
|
||||
|
||||
public Map<Long, Integer> getProductQuantityMap() {
|
||||
return this.productIds
|
||||
.stream()
|
||||
.collect(Collectors.toMap(OrderProductDescriptor::getProductId, OrderProductDescriptor::getQuantity));
|
||||
}
|
||||
|
||||
public static class OrderProductDescriptor {
|
||||
private long productId;
|
||||
private int quantity;
|
||||
|
||||
public int getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public void setQuantity(int quantity) {
|
||||
this.quantity = quantity;
|
||||
}
|
||||
|
||||
public long getProductId() {
|
||||
return productId;
|
||||
}
|
||||
|
||||
public void setProductId(long productId) {
|
||||
this.productId = productId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.baeldung.hexagonal.store.application.dto.response;
|
||||
|
||||
public class OrderProductResponseDto {
|
||||
|
||||
private Integer quantity;
|
||||
private ProductResponseDto product;
|
||||
|
||||
public Integer getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public void setQuantity(Integer quantity) {
|
||||
this.quantity = quantity;
|
||||
}
|
||||
|
||||
public ProductResponseDto getProduct() {
|
||||
return product;
|
||||
}
|
||||
|
||||
public void setProduct(ProductResponseDto product) {
|
||||
this.product = product;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.baeldung.hexagonal.store.application.dto.response;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class OrderResponseDto {
|
||||
private String status;
|
||||
private Long id;
|
||||
private List<OrderProductResponseDto> orderProducts;
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public List<OrderProductResponseDto> getOrderProducts() {
|
||||
return orderProducts;
|
||||
}
|
||||
|
||||
public void setOrderProducts(List<OrderProductResponseDto> orderProducts) {
|
||||
this.orderProducts = orderProducts;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.baeldung.hexagonal.store.application.dto.response;
|
||||
|
||||
public class ProductResponseDto {
|
||||
private String name;
|
||||
private Double price;
|
||||
private Long id;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Double getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public void setPrice(Double price) {
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
spring.h2.console.enabled=true
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.baeldung;
|
||||
|
||||
import com.baeldung.hexagonal.store.application.StoreApplication;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = StoreApplication.class)
|
||||
public class SpringContextIntegrationTest {
|
||||
|
||||
@Test
|
||||
public void whenSpringContextIsBootstrapped_thenNoExceptions() {
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user