Merge pull request #8268 from lukaszrys/feature/BAEL-2275_ddd_layers
Feature/bael 2275 ddd layers
This commit is contained in:
commit
bf49d2c5f9
|
@ -76,6 +76,11 @@
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-core</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>de.flapdoodle.embed</groupId>
|
<groupId>de.flapdoodle.embed</groupId>
|
||||||
<artifactId>de.flapdoodle.embed.mongo</artifactId>
|
<artifactId>de.flapdoodle.embed.mongo</artifactId>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
package com.baeldung.ddd;
|
package com.baeldung.ddd;
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication(scanBasePackages = "com.baeldung.ddd.order")
|
||||||
public class PersistingDddAggregatesApplication {
|
public class PersistingDddAggregatesApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(PersistingDddAggregatesApplication.class, args);
|
SpringApplication.run(PersistingDddAggregatesApplication.class, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.baeldung.dddhexagonalspring;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.context.annotation.PropertySource;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@PropertySource(value = { "classpath:ddd-layers.properties" })
|
||||||
|
public class DomainLayerApplication {
|
||||||
|
public static void main(final String[] args) {
|
||||||
|
SpringApplication.run(DomainLayerApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package com.baeldung.dddhexagonalspring.application.controller;
|
||||||
|
|
||||||
|
import com.baeldung.dddhexagonalspring.application.request.AddProductRequest;
|
||||||
|
import com.baeldung.dddhexagonalspring.application.request.CreateOrderRequest;
|
||||||
|
import com.baeldung.dddhexagonalspring.application.response.CreateOrderResponse;
|
||||||
|
import com.baeldung.dddhexagonalspring.domain.service.OrderService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/orders")
|
||||||
|
public class OrderController {
|
||||||
|
|
||||||
|
private final OrderService orderService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public OrderController(OrderService orderService) {
|
||||||
|
this.orderService = orderService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
CreateOrderResponse createOrder(@RequestBody final CreateOrderRequest createOrderRequest) {
|
||||||
|
final UUID id = orderService.createOrder(createOrderRequest.getProduct());
|
||||||
|
|
||||||
|
return new CreateOrderResponse(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = "/{id}/products", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
void addProduct(@PathVariable final UUID id, @RequestBody final AddProductRequest addProductRequest) {
|
||||||
|
orderService.addProduct(id, addProductRequest.getProduct());
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping(value = "/{id}/products", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
void deleteProduct(@PathVariable final UUID id, @RequestParam final UUID productId) {
|
||||||
|
orderService.deleteProduct(id, productId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/complete")
|
||||||
|
void completeOrder(@PathVariable final UUID id) {
|
||||||
|
orderService.completeOrder(id);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.baeldung.dddhexagonalspring.application.request;
|
||||||
|
|
||||||
|
import com.baeldung.dddhexagonalspring.domain.Product;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
public class AddProductRequest {
|
||||||
|
@NotNull private Product product;
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
|
public AddProductRequest(@JsonProperty("product") final Product product) {
|
||||||
|
this.product = product;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Product getProduct() {
|
||||||
|
return product;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.baeldung.dddhexagonalspring.application.request;
|
||||||
|
|
||||||
|
import com.baeldung.dddhexagonalspring.domain.Product;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
public class CreateOrderRequest {
|
||||||
|
@NotNull private Product product;
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
|
public CreateOrderRequest(@JsonProperty("product") @NotNull final Product product) {
|
||||||
|
this.product = product;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Product getProduct() {
|
||||||
|
return product;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.baeldung.dddhexagonalspring.application.response;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class CreateOrderResponse {
|
||||||
|
private final UUID id;
|
||||||
|
|
||||||
|
public CreateOrderResponse(final UUID id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.baeldung.dddhexagonalspring.domain;
|
||||||
|
|
||||||
|
class DomainException extends RuntimeException {
|
||||||
|
DomainException(final String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
package com.baeldung.dddhexagonalspring.domain;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class Order {
|
||||||
|
private UUID id;
|
||||||
|
private OrderStatus status;
|
||||||
|
private List<OrderItem> orderItems;
|
||||||
|
private BigDecimal price;
|
||||||
|
|
||||||
|
public Order(final UUID id, final Product product) {
|
||||||
|
this.id = id;
|
||||||
|
this.orderItems = new ArrayList<>(Collections.singletonList(new OrderItem(product)));
|
||||||
|
this.status = OrderStatus.CREATED;
|
||||||
|
this.price = product.getPrice();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void complete() {
|
||||||
|
validateState();
|
||||||
|
this.status = OrderStatus.COMPLETED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addOrder(final Product product) {
|
||||||
|
validateState();
|
||||||
|
validateProduct(product);
|
||||||
|
orderItems.add(new OrderItem(product));
|
||||||
|
price = price.add(product.getPrice());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeOrder(final UUID id) {
|
||||||
|
validateState();
|
||||||
|
final OrderItem orderItem = getOrderItem(id);
|
||||||
|
orderItems.remove(orderItem);
|
||||||
|
|
||||||
|
price = price.subtract(orderItem.getPrice());
|
||||||
|
}
|
||||||
|
|
||||||
|
private OrderItem getOrderItem(final UUID id) {
|
||||||
|
return orderItems
|
||||||
|
.stream()
|
||||||
|
.filter(orderItem -> orderItem
|
||||||
|
.getProductId()
|
||||||
|
.equals(id))
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow(() -> new DomainException("Product with " + id + " doesn't exist."));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateState() {
|
||||||
|
if (OrderStatus.COMPLETED.equals(status)) {
|
||||||
|
throw new DomainException("The order is in completed state.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateProduct(final Product product) {
|
||||||
|
if (product == null) {
|
||||||
|
throw new DomainException("The product cannot be null.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrderStatus getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getPrice() {
|
||||||
|
return price;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<OrderItem> getOrderItems() {
|
||||||
|
return Collections.unmodifiableList(orderItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Order() {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package com.baeldung.dddhexagonalspring.domain;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class OrderItem {
|
||||||
|
private UUID productId;
|
||||||
|
private BigDecimal price;
|
||||||
|
|
||||||
|
public OrderItem(final Product product) {
|
||||||
|
this.productId = product.getId();
|
||||||
|
this.price = product.getPrice();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getProductId() {
|
||||||
|
return productId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getPrice() {
|
||||||
|
return price;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OrderItem() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
OrderItem orderItem = (OrderItem) o;
|
||||||
|
return Objects.equals(productId, orderItem.productId) && Objects.equals(price, orderItem.price);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(productId, price);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.baeldung.dddhexagonalspring.domain;
|
||||||
|
|
||||||
|
public enum OrderStatus {
|
||||||
|
CREATED, COMPLETED
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.baeldung.dddhexagonalspring.domain;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class Product {
|
||||||
|
private final UUID id;
|
||||||
|
private final BigDecimal price;
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
|
public Product(@JsonProperty("id") final UUID id, @JsonProperty("price") final BigDecimal price, @JsonProperty("name") final String name) {
|
||||||
|
this.id = id;
|
||||||
|
this.price = price;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getPrice() {
|
||||||
|
return price;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
Product product = (Product) o;
|
||||||
|
return Objects.equals(id, product.id) && Objects.equals(price, product.price) && Objects.equals(name, product.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(id, price, name);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.baeldung.dddhexagonalspring.domain.repository;
|
||||||
|
|
||||||
|
import com.baeldung.dddhexagonalspring.domain.Order;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public interface OrderRepository {
|
||||||
|
Optional<Order> findById(UUID id);
|
||||||
|
|
||||||
|
void save(Order order);
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package com.baeldung.dddhexagonalspring.domain.service;
|
||||||
|
|
||||||
|
import com.baeldung.dddhexagonalspring.domain.Order;
|
||||||
|
import com.baeldung.dddhexagonalspring.domain.Product;
|
||||||
|
import com.baeldung.dddhexagonalspring.domain.repository.OrderRepository;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class DomainOrderService implements OrderService {
|
||||||
|
|
||||||
|
private final OrderRepository orderRepository;
|
||||||
|
|
||||||
|
public DomainOrderService(final OrderRepository orderRepository) {
|
||||||
|
this.orderRepository = orderRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UUID createOrder(final Product product) {
|
||||||
|
final Order order = new Order(UUID.randomUUID(), product);
|
||||||
|
orderRepository.save(order);
|
||||||
|
|
||||||
|
return order.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addProduct(final UUID id, final Product product) {
|
||||||
|
final Order order = getOrder(id);
|
||||||
|
order.addOrder(product);
|
||||||
|
|
||||||
|
orderRepository.save(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void completeOrder(final UUID id) {
|
||||||
|
final Order order = getOrder(id);
|
||||||
|
order.complete();
|
||||||
|
|
||||||
|
orderRepository.save(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteProduct(final UUID id, final UUID productId) {
|
||||||
|
final Order order = getOrder(id);
|
||||||
|
order.removeOrder(productId);
|
||||||
|
|
||||||
|
orderRepository.save(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Order getOrder(UUID id) {
|
||||||
|
return orderRepository
|
||||||
|
.findById(id)
|
||||||
|
.orElseThrow(() -> new RuntimeException("Order with given id doesn't exist"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.baeldung.dddhexagonalspring.domain.service;
|
||||||
|
|
||||||
|
import com.baeldung.dddhexagonalspring.domain.Product;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public interface OrderService {
|
||||||
|
UUID createOrder(Product product);
|
||||||
|
|
||||||
|
void addProduct(UUID id, Product product);
|
||||||
|
|
||||||
|
void completeOrder(UUID id);
|
||||||
|
|
||||||
|
void deleteProduct(UUID id, UUID productId);
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.baeldung.dddhexagonalspring.infrastracture.configuration;
|
||||||
|
|
||||||
|
import com.baeldung.dddhexagonalspring.DomainLayerApplication;
|
||||||
|
import com.baeldung.dddhexagonalspring.domain.repository.OrderRepository;
|
||||||
|
import com.baeldung.dddhexagonalspring.domain.service.DomainOrderService;
|
||||||
|
import com.baeldung.dddhexagonalspring.domain.service.OrderService;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@ComponentScan(basePackageClasses = DomainLayerApplication.class)
|
||||||
|
public class BeanConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
OrderService orderService(final OrderRepository orderRepository) {
|
||||||
|
return new DomainOrderService(orderRepository);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.baeldung.dddhexagonalspring.infrastracture.configuration;
|
||||||
|
|
||||||
|
import com.baeldung.dddhexagonalspring.infrastracture.repository.SpringDataOrderRepository;
|
||||||
|
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
|
||||||
|
|
||||||
|
@EnableMongoRepositories(basePackageClasses = SpringDataOrderRepository.class)
|
||||||
|
public class MongoDBConfiguration {
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.baeldung.dddhexagonalspring.infrastracture.repository;
|
||||||
|
|
||||||
|
import com.baeldung.dddhexagonalspring.domain.Order;
|
||||||
|
import com.baeldung.dddhexagonalspring.domain.repository.OrderRepository;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MongoDbOrderRepository implements OrderRepository {
|
||||||
|
|
||||||
|
private final SpringDataOrderRepository orderRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public MongoDbOrderRepository(final SpringDataOrderRepository orderRepository) {
|
||||||
|
this.orderRepository = orderRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Order> findById(final UUID id) {
|
||||||
|
return orderRepository.findById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(final Order order) {
|
||||||
|
orderRepository.save(order);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.baeldung.dddhexagonalspring.infrastracture.repository;
|
||||||
|
|
||||||
|
import com.baeldung.dddhexagonalspring.domain.Order;
|
||||||
|
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface SpringDataOrderRepository extends MongoRepository<Order, UUID> {
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
spring.data.mongodb.host=localhost
|
||||||
|
spring.data.mongodb.port=27017
|
||||||
|
spring.data.mongodb.database=order-database
|
||||||
|
spring.data.mongodb.username=order
|
||||||
|
spring.data.mongodb.password=order
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.baeldung.dddhexagonalspring.domain;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class OrderProvider {
|
||||||
|
public static Order getCreatedOrder() {
|
||||||
|
return new Order(UUID.randomUUID(), new Product(UUID.randomUUID(), BigDecimal.TEN, "productName"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Order getCompletedOrder() {
|
||||||
|
final Order order = getCreatedOrder();
|
||||||
|
order.complete();
|
||||||
|
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package com.baeldung.dddhexagonalspring.domain;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.function.Executable;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
class OrderUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldCompleteOrder_thenChangeStatus() {
|
||||||
|
final Order order = OrderProvider.getCreatedOrder();
|
||||||
|
|
||||||
|
order.complete();
|
||||||
|
|
||||||
|
assertEquals(OrderStatus.COMPLETED, order.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldAddProduct_thenUpdatePrice() {
|
||||||
|
final Order order = OrderProvider.getCreatedOrder();
|
||||||
|
final int orderOriginalProductSize = order
|
||||||
|
.getOrderItems()
|
||||||
|
.size();
|
||||||
|
final BigDecimal orderOriginalPrice = order.getPrice();
|
||||||
|
final Product productToAdd = new Product(UUID.randomUUID(), new BigDecimal("20"), "secondProduct");
|
||||||
|
|
||||||
|
order.addOrder(productToAdd);
|
||||||
|
|
||||||
|
assertEquals(orderOriginalProductSize + 1, order
|
||||||
|
.getOrderItems()
|
||||||
|
.size());
|
||||||
|
assertEquals(orderOriginalPrice.add(productToAdd.getPrice()), order.getPrice());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldAddProduct_thenThrowException() {
|
||||||
|
final Order order = OrderProvider.getCompletedOrder();
|
||||||
|
final Product productToAdd = new Product(UUID.randomUUID(), new BigDecimal("20"), "secondProduct");
|
||||||
|
|
||||||
|
final Executable executable = () -> order.addOrder(productToAdd);
|
||||||
|
|
||||||
|
Assertions.assertThrows(DomainException.class, executable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRemoveProduct_thenUpdatePrice() {
|
||||||
|
final Order order = OrderProvider.getCreatedOrder();
|
||||||
|
final UUID productId = order
|
||||||
|
.getOrderItems()
|
||||||
|
.get(0)
|
||||||
|
.getProductId();
|
||||||
|
|
||||||
|
order.removeOrder(productId);
|
||||||
|
|
||||||
|
assertEquals(0, order
|
||||||
|
.getOrderItems()
|
||||||
|
.size());
|
||||||
|
assertEquals(BigDecimal.ZERO, order.getPrice());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package com.baeldung.dddhexagonalspring.domain.service;
|
||||||
|
|
||||||
|
import com.baeldung.dddhexagonalspring.domain.Order;
|
||||||
|
import com.baeldung.dddhexagonalspring.domain.OrderProvider;
|
||||||
|
import com.baeldung.dddhexagonalspring.domain.Product;
|
||||||
|
import com.baeldung.dddhexagonalspring.domain.repository.OrderRepository;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.function.Executable;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
class DomainOrderServiceUnitTest {
|
||||||
|
|
||||||
|
private OrderRepository orderRepository;
|
||||||
|
private DomainOrderService tested;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
orderRepository = mock(OrderRepository.class);
|
||||||
|
tested = new DomainOrderService(orderRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldCreateOrder_thenSaveIt() {
|
||||||
|
final Product product = new Product(UUID.randomUUID(), BigDecimal.TEN, "productName");
|
||||||
|
|
||||||
|
final UUID id = tested.createOrder(product);
|
||||||
|
|
||||||
|
verify(orderRepository).save(any(Order.class));
|
||||||
|
assertNotNull(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldAddProduct_thenSaveOrder() {
|
||||||
|
final Order order = spy(OrderProvider.getCreatedOrder());
|
||||||
|
final Product product = new Product(UUID.randomUUID(), BigDecimal.TEN, "test");
|
||||||
|
when(orderRepository.findById(order.getId())).thenReturn(Optional.of(order));
|
||||||
|
|
||||||
|
tested.addProduct(order.getId(), product);
|
||||||
|
|
||||||
|
verify(orderRepository).save(order);
|
||||||
|
verify(order).addOrder(product);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldAddProduct_thenThrowException() {
|
||||||
|
final Product product = new Product(UUID.randomUUID(), BigDecimal.TEN, "test");
|
||||||
|
final UUID id = UUID.randomUUID();
|
||||||
|
when(orderRepository.findById(id)).thenReturn(Optional.empty());
|
||||||
|
|
||||||
|
final Executable executable = () -> tested.addProduct(id, product);
|
||||||
|
|
||||||
|
verify(orderRepository, times(0)).save(any(Order.class));
|
||||||
|
assertThrows(RuntimeException.class, executable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldCompleteOrder_thenSaveIt() {
|
||||||
|
final Order order = spy(OrderProvider.getCreatedOrder());
|
||||||
|
when(orderRepository.findById(order.getId())).thenReturn(Optional.of(order));
|
||||||
|
|
||||||
|
tested.completeOrder(order.getId());
|
||||||
|
|
||||||
|
verify(orderRepository).save(any(Order.class));
|
||||||
|
verify(order).complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldDeleteProduct_thenSaveOrder() {
|
||||||
|
final Order order = spy(OrderProvider.getCreatedOrder());
|
||||||
|
final UUID productId = order
|
||||||
|
.getOrderItems()
|
||||||
|
.get(0)
|
||||||
|
.getProductId();
|
||||||
|
|
||||||
|
when(orderRepository.findById(order.getId())).thenReturn(Optional.of(order));
|
||||||
|
|
||||||
|
tested.deleteProduct(order.getId(), productId);
|
||||||
|
|
||||||
|
verify(orderRepository).save(order);
|
||||||
|
verify(order).removeOrder(productId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package com.baeldung.dddhexagonalspring.infrastracture.repository;
|
||||||
|
|
||||||
|
import com.baeldung.dddhexagonalspring.domain.Order;
|
||||||
|
import com.baeldung.dddhexagonalspring.domain.Product;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
class MongoDbOrderRepositoryUnitTest {
|
||||||
|
|
||||||
|
private SpringDataOrderRepository springDataOrderRepository;
|
||||||
|
private MongoDbOrderRepository tested;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp(){
|
||||||
|
springDataOrderRepository = mock(SpringDataOrderRepository.class);
|
||||||
|
|
||||||
|
tested = new MongoDbOrderRepository(springDataOrderRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldFindById_thenReturnOrder() {
|
||||||
|
final UUID id = UUID.randomUUID();
|
||||||
|
final Order order = createOrder(id);
|
||||||
|
when(springDataOrderRepository.findById(id)).thenReturn(Optional.of(order));
|
||||||
|
|
||||||
|
final Optional<Order> result = tested.findById(id);
|
||||||
|
|
||||||
|
assertEquals(order, result.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldSaveOrder_viaSpringDataOrderRepository() {
|
||||||
|
final UUID id = UUID.randomUUID();
|
||||||
|
final Order order = createOrder(id);
|
||||||
|
|
||||||
|
tested.save(order);
|
||||||
|
|
||||||
|
verify(springDataOrderRepository).save(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Order createOrder(UUID id) {
|
||||||
|
return new Order(id, new Product(UUID.randomUUID(), BigDecimal.TEN, "product"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
## Setup DDD Hexagonal Spring Application
|
||||||
|
|
||||||
|
To run this project, follow these steps:
|
||||||
|
|
||||||
|
* Run the application database by executing `docker-compose up` in this directory.
|
||||||
|
* Launch the Spring Boot Application (DomainLayerApplication).
|
||||||
|
* By default, application will connect to this database (configuration in *ddd-layers.properties*)
|
|
@ -0,0 +1,14 @@
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
order-mongo-database:
|
||||||
|
image: mongo:3.4.13
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 27017:27017
|
||||||
|
environment:
|
||||||
|
MONGO_INITDB_ROOT_USERNAME: admin
|
||||||
|
MONGO_INITDB_ROOT_PASSWORD: admin
|
||||||
|
MONGO_INITDB_DATABASE: order-database
|
||||||
|
volumes:
|
||||||
|
- ./mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro
|
|
@ -0,0 +1,12 @@
|
||||||
|
db.createUser(
|
||||||
|
{
|
||||||
|
user: "order",
|
||||||
|
pwd: "order",
|
||||||
|
roles: [
|
||||||
|
{
|
||||||
|
role: "readWrite",
|
||||||
|
db: "order-database"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
);
|
Loading…
Reference in New Issue