BAEL-2275: Add OrderItem

This commit is contained in:
Lukasz Rys 2019-11-17 20:55:03 +01:00
parent fbd2c58844
commit 3cde05a6ed
15 changed files with 127 additions and 73 deletions

View File

@ -5,9 +5,9 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;
@SpringBootApplication
@PropertySource(value={"classpath:ddd-layers.properties"})
@PropertySource(value = { "classpath:ddd-layers.properties" })
public class DomainLayerApplication {
public static void main(String[] args) {
public static void main(final String[] args) {
SpringApplication.run(DomainLayerApplication.class, args);
}
}

View File

@ -9,6 +9,8 @@ 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 {
@ -33,8 +35,8 @@ public class OrderController {
}
@DeleteMapping(value = "/{id}/products", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
void deleteProduct(@PathVariable final ObjectId id, @RequestParam final String name) {
orderService.deleteProduct(id, name);
void deleteProduct(@PathVariable final ObjectId id, @RequestParam final UUID productId) {
orderService.deleteProduct(id, productId);
}
@PostMapping("/{id}/complete")

View File

@ -10,7 +10,7 @@ public class CreateOrderRequest {
@NotNull private Product product;
@JsonCreator
public CreateOrderRequest(@JsonProperty("product") @NotNull Product product) {
public CreateOrderRequest(@JsonProperty("product") @NotNull final Product product) {
this.product = product;
}

View File

@ -5,7 +5,7 @@ import org.bson.types.ObjectId;
public class CreateOrderResponse {
private final String id;
public CreateOrderResponse(String id) {
public CreateOrderResponse(final String id) {
this.id = id;
}

View File

@ -0,0 +1,7 @@
package com.baeldung.ddd.layers.domain;
class DomainException extends RuntimeException {
DomainException(final String message) {
super(message);
}
}

View File

@ -1,22 +1,22 @@
package com.baeldung.ddd.layers.domain;
import com.baeldung.ddd.layers.domain.exception.DomainException;
import org.bson.types.ObjectId;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
public class Order {
private final ObjectId id;
private OrderStatus status;
private List<Product> products;
private List<OrderItem> orderItems;
private BigDecimal price;
public Order(final ObjectId id, final Product product) {
this.id = id;
this.products = new ArrayList<>(Collections.singletonList(product));
this.orderItems = new ArrayList<>(Collections.singletonList(new OrderItem(product)));
this.status = OrderStatus.CREATED;
this.price = product.getPrice();
}
@ -26,29 +26,30 @@ public class Order {
this.status = OrderStatus.COMPLETED;
}
public void addProduct(final Product product) {
public void addOrder(final Product product) {
validateState();
validateProduct(product);
products.add(product);
orderItems.add(new OrderItem(product));
price = price.add(product.getPrice());
}
public void removeProduct(final String name) {
public void removeOrder(final UUID id) {
validateState();
final Product product = getProduct(name);
products.remove(product);
final OrderItem orderItem = getOrderItem(id);
orderItems.remove(orderItem);
price = price.subtract(product.getPrice());
price = price.subtract(orderItem.getPrice());
}
private Product getProduct(String name) {
return products
private OrderItem getOrderItem(final UUID id) {
return orderItems
.stream()
.filter(product -> product
.getName()
.equals(name))
.filter(orderItem -> orderItem
.getProduct()
.getId()
.equals(id))
.findFirst()
.orElseThrow(() -> new DomainException("Product with " + name + " doesn't exist."));
.orElseThrow(() -> new DomainException("Product with " + id + " doesn't exist."));
}
private void validateState() {
@ -71,11 +72,11 @@ public class Order {
return status;
}
public List<Product> getProducts() {
return Collections.unmodifiableList(products);
}
public BigDecimal getPrice() {
return price;
}
public List<OrderItem> getOrderItems() {
return orderItems;
}
}

View File

@ -0,0 +1,33 @@
package com.baeldung.ddd.layers.domain;
import java.math.BigDecimal;
import java.util.Objects;
public class OrderItem {
private final Product product;
public OrderItem(final Product product) {
this.product = product;
}
public BigDecimal getPrice() {
return product.getPrice();
}
public Product getProduct() {
return product;
}
@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(product, orderItem.product);
}
@Override
public int hashCode() {
return Objects.hash(product);
}
}

View File

@ -5,13 +5,16 @@ 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("price") final BigDecimal price, @JsonProperty("name") final String name) {
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;
}
@ -24,20 +27,20 @@ public class Product {
return name;
}
public UUID getId() {
return id;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final Product product = (Product) o;
return Objects.equals(price, product.price) && Objects.equals(name, product.name);
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(price, name);
return Objects.hash(id, price, name);
}
}

View File

@ -1,7 +0,0 @@
package com.baeldung.ddd.layers.domain.exception;
public class DomainException extends RuntimeException {
public DomainException(final String message) {
super(message);
}
}

View File

@ -5,11 +5,13 @@ import com.baeldung.ddd.layers.domain.Product;
import com.baeldung.ddd.layers.domain.repository.OrderRepository;
import org.bson.types.ObjectId;
import java.util.UUID;
public class DomainOrderService implements OrderService {
private final OrderRepository orderRepository;
public DomainOrderService(OrderRepository orderRepository) {
public DomainOrderService(final OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
@ -22,15 +24,15 @@ public class DomainOrderService implements OrderService {
}
@Override
public void addProduct(ObjectId id, Product product) {
public void addProduct(final ObjectId id, final Product product) {
final Order order = getOrder(id);
order.addProduct(product);
order.addOrder(product);
orderRepository.save(order);
}
@Override
public void completeOrder(ObjectId id) {
public void completeOrder(final ObjectId id) {
final Order order = getOrder(id);
order.complete();
@ -38,9 +40,9 @@ public class DomainOrderService implements OrderService {
}
@Override
public void deleteProduct(ObjectId id, String name) {
public void deleteProduct(final ObjectId id, final UUID productId) {
final Order order = getOrder(id);
order.removeProduct(name);
order.removeOrder(productId);
orderRepository.save(order);
}

View File

@ -3,6 +3,8 @@ package com.baeldung.ddd.layers.domain.service;
import com.baeldung.ddd.layers.domain.Product;
import org.bson.types.ObjectId;
import java.util.UUID;
public interface OrderService {
ObjectId createOrder(final Product product);
@ -10,5 +12,5 @@ public interface OrderService {
void completeOrder(ObjectId id);
void deleteProduct(ObjectId id, String name);
void deleteProduct(ObjectId id, UUID productId);
}

View File

@ -10,7 +10,7 @@ import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackageClasses = DomainLayerApplication.class)
public class DomainConfiguration {
public class BeanConfiguration {
@Bean
OrderService orderService(final OrderRepository orderRepository) {

View File

@ -3,10 +3,11 @@ package com.baeldung.ddd.layers.domain;
import org.bson.types.ObjectId;
import java.math.BigDecimal;
import java.util.UUID;
public class OrderProvider {
public static Order getCreatedOrder() {
return new Order(ObjectId.get(), new Product(BigDecimal.TEN, "productName"));
return new Order(ObjectId.get(), new Product(UUID.randomUUID(), BigDecimal.TEN, "productName"));
}
public static Order getCompletedOrder() {

View File

@ -1,13 +1,11 @@
package com.baeldung.ddd.layers.domain;
import com.baeldung.ddd.layers.domain.exception.DomainException;
import org.bson.types.ObjectId;
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.Arrays;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -25,22 +23,26 @@ class OrderUnitTest {
@Test
void shouldAddProduct_thenUpdatePrice() {
final Order order = OrderProvider.getCreatedOrder();
final int orderOriginalProductSize = order.getProducts().size();
final int orderOriginalProductSize = order
.getOrderItems()
.size();
final BigDecimal orderOriginalPrice = order.getPrice();
final Product productToAdd = new Product(new BigDecimal("20"), "secondProduct");
final Product productToAdd = new Product(UUID.randomUUID(), new BigDecimal("20"), "secondProduct");
order.addProduct(productToAdd);
order.addOrder(productToAdd);
assertEquals(orderOriginalProductSize + 1, order.getProducts().size());
assertEquals(orderOriginalProductSize + 1, order
.getOrderItems()
.size());
assertEquals(orderOriginalPrice.add(productToAdd.getPrice()), order.getPrice());
}
@Test
void shouldAddProduct_thenThrowException(){
void shouldAddProduct_thenThrowException() {
final Order order = OrderProvider.getCompletedOrder();
final Product productToAdd = new Product(new BigDecimal("20"), "secondProduct");
final Product productToAdd = new Product(UUID.randomUUID(), new BigDecimal("20"), "secondProduct");
final Executable executable = () -> order.addProduct(productToAdd);
final Executable executable = () -> order.addOrder(productToAdd);
Assertions.assertThrows(DomainException.class, executable);
}
@ -49,9 +51,15 @@ class OrderUnitTest {
void shouldRemoveProduct_thenUpdatePrice() {
final Order order = OrderProvider.getCreatedOrder();
order.removeProduct(order.getProducts().get(0).getName());
order.removeOrder(order
.getOrderItems()
.get(0)
.getProduct()
.getId());
assertEquals(0, order.getProducts().size());
assertEquals(0, order
.getOrderItems()
.size());
assertEquals(BigDecimal.ZERO, order.getPrice());
}
}

View File

@ -11,6 +11,7 @@ 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;
@ -30,7 +31,7 @@ class DomainOrderServiceUnitTest {
@Test
void shouldCreateOrder_thenSaveIt() {
final Product product = new Product(BigDecimal.TEN, "productName");
final Product product = new Product(UUID.randomUUID(), BigDecimal.TEN, "productName");
final ObjectId id = tested.createOrder(product);
@ -41,18 +42,18 @@ class DomainOrderServiceUnitTest {
@Test
void shouldAddProduct_thenSaveOrder() {
final Order order = spy(OrderProvider.getCreatedOrder());
final Product product = new Product(BigDecimal.TEN, "test");
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).addProduct(product);
verify(order).addOrder(product);
}
@Test
void shouldAddProduct_thenThrowException() {
final Product product = new Product(BigDecimal.TEN, "test");
final Product product = new Product(UUID.randomUUID(), BigDecimal.TEN, "test");
final ObjectId id = ObjectId.get();
when(orderRepository.findById(id)).thenReturn(Optional.empty());
@ -76,15 +77,16 @@ class DomainOrderServiceUnitTest {
@Test
void shouldDeleteProduct_thenSaveOrder() {
final Order order = spy(OrderProvider.getCreatedOrder());
final String productName = order
.getProducts()
final UUID productId = order
.getOrderItems()
.get(0)
.getName();
.getProduct()
.getId();
when(orderRepository.findById(order.getId())).thenReturn(Optional.of(order));
tested.deleteProduct(order.getId(), productName);
tested.deleteProduct(order.getId(), productId);
verify(orderRepository).save(order);
verify(order).removeProduct(productName);
verify(order).removeOrder(productId);
}
}