Java 26392 Move modules to patterns-module (#15060)

* JAVA-26392 Move axon module and ddd module to patterns-modules

* JAVA-26292 Move ddd-contexts to patterns-module
This commit is contained in:
anuragkumawat
2023-10-27 13:27:32 +03:00
committed by GitHub
parent 16b931c4df
commit 1028ab97c4
146 changed files with 23 additions and 28 deletions
+9
View File
@@ -0,0 +1,9 @@
## Domain-driven Design (DDD)
This module contains articles about Domain-driven Design (DDD)
### Relevant articles
- [Persisting DDD Aggregates](https://www.baeldung.com/spring-persisting-ddd-aggregates)
- [Double Dispatch in DDD](https://www.baeldung.com/ddd-double-dispatch)
- [Organizing Layers Using Hexagonal Architecture, DDD, and Spring](https://www.baeldung.com/hexagonal-architecture-ddd-spring)
+99
View File
@@ -0,0 +1,99 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>ddd</artifactId>
<name>ddd</name>
<packaging>jar</packaging>
<description>DDD series examples</description>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../../parent-boot-2</relativePath>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>${junit-jupiter.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<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>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra</artifactId>
</dependency>
<dependency>
<groupId>org.joda</groupId>
<artifactId>joda-money</artifactId>
<version>${joda-money.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<joda-money.version>1.0.1</joda-money.version>
</properties>
</project>
@@ -0,0 +1,12 @@
package com.baeldung.ddd;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackages = "com.baeldung.ddd.order")
public class PersistingDddAggregatesApplication {
public static void main(String[] args) {
SpringApplication.run(PersistingDddAggregatesApplication.class, args);
}
}
@@ -0,0 +1,52 @@
package com.baeldung.ddd.order;
import java.util.ArrayList;
import java.util.List;
import org.joda.money.Money;
public class Order {
private final List<OrderLine> orderLines;
private Money totalCost;
public Order(List<OrderLine> orderLines) {
checkNotNull(orderLines);
if (orderLines.isEmpty()) {
throw new IllegalArgumentException("Order must have at least one order line item");
}
this.orderLines = new ArrayList<>(orderLines);
totalCost = calculateTotalCost();
}
public void addLineItem(OrderLine orderLine) {
checkNotNull(orderLine);
orderLines.add(orderLine);
totalCost = totalCost.plus(orderLine.cost());
}
public List<OrderLine> getOrderLines() {
return new ArrayList<>(orderLines);
}
public void removeLineItem(int line) {
OrderLine removedLine = orderLines.remove(line);
totalCost = totalCost.minus(removedLine.cost());
}
public Money totalCost() {
return totalCost;
}
private Money calculateTotalCost() {
return orderLines.stream()
.map(OrderLine::cost)
.reduce(Money::plus)
.get();
}
private static void checkNotNull(Object par) {
if (par == null) {
throw new NullPointerException("Parameter cannot be null");
}
}
}
@@ -0,0 +1,67 @@
package com.baeldung.ddd.order;
import org.joda.money.Money;
public class OrderLine {
private final Product product;
private final int quantity;
public OrderLine(Product product, int quantity) {
super();
this.product = product;
this.quantity = quantity;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
OrderLine other = (OrderLine) obj;
if (product == null) {
if (other.product != null) {
return false;
}
} else if (!product.equals(other.product)) {
return false;
}
if (quantity != other.quantity) {
return false;
}
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((product == null) ? 0 : product.hashCode());
result = prime * result + quantity;
return result;
}
@Override
public String toString() {
return "OrderLine [product=" + product + ", quantity=" + quantity + "]";
}
Money cost() {
return product.getPrice()
.multipliedBy(quantity);
}
Product getProduct() {
return product;
}
int getQuantity() {
return quantity;
}
}
@@ -0,0 +1,52 @@
package com.baeldung.ddd.order;
import org.joda.money.Money;
public class Product {
private final Money price;
public Product(Money price) {
super();
this.price = price;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Product other = (Product) obj;
if (price == null) {
if (other.price != null) {
return false;
}
} else if (!price.equals(other.price)) {
return false;
}
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((price == null) ? 0 : price.hashCode());
return result;
}
@Override
public String toString() {
return "Product [price=" + price + "]";
}
Money getPrice() {
return price;
}
}
@@ -0,0 +1,46 @@
package com.baeldung.ddd.order.config;
import org.bson.Document;
import org.joda.money.CurrencyUnit;
import org.joda.money.Money;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import java.math.BigDecimal;
import java.util.Collections;
@Configuration
public class CustomMongoConfiguration {
@Bean
public MongoCustomConversions customConversions() {
return new MongoCustomConversions(Collections.singletonList(DocumentToMoneyConverter.INSTANCE));
}
@ReadingConverter
enum DocumentToMoneyConverter implements Converter<Document, Money> {
INSTANCE;
@Override
public Money convert(Document source) {
Document money = source.get("money", Document.class);
return Money.of(getCurrency(money), getAmount(money));
}
private CurrencyUnit getCurrency(Document money) {
Document currency = money.get("currency", Document.class);
String currencyCode = currency.getString("code");
return CurrencyUnit.of(currencyCode);
}
private BigDecimal getAmount(Document money) {
String amount = money.getString("amount");
return BigDecimal.valueOf(Double.parseDouble(amount));
}
}
}
@@ -0,0 +1,15 @@
package com.baeldung.ddd.order.doubledispatch;
import org.joda.money.CurrencyUnit;
import org.joda.money.Money;
public class AmountBasedDiscountPolicy implements DiscountPolicy {
@Override
public double discount(Order order) {
if (order.totalCost()
.isGreaterThan(Money.of(CurrencyUnit.USD, 500.00))) {
return 0.10;
} else
return 0;
}
}
@@ -0,0 +1,5 @@
package com.baeldung.ddd.order.doubledispatch;
public interface DiscountPolicy {
double discount(Order order);
}
@@ -0,0 +1,8 @@
package com.baeldung.ddd.order.doubledispatch;
public class FlatDiscountPolicy implements DiscountPolicy {
@Override
public double discount(Order order) {
return 0.01;
}
}
@@ -0,0 +1,29 @@
package com.baeldung.ddd.order.doubledispatch;
import java.math.RoundingMode;
import java.util.List;
import org.joda.money.Money;
import com.baeldung.ddd.order.OrderLine;
import com.baeldung.ddd.order.doubledispatch.visitor.OrderVisitor;
import com.baeldung.ddd.order.doubledispatch.visitor.Visitable;
public class Order extends com.baeldung.ddd.order.Order implements Visitable<OrderVisitor> {
public Order(List<OrderLine> orderLines) {
super(orderLines);
}
public Money totalCost(SpecialDiscountPolicy discountPolicy) {
return totalCost().multipliedBy(1 - applyDiscountPolicy(discountPolicy), RoundingMode.HALF_UP);
}
protected double applyDiscountPolicy(SpecialDiscountPolicy discountPolicy) {
return discountPolicy.discount(this);
}
@Override
public void accept(OrderVisitor visitor) {
visitor.visit(this);
}
}
@@ -0,0 +1,5 @@
package com.baeldung.ddd.order.doubledispatch;
public interface SpecialDiscountPolicy extends DiscountPolicy {
double discount(SpecialOrder order);
}
@@ -0,0 +1,36 @@
package com.baeldung.ddd.order.doubledispatch;
import java.util.List;
import com.baeldung.ddd.order.OrderLine;
import com.baeldung.ddd.order.doubledispatch.visitor.OrderVisitor;
public class SpecialOrder extends Order {
private boolean eligibleForExtraDiscount;
public SpecialOrder(List<OrderLine> orderLines) {
super(orderLines);
this.eligibleForExtraDiscount = false;
}
public SpecialOrder(List<OrderLine> orderLines, boolean eligibleForSpecialDiscount) {
super(orderLines);
this.eligibleForExtraDiscount = eligibleForSpecialDiscount;
}
public boolean isEligibleForExtraDiscount() {
return eligibleForExtraDiscount;
}
@Override
protected double applyDiscountPolicy(SpecialDiscountPolicy discountPolicy) {
return discountPolicy.discount(this);
}
@Override
public void accept(OrderVisitor visitor) {
visitor.visit(this);
}
}
@@ -0,0 +1,24 @@
package com.baeldung.ddd.order.doubledispatch.visitor;
import com.baeldung.ddd.order.doubledispatch.Order;
import com.baeldung.ddd.order.doubledispatch.SpecialOrder;
public class HtmlOrderViewCreator implements OrderVisitor {
private String html;
public String getHtml() {
return html;
}
@Override
public void visit(Order order) {
html = String.format("<p>Regular order total cost: %s</p>", order.totalCost());
}
@Override
public void visit(SpecialOrder order) {
html = String.format("<h1>Special Order</h1><p>total cost: %s</p>", order.totalCost());
}
}
@@ -0,0 +1,9 @@
package com.baeldung.ddd.order.doubledispatch.visitor;
import com.baeldung.ddd.order.doubledispatch.Order;
import com.baeldung.ddd.order.doubledispatch.SpecialOrder;
public interface OrderVisitor {
void visit(Order order);
void visit(SpecialOrder order);
}
@@ -0,0 +1,5 @@
package com.baeldung.ddd.order.doubledispatch.visitor;
public interface Visitable<V> {
void accept(V visitor);
}
@@ -0,0 +1,111 @@
package com.baeldung.ddd.order.jpa;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "order_table")
class JpaOrder {
private String currencyUnit;
@Id
@GeneratedValue
private Long id;
@ElementCollection(fetch = FetchType.EAGER)
private final List<JpaOrderLine> orderLines;
private BigDecimal totalCost;
JpaOrder() {
totalCost = null;
orderLines = new ArrayList<>();
}
JpaOrder(List<JpaOrderLine> orderLines) {
checkNotNull(orderLines);
if (orderLines.isEmpty()) {
throw new IllegalArgumentException("Order must have at least one order line item");
}
this.orderLines = new ArrayList<>(orderLines);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
JpaOrder other = (JpaOrder) obj;
if (id == null) {
if (other.id != null) {
return false;
}
} else if (!id.equals(other.id)) {
return false;
}
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public String toString() {
return "JpaOrder [currencyUnit=" + currencyUnit + ", id=" + id + ", orderLines=" + orderLines + ", totalCost=" + totalCost + "]";
}
void addLineItem(JpaOrderLine orderLine) {
checkNotNull(orderLine);
orderLines.add(orderLine);
}
String getCurrencyUnit() {
return currencyUnit;
}
Long getId() {
return id;
}
List<JpaOrderLine> getOrderLines() {
return new ArrayList<>(orderLines);
}
BigDecimal getTotalCost() {
return totalCost;
}
void removeLineItem(int line) {
orderLines.remove(line);
}
void setCurrencyUnit(String currencyUnit) {
this.currencyUnit = currencyUnit;
}
void setTotalCost(BigDecimal totalCost) {
this.totalCost = totalCost;
}
private static void checkNotNull(Object par) {
if (par == null) {
throw new NullPointerException("Parameter cannot be null");
}
}
}
@@ -0,0 +1,70 @@
package com.baeldung.ddd.order.jpa;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
@Embeddable
class JpaOrderLine {
@Embedded
private final JpaProduct product;
private final int quantity;
JpaOrderLine() {
quantity = 0;
product = null;
}
JpaOrderLine(JpaProduct product, int quantity) {
super();
this.product = product;
this.quantity = quantity;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
JpaOrderLine other = (JpaOrderLine) obj;
if (product == null) {
if (other.product != null) {
return false;
}
} else if (!product.equals(other.product)) {
return false;
}
if (quantity != other.quantity) {
return false;
}
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((product == null) ? 0 : product.hashCode());
result = prime * result + quantity;
return result;
}
@Override
public String toString() {
return "JpaOrderLine [product=" + product + ", quantity=" + quantity + "]";
}
JpaProduct getProduct() {
return product;
}
int getQuantity() {
return quantity;
}
}
@@ -0,0 +1,7 @@
package com.baeldung.ddd.order.jpa;
import org.springframework.data.jpa.repository.JpaRepository;
public interface JpaOrderRepository extends JpaRepository<JpaOrder, Long> {
}
@@ -0,0 +1,79 @@
package com.baeldung.ddd.order.jpa;
import java.math.BigDecimal;
import javax.persistence.Embeddable;
@Embeddable
class JpaProduct {
private String currencyUnit;
private BigDecimal price;
public JpaProduct() {
}
public JpaProduct(BigDecimal price, String currencyUnit) {
super();
this.price = price;
this.currencyUnit = currencyUnit;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
JpaProduct other = (JpaProduct) obj;
if (currencyUnit == null) {
if (other.currencyUnit != null) {
return false;
}
} else if (!currencyUnit.equals(other.currencyUnit)) {
return false;
}
if (price == null) {
if (other.price != null) {
return false;
}
} else if (!price.equals(other.price)) {
return false;
}
return true;
}
public String getCurrencyUnit() {
return currencyUnit;
}
public BigDecimal getPrice() {
return price;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((currencyUnit == null) ? 0 : currencyUnit.hashCode());
result = prime * result + ((price == null) ? 0 : price.hashCode());
return result;
}
public void setCurrencyUnit(String currencyUnit) {
this.currencyUnit = currencyUnit;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
@Override
public String toString() {
return "JpaProduct [currencyUnit=" + currencyUnit + ", price=" + price + "]";
}
}
@@ -0,0 +1,9 @@
package com.baeldung.ddd.order.mongo;
import org.springframework.data.mongodb.repository.MongoRepository;
import com.baeldung.ddd.order.Order;
public interface OrderMongoRepository extends MongoRepository<Order, String> {
}
@@ -0,0 +1,37 @@
package com.baeldung.dddhexagonalspring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.PropertySource;
import com.baeldung.dddhexagonalspring.application.cli.CliOrderController;
@SpringBootApplication
@PropertySource(value = { "classpath:ddd-layers.properties" })
public class DomainLayerApplication implements CommandLineRunner {
public static void main(final String[] args) {
SpringApplication application = new SpringApplication(DomainLayerApplication.class);
// uncomment to run just the console application
// application.setWebApplicationType(WebApplicationType.NONE);
application.run(args);
}
@Autowired
public CliOrderController orderController;
@Autowired
public ConfigurableApplicationContext context;
@Override
public void run(String... args) throws Exception {
orderController.createCompleteOrder();
orderController.createIncompleteOrder();
// uncomment to stop the context when execution is done
// context.close();
}
}
@@ -0,0 +1,47 @@
package com.baeldung.dddhexagonalspring.application.cli;
import java.math.BigDecimal;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.baeldung.dddhexagonalspring.domain.Product;
import com.baeldung.dddhexagonalspring.domain.service.OrderService;
@Component
public class CliOrderController {
private static final Logger LOG = LoggerFactory.getLogger(CliOrderController.class);
private final OrderService orderService;
@Autowired
public CliOrderController(OrderService orderService) {
this.orderService = orderService;
}
public void createCompleteOrder() {
LOG.info("<<Create complete order>>");
UUID orderId = createOrder();
orderService.completeOrder(orderId);
}
public void createIncompleteOrder() {
LOG.info("<<Create incomplete order>>");
UUID orderId = createOrder();
}
private UUID createOrder() {
LOG.info("Placing a new order with two products");
Product mobilePhone = new Product(UUID.randomUUID(), BigDecimal.valueOf(200), "mobile");
Product razor = new Product(UUID.randomUUID(), BigDecimal.valueOf(50), "razor");
LOG.info("Creating order with mobile phone");
UUID orderId = orderService.createOrder(mobilePhone);
LOG.info("Adding a razor to the order");
orderService.addProduct(orderId, razor);
return orderId;
}
}
@@ -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,45 @@
package com.baeldung.dddhexagonalspring.application.rest;
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,7 @@
package com.baeldung.dddhexagonalspring.domain;
class DomainException extends RuntimeException {
DomainException(final String message) {
super(message);
}
}
@@ -0,0 +1,96 @@
package com.baeldung.dddhexagonalspring.domain;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
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);
}
@Override
public int hashCode() {
return Objects.hash(id, orderItems, price, status);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof Order))
return false;
Order other = (Order) obj;
return Objects.equals(id, other.id) && Objects.equals(orderItems, other.orderItems) && Objects.equals(price, other.price) && status == other.status;
}
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);
}
}

Some files were not shown because too many files have changed in this diff Show More