Initial commit

This commit is contained in:
Alexander Molochko
2019-03-17 08:46:15 +02:00
commit c2df4f148f
54 changed files with 1860 additions and 0 deletions

89
store-application/pom.xml Normal file
View 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>

View File

@@ -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);
}
}

View File

@@ -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());
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,5 @@
package com.baeldung.hexagonal.store.application.dto.entity;
public class CustomerDto {
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1 @@
spring.h2.console.enabled=true

View File

@@ -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() {
}
}