From 7ecab4f188e37714c448d72413958cfa882610c0 Mon Sep 17 00:00:00 2001 From: Junh-b Date: Sat, 31 May 2025 10:13:16 +0900 Subject: [PATCH 1/9] =?UTF-8?q?refactor:=20deprecated=EB=90=9C=20=EC=A3=BC?= =?UTF-8?q?=EB=AC=B8=ED=81=90=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 개발 버전에서 WaitingOrders, WaitingOrdersMaanger로 성공을 확인했기 때문에, 기존 deprecated 클래스를 제거합니다 --- .../order/queue/OrderQueueManager.java | 91 ------------------- .../order/queue/OrderQueueManagerPool.java | 33 ------- 2 files changed, 124 deletions(-) delete mode 100644 src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/order/queue/OrderQueueManager.java delete mode 100644 src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/order/queue/OrderQueueManagerPool.java diff --git a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/order/queue/OrderQueueManager.java b/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/order/queue/OrderQueueManager.java deleted file mode 100644 index 9d0a142c..00000000 --- a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/order/queue/OrderQueueManager.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.cleanengine.coin.order.adapter.out.persistentce.order.queue; - -import com.cleanengine.coin.order.domain.BuyOrder; -import com.cleanengine.coin.order.domain.Order; -import com.cleanengine.coin.order.domain.SellOrder; -import lombok.Getter; - -import java.util.concurrent.PriorityBlockingQueue; - -@Deprecated -@Getter -public class OrderQueueManager { - private final String ticker; - private final PriorityBlockingQueue marketSellOrderQueue; - private final PriorityBlockingQueue limitSellOrderQueue; - private final PriorityBlockingQueue marketBuyOrderQueue; - private final PriorityBlockingQueue limitBuyOrderQueue; - - public OrderQueueManager(String ticker) { - this.ticker = ticker; - this.marketSellOrderQueue = new PriorityBlockingQueue<>(); - this.limitSellOrderQueue = new PriorityBlockingQueue<>(); - this.marketBuyOrderQueue = new PriorityBlockingQueue<>(); - this.limitBuyOrderQueue = new PriorityBlockingQueue<>(); - } - - public void addOrder(Order order) { - if(order instanceof SellOrder) { - if(order.getIsMarketOrder()) { - marketSellOrderQueue.add((SellOrder) order); - } else { - limitSellOrderQueue.add((SellOrder) order); - } - } else { - if(order.getIsMarketOrder()) { - marketBuyOrderQueue.add((BuyOrder) order); - } else { - limitBuyOrderQueue.add((BuyOrder) order); - } - } - } - - public int getMarketSellOrderQueueSize() { - return marketSellOrderQueue.size(); - } - - public int getMarketBuyOrderQueueSize() { - return marketBuyOrderQueue.size(); - } - - public int getLimitSellOrderQueueSize() { - return limitSellOrderQueue.size(); - } - - public int getLimitBuyOrderQueueSize() { - return limitBuyOrderQueue.size(); - } - - public SellOrder getMarketSellOrder() { - return marketSellOrderQueue.peek(); - } - - public BuyOrder getMarketBuyOrder() { - return marketBuyOrderQueue.peek(); - } - - public SellOrder getLimitSellOrder() { - return limitSellOrderQueue.peek(); - } - - public BuyOrder getLimitBuyOrder() { - return limitBuyOrderQueue.peek(); - } - - public boolean removeOrderFromQueue(Order order) { - if (order instanceof SellOrder) { - if (order.getIsMarketOrder()) { - return marketSellOrderQueue.remove(order); - } else { - return limitSellOrderQueue.remove(order); - } - } else { - if (order.getIsMarketOrder()) { - return marketBuyOrderQueue.remove(order); - } else { - return limitBuyOrderQueue.remove(order); - } - } - } - -} diff --git a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/order/queue/OrderQueueManagerPool.java b/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/order/queue/OrderQueueManagerPool.java deleted file mode 100644 index cd394214..00000000 --- a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/order/queue/OrderQueueManagerPool.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.cleanengine.coin.order.adapter.out.persistentce.order.queue; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import java.util.HashMap; -import java.util.Optional; - -@Deprecated -@Slf4j -@Component -public class OrderQueueManagerPool { - private final HashMap orderQueueManagerMap = new HashMap<>(); - - public OrderQueueManager getOrderQueueManager(String ticker){ - if(!orderQueueManagerMap.containsKey(ticker)){ - addOrderQueueManager(ticker); - } - - Optional orderQueueManagerOpt = Optional.ofNullable(orderQueueManagerMap.get(ticker)); - if(orderQueueManagerOpt.isEmpty()){ - log.debug("OrderQueueManager not found. with " + ticker); - throw new RuntimeException("OrderQueueManager not found with " + ticker); - } - return orderQueueManagerOpt.get(); - } - - protected synchronized void addOrderQueueManager(String ticker){ - if(!orderQueueManagerMap.containsKey(ticker)){ - orderQueueManagerMap.put(ticker, new OrderQueueManager(ticker)); - } - } -} From ad164b8951927ce901a5a2cfe4a041b90ae1810b Mon Sep 17 00:00:00 2001 From: Junh-b Date: Sat, 31 May 2025 10:13:16 +0900 Subject: [PATCH 2/9] =?UTF-8?q?refactor:=20deprecated=EB=90=9C=20=EC=A3=BC?= =?UTF-8?q?=EB=AC=B8=ED=81=90=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 개발 버전에서 WaitingOrders, WaitingOrdersMaanger로 성공을 확인했기 때문에, 기존 deprecated 클래스를 제거합니다 --- .../order/queue/OrderQueueManager.java | 91 ------------------- .../order/queue/OrderQueueManagerPool.java | 33 ------- 2 files changed, 124 deletions(-) delete mode 100644 src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/order/queue/OrderQueueManager.java delete mode 100644 src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/order/queue/OrderQueueManagerPool.java diff --git a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/order/queue/OrderQueueManager.java b/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/order/queue/OrderQueueManager.java deleted file mode 100644 index 9d0a142c..00000000 --- a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/order/queue/OrderQueueManager.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.cleanengine.coin.order.adapter.out.persistentce.order.queue; - -import com.cleanengine.coin.order.domain.BuyOrder; -import com.cleanengine.coin.order.domain.Order; -import com.cleanengine.coin.order.domain.SellOrder; -import lombok.Getter; - -import java.util.concurrent.PriorityBlockingQueue; - -@Deprecated -@Getter -public class OrderQueueManager { - private final String ticker; - private final PriorityBlockingQueue marketSellOrderQueue; - private final PriorityBlockingQueue limitSellOrderQueue; - private final PriorityBlockingQueue marketBuyOrderQueue; - private final PriorityBlockingQueue limitBuyOrderQueue; - - public OrderQueueManager(String ticker) { - this.ticker = ticker; - this.marketSellOrderQueue = new PriorityBlockingQueue<>(); - this.limitSellOrderQueue = new PriorityBlockingQueue<>(); - this.marketBuyOrderQueue = new PriorityBlockingQueue<>(); - this.limitBuyOrderQueue = new PriorityBlockingQueue<>(); - } - - public void addOrder(Order order) { - if(order instanceof SellOrder) { - if(order.getIsMarketOrder()) { - marketSellOrderQueue.add((SellOrder) order); - } else { - limitSellOrderQueue.add((SellOrder) order); - } - } else { - if(order.getIsMarketOrder()) { - marketBuyOrderQueue.add((BuyOrder) order); - } else { - limitBuyOrderQueue.add((BuyOrder) order); - } - } - } - - public int getMarketSellOrderQueueSize() { - return marketSellOrderQueue.size(); - } - - public int getMarketBuyOrderQueueSize() { - return marketBuyOrderQueue.size(); - } - - public int getLimitSellOrderQueueSize() { - return limitSellOrderQueue.size(); - } - - public int getLimitBuyOrderQueueSize() { - return limitBuyOrderQueue.size(); - } - - public SellOrder getMarketSellOrder() { - return marketSellOrderQueue.peek(); - } - - public BuyOrder getMarketBuyOrder() { - return marketBuyOrderQueue.peek(); - } - - public SellOrder getLimitSellOrder() { - return limitSellOrderQueue.peek(); - } - - public BuyOrder getLimitBuyOrder() { - return limitBuyOrderQueue.peek(); - } - - public boolean removeOrderFromQueue(Order order) { - if (order instanceof SellOrder) { - if (order.getIsMarketOrder()) { - return marketSellOrderQueue.remove(order); - } else { - return limitSellOrderQueue.remove(order); - } - } else { - if (order.getIsMarketOrder()) { - return marketBuyOrderQueue.remove(order); - } else { - return limitBuyOrderQueue.remove(order); - } - } - } - -} diff --git a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/order/queue/OrderQueueManagerPool.java b/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/order/queue/OrderQueueManagerPool.java deleted file mode 100644 index cd394214..00000000 --- a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/order/queue/OrderQueueManagerPool.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.cleanengine.coin.order.adapter.out.persistentce.order.queue; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import java.util.HashMap; -import java.util.Optional; - -@Deprecated -@Slf4j -@Component -public class OrderQueueManagerPool { - private final HashMap orderQueueManagerMap = new HashMap<>(); - - public OrderQueueManager getOrderQueueManager(String ticker){ - if(!orderQueueManagerMap.containsKey(ticker)){ - addOrderQueueManager(ticker); - } - - Optional orderQueueManagerOpt = Optional.ofNullable(orderQueueManagerMap.get(ticker)); - if(orderQueueManagerOpt.isEmpty()){ - log.debug("OrderQueueManager not found. with " + ticker); - throw new RuntimeException("OrderQueueManager not found with " + ticker); - } - return orderQueueManagerOpt.get(); - } - - protected synchronized void addOrderQueueManager(String ticker){ - if(!orderQueueManagerMap.containsKey(ticker)){ - orderQueueManagerMap.put(ticker, new OrderQueueManager(ticker)); - } - } -} From 1894517072a74b3fe2708c73188143fb86a62e6d Mon Sep 17 00:00:00 2001 From: Junh-b Date: Mon, 2 Jun 2025 02:26:21 +0900 Subject: [PATCH 3/9] =?UTF-8?q?refactor:=20Order=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 공통 생성 로직을 super 클래스에 끌어올렸습니다. remainingSize 수정 메서드를 super 클래스에 끌어올렸습니다. validationError 기반으로 exception을 반환하는 기능을 super 클래스에 끌어올렸습니다. 상태변화 메서드에 대한 null체크를 추가했습니다. --- .../coin/order/domain/BuyOrder.java | 71 ++++++++----------- .../cleanengine/coin/order/domain/Order.java | 33 ++++++++- .../coin/order/domain/SellOrder.java | 51 +++---------- 3 files changed, 70 insertions(+), 85 deletions(-) diff --git a/src/main/java/com/cleanengine/coin/order/domain/BuyOrder.java b/src/main/java/com/cleanengine/coin/order/domain/BuyOrder.java index 5966a750..587c67ce 100644 --- a/src/main/java/com/cleanengine/coin/order/domain/BuyOrder.java +++ b/src/main/java/com/cleanengine/coin/order/domain/BuyOrder.java @@ -1,13 +1,13 @@ package com.cleanengine.coin.order.domain; -import jakarta.persistence.*; +import jakarta.persistence.AttributeOverride; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; import lombok.AccessLevel; -import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.Setter; import org.springframework.validation.FieldError; -import com.cleanengine.coin.common.error.DomainValidationException; import java.time.LocalDateTime; import java.util.ArrayList; @@ -19,7 +19,6 @@ @AttributeOverride(name="id", column=@Column(name="buy_order_id")) @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter -@Setter public class BuyOrder extends Order implements Comparable { @Column(name="locked_deposit", nullable = false, updatable = false) private Double lockedDeposit; @@ -27,26 +26,17 @@ public class BuyOrder extends Order implements Comparable { @Column(name="remaining_deposit", nullable = false) private Double remainingDeposit; - public static BuyOrder createMarketBuyOrder(String ticker, Integer userId, Double price, + public static BuyOrder createMarketBuyOrder(String ticker, Integer userId, Double deposit, LocalDateTime createdAt, Boolean isBot) { + // TODO command 객체의 validation으로 추출 List errors = new ArrayList<>(); - if(price == null){ - errors.add(new FieldError("BuyOrder", "price", "price cannot be null")); + if(deposit == null){ + errors.add(new FieldError("BuyOrder", "deposit", "deposit cannot be null")); } handleValidationErrors(errors); - BuyOrder buyOrder = new BuyOrder(); - buyOrder.ticker = ticker; - buyOrder.userId = userId; - buyOrder.state = OrderStatus.WAIT; - buyOrder.lockedDeposit = price; - buyOrder.orderSize = null; - buyOrder.price = null; - buyOrder.createdAt = createdAt; - buyOrder.isMarketOrder = true; - buyOrder.remainingDeposit = buyOrder.lockedDeposit; - buyOrder.remainingSize = null; - buyOrder.isBot = isBot; + BuyOrder buyOrder = new BuyOrder(null, ticker, userId, OrderStatus.WAIT, null, + null, null, createdAt, true, isBot, deposit, deposit); return buyOrder; } @@ -61,28 +51,12 @@ public static BuyOrder createLimitBuyOrder(String ticker, Integer userId, Double } handleValidationErrors(errors); - BuyOrder buyOrder = new BuyOrder(); - buyOrder.ticker = ticker; - buyOrder.userId = userId; - buyOrder.state = OrderStatus.WAIT; - buyOrder.lockedDeposit = orderSize * price; - buyOrder.orderSize = orderSize; - buyOrder.price = price; - buyOrder.createdAt = createdAt; - buyOrder.isMarketOrder = false; - buyOrder.remainingDeposit = buyOrder.lockedDeposit; - buyOrder.remainingSize = orderSize; - buyOrder.isBot = isBot; + Double deposit = orderSize * price; + BuyOrder buyOrder = new BuyOrder(null, ticker, userId, OrderStatus.WAIT, orderSize, price, + orderSize, createdAt, false, isBot, deposit, deposit); return buyOrder; } - private static void handleValidationErrors(List errors) { - if(errors.size() > 0){ - throw new DomainValidationException( - "Validation Error occurred Creating BuyOrder", errors); - } - } - @Override public int compareTo(BuyOrder order) { // 지정가 매수 가격 비교 @@ -98,6 +72,10 @@ public int compareTo(BuyOrder order) { } public void decreaseRemainingDeposit(Double amount) { + if(amount == null){ + throw new IllegalArgumentException("감소시킬 양은 null일 수 없습니다."); + } + if (remainingDeposit >= amount) { remainingDeposit -= amount; } else { @@ -105,12 +83,19 @@ public void decreaseRemainingDeposit(Double amount) { } } + @Override public void decreaseRemainingSize(Double amount) { - if (remainingSize >= amount) { - remainingSize -= amount; - } else { - throw new IllegalArgumentException("주문의 잔여 수량은 0 이상이어야 합니다."); + if(isMarketOrder){ + throw new IllegalArgumentException("시장가 매수 주문은 잔량을 수정할 수 없습니다."); } + super.decreaseRemainingSize(amount); } + protected BuyOrder(Long id, String ticker, Integer userId, OrderStatus state, Double orderSize, + Double price, Double remainingSize, LocalDateTime createdAt, Boolean isMarketOrder, Boolean isBot, + Double lockedDeposit, Double remainingDeposit) { + super(id, ticker, userId, state, orderSize, price, remainingSize, createdAt, isMarketOrder, isBot); + this.lockedDeposit = lockedDeposit; + this.remainingDeposit = remainingDeposit; + } } diff --git a/src/main/java/com/cleanengine/coin/order/domain/Order.java b/src/main/java/com/cleanengine/coin/order/domain/Order.java index e5446a5f..50beb799 100644 --- a/src/main/java/com/cleanengine/coin/order/domain/Order.java +++ b/src/main/java/com/cleanengine/coin/order/domain/Order.java @@ -1,15 +1,21 @@ package com.cleanengine.coin.order.domain; +import com.cleanengine.coin.common.error.DomainValidationException; import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.Getter; -import lombok.Setter; +import lombok.NoArgsConstructor; +import org.springframework.validation.FieldError; import java.time.LocalDateTime; +import java.util.List; import java.util.Objects; @MappedSuperclass +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PROTECTED) @Getter -@Setter public abstract class Order { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) protected Long id; @@ -56,4 +62,27 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(this.getClass(), this.id); } + + public void decreaseRemainingSize(Double amount) { + if(amount == null){ + throw new IllegalArgumentException("감소시킬 잔량은 null일 수 없습니다."); + } + if (remainingSize >= amount) { + remainingSize -= amount; + } else { + throw new IllegalArgumentException("주문의 잔여 수량은 0 이상이어야 합니다."); + } + } + + public void setState(OrderStatus state) { + if(state == null) throw new IllegalArgumentException("OrderState cannot be null"); + this.state = state; + } + + protected static void handleValidationErrors(List errors) { + if(!errors.isEmpty()){ + throw new DomainValidationException( + "Validation Error occurred Creating Order", errors); + } + } } diff --git a/src/main/java/com/cleanengine/coin/order/domain/SellOrder.java b/src/main/java/com/cleanengine/coin/order/domain/SellOrder.java index d2183084..7ce06c78 100644 --- a/src/main/java/com/cleanengine/coin/order/domain/SellOrder.java +++ b/src/main/java/com/cleanengine/coin/order/domain/SellOrder.java @@ -1,13 +1,13 @@ package com.cleanengine.coin.order.domain; -import jakarta.persistence.*; +import jakarta.persistence.AttributeOverride; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; import lombok.AccessLevel; -import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.Setter; import org.springframework.validation.FieldError; -import com.cleanengine.coin.common.error.DomainValidationException; import java.time.LocalDateTime; import java.util.ArrayList; @@ -18,7 +18,6 @@ @AttributeOverride(name="id", column=@Column(name="sell_order_id")) @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter -@Setter public class SellOrder extends Order implements Comparable { public static SellOrder createMarketSellOrder(String ticker, Integer userId, Double orderSize, LocalDateTime createdAt, Boolean isBot) { @@ -29,16 +28,8 @@ public static SellOrder createMarketSellOrder(String ticker, Integer userId, Dou handleValidationErrors(errors); - SellOrder sellOrder = new SellOrder(); - sellOrder.ticker = ticker; - sellOrder.userId = userId; - sellOrder.state = OrderStatus.WAIT; - sellOrder.orderSize = orderSize; - sellOrder.price = null; - sellOrder.createdAt = createdAt; - sellOrder.isMarketOrder = true; - sellOrder.remainingSize = orderSize; - sellOrder.isBot = isBot; + SellOrder sellOrder = new SellOrder(null, ticker, userId, OrderStatus.WAIT, orderSize, null, + orderSize, createdAt, true, isBot); return sellOrder; } @@ -54,26 +45,11 @@ public static SellOrder createLimitSellOrder(String ticker, Integer userId, Doub handleValidationErrors(errors); - SellOrder sellOrder = new SellOrder(); - sellOrder.ticker = ticker; - sellOrder.userId = userId; - sellOrder.state = OrderStatus.WAIT; - sellOrder.orderSize = orderSize; - sellOrder.price = price; - sellOrder.createdAt = createdAt; - sellOrder.isMarketOrder = false; - sellOrder.remainingSize = orderSize; - sellOrder.isBot = isBot; + SellOrder sellOrder = new SellOrder(null, ticker, userId, OrderStatus.WAIT, orderSize, price, + orderSize, createdAt, false, isBot); return sellOrder; } - private static void handleValidationErrors(List errors) { - if(errors.size() > 0){ - throw new DomainValidationException( - "Validation Error occurred Creating SellOrder", errors); - } - } - @Override public int compareTo(SellOrder order) { // 지정가 매도 가격 비교 @@ -84,16 +60,11 @@ public int compareTo(SellOrder order) { } // 생성 시간 비교 - return this.createdAt.compareTo(order.createdAt); } - public void decreaseRemainingSize(Double amount) { - if (remainingSize >= amount) { - remainingSize -= amount; - } else { - throw new IllegalArgumentException("주문의 잔여 수량은 0 이상이어야 합니다."); - } + protected SellOrder(Long id, String ticker, Integer userId, OrderStatus state, Double orderSize, + Double price, Double remainingSize, LocalDateTime createdAt, Boolean isMarketOrder, Boolean isBot) { + super(id, ticker, userId, state, orderSize, price, remainingSize, createdAt, isMarketOrder, isBot); } - } From fdbda0806c5fd9397f9feb6bf1d8c6ab1300468f Mon Sep 17 00:00:00 2001 From: Junh-b Date: Mon, 2 Jun 2025 02:27:12 +0900 Subject: [PATCH 4/9] =?UTF-8?q?test:=20Order=20=EB=8F=84=EB=A9=94=EC=9D=B8?= =?UTF-8?q?=20=EB=8B=A8=EC=9C=84=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Order 도메인의 엔티티들에 대한 단위 테스트를 추가했습니다. --- .../coin/order/domain/BuyOrderTest.java | 183 ++++++++++++++++++ .../coin/order/domain/OrderTest.java | 140 ++++++++++++++ .../coin/order/domain/SellOrderTest.java | 138 +++++++++++++ 3 files changed, 461 insertions(+) create mode 100644 src/test/java/com/cleanengine/coin/order/domain/BuyOrderTest.java create mode 100644 src/test/java/com/cleanengine/coin/order/domain/OrderTest.java create mode 100644 src/test/java/com/cleanengine/coin/order/domain/SellOrderTest.java diff --git a/src/test/java/com/cleanengine/coin/order/domain/BuyOrderTest.java b/src/test/java/com/cleanengine/coin/order/domain/BuyOrderTest.java new file mode 100644 index 00000000..c812772b --- /dev/null +++ b/src/test/java/com/cleanengine/coin/order/domain/BuyOrderTest.java @@ -0,0 +1,183 @@ +package com.cleanengine.coin.order.domain; + +import com.cleanengine.coin.common.error.DomainValidationException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; + +import static org.junit.jupiter.api.Assertions.*; + +public class BuyOrderTest { + protected static final LocalDateTime baseTime = LocalDateTime.now(); + + @Nested + @DisplayName("시장가 매수주문 생성 테스트") + class CreateMarketBuyOrderTest{ + @DisplayName("deposit이 null인 시장가 매수주문을 생성시 Exception을 반환함") + @Test + public void createMarketBuyOrderWithNullDeposit_throwException(){ + Double nullDeposit = null; + + assertThrows(DomainValidationException.class, ()->{ + BuyOrder.createMarketBuyOrder("BTC", 1, nullDeposit, baseTime, false); + }); + } + + @DisplayName("deposit이 null이 아닌 시장가 매수 주문을 생성시 deposit 관련 필드가 정상적으로 초기화 됨") + @Test + public void createMarketBuyOrderWithDeposit_initializeDepositCorrectly() { + Double nonNullDeposit = 1000.0; + + BuyOrder buyOrder = BuyOrder.createMarketBuyOrder("BTC", 1, nonNullDeposit, baseTime, false); + + assertEquals(nonNullDeposit, buyOrder.getLockedDeposit()); + assertEquals(nonNullDeposit, buyOrder.getRemainingDeposit()); + } + + @DisplayName("시장가 매수 주문 생성시 OrderStatus가 WAIT로 초기화 됨") + @Test + public void createMarketBuyOrder_initializeOrderStatusWithWait() { + BuyOrder buyOrder = BuyOrder.createMarketBuyOrder("BTC", 1, 1000.0, baseTime, false); + + assertEquals(OrderStatus.WAIT, buyOrder.getState()); + } + } + + @Nested + @DisplayName("지정가 매수주문 생성 테스트") + class CreateLimitBuyOrderTest{ + @DisplayName("orderSize가 null인 지정가 매수주문을 생성시 Exception을 반환함") + @Test + public void createLimitBuyOrderWithNullOrderSize_throwException(){ + Double nullOrderSize = null; + + assertThrows(DomainValidationException.class, ()->{ + BuyOrder.createLimitBuyOrder("BTC", 1, nullOrderSize, 100.0, baseTime, false); + }); + } + + @DisplayName("price가 null인 지정가 매수주문을 생성시 Exception을 반환함") + @Test + public void createLimitBuyOrderWithNullPrice_throwException(){ + Double nullPrice = null; + + assertThrows(DomainValidationException.class, ()->{ + BuyOrder.createLimitBuyOrder("BTC", 1, 100.0, nullPrice, baseTime, false); + }); + } + + @DisplayName("orderSize와 price가 null이 아닌 지정가 매수 주문을 생성시 deposit 관련 필드가 정상적으로 초기화 됨") + @Test + public void createLimitBuyOrder_initializeDepositCorrectly() { + Double orderSize = 10.0; + Double price = 10.0; + + BuyOrder buyOrder = BuyOrder.createLimitBuyOrder("BTC", 1, orderSize, price, baseTime, false); + + Double deposit = orderSize * price; + assertEquals(deposit, buyOrder.getLockedDeposit()); + assertEquals(deposit, buyOrder.getRemainingDeposit()); + } + + @DisplayName("지정가 매수 주문 생성시 OrderStatus가 WAIT로 초기화 됨") + @Test + public void createLimitBuyOrder_initializeOrderStatusWithWait() { + BuyOrder buyOrder = BuyOrder.createLimitBuyOrder("BTC", 1, 10.0, 10.0, baseTime, false); + + assertEquals(OrderStatus.WAIT, buyOrder.getState()); + } + } + + @Nested + @DisplayName("compareTo 테스트") + class CompareToTest{ + @DisplayName("가격이 큰 지정가 매수주문과 가격이 작은 지정가 매수주문을 compareTo시, 가격이 큰 주문이 음수 결과가 나와야 함") + @Test + void compareToLimitBuyOrdersWithDifferentPrices_biggerBuyOrder_returnNegative() { + BuyOrder biggerPriceBuyOrder = BuyOrder.createLimitBuyOrder("BTC", 1, 100.0, 5.0, baseTime, false); + BuyOrder smallerPriceBuyOrder = BuyOrder.createLimitBuyOrder("BTC", 1, 100.0, 1.0, baseTime, false); + + assertTrue(biggerPriceBuyOrder.compareTo(smallerPriceBuyOrder) < 0); + assertTrue(smallerPriceBuyOrder.compareTo(biggerPriceBuyOrder) > 0); + } + + @DisplayName("가격이 동일하고, 생성 시간이 동일한 지정가 매수 주문을 compareTo시, 0이 나와야 함") + @Test + void compareToLimitBuyOrderWithSamePricesAndSameTimes_returnZero() { + BuyOrder sameTimeBuyOrder1 = BuyOrder.createLimitBuyOrder("BTC", 1, 100.0, 1.0, baseTime, false); + BuyOrder sameTimeBuyOrder2 = BuyOrder.createLimitBuyOrder("BTC", 1, 100.0, 1.0, baseTime, false); + + assertEquals(0, sameTimeBuyOrder1.compareTo(sameTimeBuyOrder2)); + } + + @DisplayName("가격이 같고 생성시간이 다른 지정가 매수주문을 compareTo시 생성시간이 빠른 주문이 음수가 나와야 함") + @Test + void compareToLimitBuyOrdersWithDifferentTimes_earlierTimeBuyOrder_returnNegative() { + LocalDateTime earlierTime = baseTime.minusSeconds(1); + LocalDateTime laterTime = baseTime.plusSeconds(1); + + BuyOrder earlierTimeBuyOrder = BuyOrder.createLimitBuyOrder("BTC", 1, 100.0, 1.0, earlierTime, false); + BuyOrder laterTimeBuyOrder = BuyOrder.createLimitBuyOrder("BTC", 1, 100.0, 1.0, laterTime, false); + + assertTrue(earlierTimeBuyOrder.compareTo(laterTimeBuyOrder) < 0); + assertTrue(laterTimeBuyOrder.compareTo(earlierTimeBuyOrder) > 0); + } + + @DisplayName("시장가 매수 주문들에 대해 compareTo시 생성시간이 빠른 주문이 음수가 나와야 함") + @Test + void compareToMarketBuyOrders_earlierTimeBuyOrder_returnNegative() { + LocalDateTime earlierTime = baseTime.minusSeconds(1); + LocalDateTime laterTime = baseTime.plusSeconds(1); + + BuyOrder earlierTimeBuyOrder = BuyOrder.createMarketBuyOrder("BTC", 1, 100.0, earlierTime, false); + BuyOrder laterTimeBuyOrder = BuyOrder.createMarketBuyOrder("BTC", 1, 1000.0, laterTime, false); + + assertTrue(earlierTimeBuyOrder.compareTo(laterTimeBuyOrder) < 0); + assertTrue(laterTimeBuyOrder.compareTo(earlierTimeBuyOrder) > 0); + } + } + + @Nested + @DisplayName("decreaseRemainingDeposit 테스트") + class DecreaseRemainingDepositTest{ + @DisplayName("null인 amount로 decreaseRemainingDeposit 호출시, Exception을 반환한다.") + @Test + void decreaseRemainingDepositWithNullAmount_throwsException() { + BuyOrder buyOrder = BuyOrder.createLimitBuyOrder("BTC", 1, 100.0, 10.0, baseTime, false); + + assertThrows(IllegalArgumentException.class, () -> buyOrder.decreaseRemainingDeposit(null)); + } + + @DisplayName("remainingDeposit보다 큰 amount로 decreaseRemainingDeposit 호출시, Exception을 반환한다.") + @Test + void decreaseRemainingDepositWithBiggerAmount_throwsException() { + BuyOrder buyOrder = BuyOrder.createLimitBuyOrder("BTC", 1, 100.0, 10.0, baseTime, false); + + assertThrows(IllegalArgumentException.class, () -> buyOrder.decreaseRemainingDeposit(2000.0)); + } + + @DisplayName("remainingDeposit보다 작은 amount로 decreaseRemainingDeposit 호출시, 정상 적용된다.") + @Test + void decreaseRemainingDepositWithSmallerAmount_resultAsExpected() { + BuyOrder buyOrder = BuyOrder.createLimitBuyOrder("BTC", 1, 100.0, 10.0, baseTime, false); + + buyOrder.decreaseRemainingDeposit(900.0); + + assertEquals(100.0, buyOrder.getRemainingDeposit()); + } + } + + @Nested + @DisplayName("decreaseRemainingSize 테스트") + class DecreaseRemainingSizeTest{ + @DisplayName("시장가 매수 주문에 대해 decreaseRemainingSize를 할 경우 Exception을 반환한다.") + @Test + void decreaseRemainingSizeWithMarketBuyOrder_throwsException() { + BuyOrder buyOrder = BuyOrder.createMarketBuyOrder("BTC", 1, 100.0, baseTime, false); + + assertThrows(IllegalArgumentException.class, () -> buyOrder.decreaseRemainingSize(10.0)); + } + } +} diff --git a/src/test/java/com/cleanengine/coin/order/domain/OrderTest.java b/src/test/java/com/cleanengine/coin/order/domain/OrderTest.java new file mode 100644 index 00000000..7bc4a30b --- /dev/null +++ b/src/test/java/com/cleanengine/coin/order/domain/OrderTest.java @@ -0,0 +1,140 @@ +package com.cleanengine.coin.order.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; + +import static com.cleanengine.coin.order.domain.tool.BuyOrderGenerator.LimitBuyOrderGenerator.createLimitBuyOrderWithRandomPrice; +import static org.junit.jupiter.api.Assertions.*; + +@DisplayName("주문(Order) 엔티티 테스트") +class OrderTest { + @Nested + @DisplayName("equals 테스트") + class EqualsTest{ + @DisplayName("같은 주문 객체에 대해 equals 연산을 수행하면, true를 반환한다.") + @Test + void equalsSameObject_returnTrue() { + BuyOrder buyOrder = new BuyOrder(1L, "BTC", 1, OrderStatus.WAIT, 1.0, 1.0, 1.0, null, false, false, 1.0, 1.0); + + assertTrue(buyOrder.equals(buyOrder)); + + } + @DisplayName("null인 주문 객체에 대해 equals 연산을 수행하면, false를 반환한다.") + @Test + void equalsNull_returnFalse() { + BuyOrder buyOrder = new BuyOrder(1L, "BTC", 1, OrderStatus.WAIT, 1.0, 1.0, 1.0, null, false, false, 1.0, 1.0); + BuyOrder nullBuyOrder = null; + + assertFalse(buyOrder.equals(nullBuyOrder)); + + } + @DisplayName("id가 같은 매도 주문과 매수주문에 대해 equals 연산을 수행하면, false를 반환한다.") + @Test + void equalsSameIdBuyOrderAndSellOrder_returnFalse() { + BuyOrder buyOrder = new BuyOrder(1L, "BTC", 1, OrderStatus.WAIT, 1.0, 1.0, 1.0, null, false, false, 1.0, 1.0); + SellOrder sellOrder = new SellOrder(1L, "BTC", 1, OrderStatus.WAIT, 1.0, 1.0, 1.0, null, false, false); + + assertFalse(buyOrder.equals(sellOrder)); + } + @DisplayName("id가 같은 매수주문 객체에 대해 equals 연산을 수행하면, true를 반환한다.") + @Test + void equalsSameIdBuyOrder_returnTrue() { + BuyOrder buyOrder = new BuyOrder(1L, "BTC", 1, OrderStatus.WAIT, 1.0, 1.0, 1.0, null, false, false, 1.0, 1.0); + BuyOrder buyOrder2 = new BuyOrder(1L, "BTC", 1, OrderStatus.WAIT, 2.0, 2.0, 2.0, null, false, false, 2.0, 2.0); + + assertTrue(buyOrder.equals(buyOrder2)); + } + @DisplayName("id가 다른 매수주문 객체에 대해 equals 연산을 수행하면, false를 반환한다.") + @Test + void equalsDifferentIdBuyOrder_returnFalse() { + BuyOrder buyOrder = new BuyOrder(1L, "BTC", 1, OrderStatus.WAIT, 1.0, 1.0, 1.0, null, false, false, 1.0, 1.0); + BuyOrder buyOrder2 = new BuyOrder(2L, "BTC", 1, OrderStatus.WAIT, 1.0, 1.0, 1.0, null, false, false, 1.0, 1.0); + + assertFalse(buyOrder.equals(buyOrder2)); + } + @DisplayName("id가 같은 매도주문 객체에 대해 equals 연산을 수행하면, true를 반환한다.") + @Test + void equalsSameIdSellOrder_returnTrue() { + SellOrder sellOrder = new SellOrder(1L, "BTC", 1, OrderStatus.WAIT, 1.0, 1.0, 1.0, null, false, false); + SellOrder sellOrder2 = new SellOrder(1L, "BTC", 1, OrderStatus.WAIT, 2.0, 2.0, 2.0, null, false, false); + + assertTrue(sellOrder.equals(sellOrder2)); + } + @DisplayName("id가 다른 매도주문 객체에 대해 equals 연산을 수행하면, false를 반환한다.") + @Test + void equalsDifferentIdSellOrder_returnFalse() { + SellOrder sellOrder = new SellOrder(1L, "BTC", 1, OrderStatus.WAIT, 1.0, 1.0, 1.0, null, false, false); + SellOrder sellOrder2 = new SellOrder(2L, "BTC", 1, OrderStatus.WAIT, 1.0, 1.0, 1.0, null, false, false); + + assertFalse(sellOrder.equals(sellOrder2)); + } + } + + @Nested + @DisplayName("hashCode 테스트") + class HashCodeTest{ + @DisplayName("매도 주문이 id가 같으면 hashcode는 같은 값이어야 한다.") + @Test + void sameIdSellOrders_returnSameHashCode() { + SellOrder sellOrder1 = new SellOrder(1L, "BTC", 1, OrderStatus.WAIT, 1.0, 1.0, 1.0, null, false, false); + SellOrder sellOrder2 = new SellOrder(1L, "BTC", 1, OrderStatus.WAIT, 2.0, 2.0, 2.0, null, false, false); + + assertEquals(sellOrder1.hashCode(), sellOrder2.hashCode()); + } + + @DisplayName("매수 주문이 id가 같으면 hashcode는 같은 값이어야 한다.") + @Test + void sameIdBuyOrders_returnSameHashCode() { + BuyOrder buyOrder1 = new BuyOrder(1L, "BTC", 1, OrderStatus.WAIT, 1.0, 1.0, 1.0, null, false, false, 1.0, 1.0); + BuyOrder buyOrder2 = new BuyOrder(1L, "BTC", 1, OrderStatus.WAIT, 2.0, 2.0, 2.0, null, false, false, 2.0, 2.0); + + assertEquals(buyOrder1.hashCode(), buyOrder2.hashCode()); + } + } + + @Nested + @DisplayName("setState 테스트") + class SetStateTest{ + @DisplayName("null인 orderState로 setState를 하면, Exception을 반환한다.") + @Test + void setNullOrderState_throwIllegalArgumentException() { + BuyOrder buyOrder = createLimitBuyOrderWithRandomPrice(); + OrderStatus nullState = null; + + assertThrows(IllegalArgumentException.class, () -> buyOrder.setState(nullState)); + } + } + + @Nested + @DisplayName("decreaseRemainingSize 테스트") + class DecreaseRemainingSizeTest{ + @DisplayName("null인 amount로 decreaseRemainingSize 호출시, Exception을 반환한다.") + @Test + void decreaseRemainingSizeWithNullAmount_throwsException() { + BuyOrder buyOrder = BuyOrder.createLimitBuyOrder("BTC", 1, 100.0, 10.0, LocalDateTime.now(), false); + + assertThrows(IllegalArgumentException.class, () -> buyOrder.decreaseRemainingSize(null)); + } + + @DisplayName("remainingSize보다 큰 amount로 decreaseRemainingSize 호출시, Exception을 반환한다.") + @Test + void decreaseRemainingSizeWithBiggerAmount_throwsException() { + BuyOrder buyOrder = BuyOrder.createLimitBuyOrder("BTC", 1, 100.0, 10.0, LocalDateTime.now(), false); + + assertThrows(IllegalArgumentException.class, () -> buyOrder.decreaseRemainingSize(200.0)); + } + + @DisplayName("remainingSize보다 작은 amount로 decreaseRemainingSize 호출시, 정상 적용된다.") + @Test + void decreaseRemainingSizeWithSmallerAmount_resultAsExpected() { + BuyOrder buyOrder = BuyOrder.createLimitBuyOrder("BTC", 1, 100.0, 10.0, LocalDateTime.now(), false); + + buyOrder.decreaseRemainingSize(90.0); + + assertEquals(10.0, buyOrder.getRemainingSize()); + } + } +} \ No newline at end of file diff --git a/src/test/java/com/cleanengine/coin/order/domain/SellOrderTest.java b/src/test/java/com/cleanengine/coin/order/domain/SellOrderTest.java new file mode 100644 index 00000000..f8df69e7 --- /dev/null +++ b/src/test/java/com/cleanengine/coin/order/domain/SellOrderTest.java @@ -0,0 +1,138 @@ +package com.cleanengine.coin.order.domain; + +import com.cleanengine.coin.common.error.DomainValidationException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class SellOrderTest { + protected static final LocalDateTime baseTime = LocalDateTime.now(); + + @Nested + @DisplayName("시장가 매도주문 생성 테스트") + class CreateMarketSellOrderTest{ + @DisplayName("orderSize가 null인 시장가 매도주문을 생성시 Exception을 반환함") + @Test + public void createMarketSellOrderWithNullOrderSize_throwException(){ + Double nullOrderSize = null; + + assertThrows(DomainValidationException.class, ()->{ + SellOrder.createMarketSellOrder("BTC", 1, nullOrderSize, baseTime, false); + }); + } + + @DisplayName("orderSize가 null이 아닌 시장가 매도 주문을 생성시 remainingSize 관련 필드가 정상적으로 초기화 됨") + @Test + public void createMarketSellOrder_initializeRemainingSizeCorrectly() { + Double orderSize = 10.0; + + SellOrder sellOrder = SellOrder.createMarketSellOrder("BTC", 1, orderSize, baseTime, false); + + assertEquals(orderSize, sellOrder.getRemainingSize()); + } + + @DisplayName("시장가 매도 주문 생성시 OrderStatus가 WAIT로 초기화 됨") + @Test + public void createMarketSellOrder_initializeOrderStatusWithWait() { + SellOrder sellOrder = SellOrder.createMarketSellOrder("BTC", 1, 10.0, baseTime, false); + + assertEquals(OrderStatus.WAIT, sellOrder.getState()); + } + } + + @Nested + @DisplayName("지정가 매도주문 생성 테스트") + class CreateLimitSellOrderTest{ + @DisplayName("orderSize가 null인 지정가 매도주문을 생성시 Exception을 반환함") + @Test + public void createLimitSellOrderWithNullOrderSize_throwException(){ + Double nullOrderSize = null; + + assertThrows(DomainValidationException.class, ()->{ + SellOrder.createLimitSellOrder("BTC", 1, nullOrderSize, 10.0, baseTime, false); + }); + } + + @DisplayName("price가 null인 지정가 매도주문을 생성시 Exception을 반환함") + @Test + public void createLimitSellOrderWithNullPrice_throwException(){ + Double nullPrice = null; + + assertThrows(DomainValidationException.class, ()->{ + SellOrder.createLimitSellOrder("BTC", 1, 10.0, nullPrice, baseTime, false); + }); + } + + @DisplayName("orderSize와 price가 null이 아닌 지정가 매도 주문을 생성시 remainingSize 관련 필드가 정상적으로 초기화 됨") + @Test + public void createLimitSellOrder_initializeRemainingSizeCorrectly() { + Double size = 10.0; + + SellOrder sellOrder = SellOrder.createLimitSellOrder("BTC", 1, size, size, baseTime, false); + + assertEquals(size, sellOrder.getRemainingSize()); + } + + @DisplayName("지정가 매도 주문 생성시 OrderStatus가 WAIT로 초기화 됨") + @Test + public void createLimitSellOrder_initializeOrderStatusWithWait() { + SellOrder sellOrder = SellOrder.createLimitSellOrder("BTC", 1, 10.0, 10.0, baseTime, false); + + assertEquals(OrderStatus.WAIT, sellOrder.getState()); + } + } + + @Nested + @DisplayName("compareTo 테스트") + class CompareToTest{ + @DisplayName("가격이 작은 지정가 매도주문과 가격이 큰 지정가 매도주문 compareTo시, 가격이 작은 주문이 음수 결과가 나와야 함") + @Test + void compareToLimitSellOrdersWithDifferentPrices_smallerSellOrder_returnNegative() { + SellOrder smallerPriceSellOrder = SellOrder.createLimitSellOrder("BTC", 1, 100.0, 1.0, baseTime, false); + SellOrder biggerPriceSellOrder = SellOrder.createLimitSellOrder("BTC", 1, 100.0, 5.0, baseTime, false); + + assertTrue(smallerPriceSellOrder.compareTo(biggerPriceSellOrder) < 0); + assertTrue(biggerPriceSellOrder.compareTo(smallerPriceSellOrder) > 0); + } + + @DisplayName("가격이 동일하고, 생성 시간이 동일한 지정가 매도 주문을 compareTo시, 0이 나와야 함") + @Test + void compareToLimitSellOrderWithSamePricesAndSameTimes_returnZero() { + SellOrder sameTimeSellOrder1 = SellOrder.createLimitSellOrder("BTC", 1, 100.0, 1.0, baseTime, false); + SellOrder sameTimeSellOrder2 = SellOrder.createLimitSellOrder("BTC", 1, 100.0, 1.0, baseTime, false); + + assertEquals(0, sameTimeSellOrder1.compareTo(sameTimeSellOrder2)); + } + + @DisplayName("가격이 같고 생성시간이 다른 지정가 매도주문을 compareTo시 생성시간이 빠른 주문이 음수가 나와야 함") + @Test + void compareToLimitSellOrdersWithDifferentTimes_earlierTimeSellOrder_returnNegative() { + LocalDateTime earlierTime = baseTime.minusSeconds(1); + LocalDateTime laterTime = baseTime.plusSeconds(1); + + SellOrder earlierTimeSellOrder = SellOrder.createLimitSellOrder("BTC", 1, 100.0, 1.0, earlierTime, false); + SellOrder laterTimeSellOrder = SellOrder.createLimitSellOrder("BTC", 1, 100.0, 1.0, laterTime, false); + + assertTrue(earlierTimeSellOrder.compareTo(laterTimeSellOrder) < 0); + assertTrue(laterTimeSellOrder.compareTo(earlierTimeSellOrder) > 0); + } + + @DisplayName("시장가 매도 주문들에 대해 compareTo시 생성시간이 빠른 주문이 음수가 나와야 함") + @Test + void compareToMarketSellOrders_earlierTimeSellOrder_returnNegative() { + LocalDateTime earlierTime = baseTime.minusSeconds(1); + LocalDateTime laterTime = baseTime.plusSeconds(1); + + SellOrder earlierTimeSellOrder = SellOrder.createMarketSellOrder("BTC", 1, 100.0, earlierTime, false); + SellOrder laterTimeSellOrder = SellOrder.createMarketSellOrder("BTC", 1, 1000.0, laterTime, false); + + assertTrue(earlierTimeSellOrder.compareTo(laterTimeSellOrder) < 0); + assertTrue(laterTimeSellOrder.compareTo(earlierTimeSellOrder) > 0); + } + } +} From 8410075d67d4ac66de903b4514478b4dbd992c2d Mon Sep 17 00:00:00 2001 From: Junh-b Date: Mon, 2 Jun 2025 02:36:15 +0900 Subject: [PATCH 5/9] =?UTF-8?q?test:=20Asset=20=EC=97=94=ED=8B=B0=ED=8B=B0?= =?UTF-8?q?=20null=20check=20=EC=B6=94=EA=B0=80,=20=EB=8B=A8=EC=9C=84=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cleanengine/coin/order/domain/Asset.java | 13 +++++- .../coin/order/domain/AssetTest.java | 41 +++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 src/test/java/com/cleanengine/coin/order/domain/AssetTest.java diff --git a/src/main/java/com/cleanengine/coin/order/domain/Asset.java b/src/main/java/com/cleanengine/coin/order/domain/Asset.java index 5e70c9ac..8cb5ebf4 100644 --- a/src/main/java/com/cleanengine/coin/order/domain/Asset.java +++ b/src/main/java/com/cleanengine/coin/order/domain/Asset.java @@ -1,11 +1,14 @@ package com.cleanengine.coin.order.domain; import jakarta.persistence.*; -import lombok.*; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; @Entity @Table(name = "asset") -@NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter public class Asset { @Id @Column(name = "ticker", length = 10, nullable = false) @@ -16,7 +19,13 @@ public class Asset { private byte[] icon; public Asset(String ticker, String name){ + if(ticker == null || name == null) throw new IllegalArgumentException("ticker, name cannot be null"); this.ticker = ticker; this.name = name; } + + public Asset(String ticker, String name, byte[] icon){ + this(ticker, name); + this.icon = icon; + } } diff --git a/src/test/java/com/cleanengine/coin/order/domain/AssetTest.java b/src/test/java/com/cleanengine/coin/order/domain/AssetTest.java new file mode 100644 index 00000000..0a8d9072 --- /dev/null +++ b/src/test/java/com/cleanengine/coin/order/domain/AssetTest.java @@ -0,0 +1,41 @@ +package com.cleanengine.coin.order.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class AssetTest { + @Nested + @DisplayName("Asset 생성 테스트") + class CreateAssetTest{ + @DisplayName("ticker가 null인 Asset을 생성시 Exception을 반환한다.") + @Test + void createAssetWithNullTicker_returnsException() { + String nullTicker = null; + + assertThrows(IllegalArgumentException.class, () -> new Asset(nullTicker, "name")); + } + + @DisplayName("name이 null인 Asset을 생성시 Exception을 반환한다.") + @Test + void createAssetWithNullName_returnsException() { + String nullName = null; + + assertThrows(IllegalArgumentException.class, () -> new Asset("BTC", nullName)); + } + @DisplayName("ticker와 name이 null이 아닌 Asset 생성시 제대로 초기화된다.") + @Test + void createAsset_initializedAsExpected() { + String ticker = "BTC"; + String name = "비트코인"; + + Asset asset = new Asset(ticker, name, null); + + assertEquals(ticker, asset.getTicker()); + assertEquals(name, asset.getName()); + } + } +} From ac36f5dc69817994ca8865ebe05a68b9942c19bc Mon Sep 17 00:00:00 2001 From: Junh-b Date: Mon, 2 Jun 2025 11:07:29 +0900 Subject: [PATCH 6/9] =?UTF-8?q?refactor:=20=EC=A3=BC=EB=AC=B8=20=EC=A0=91?= =?UTF-8?q?=EC=88=98=EC=8B=9C=20sub=20logic=EB=93=A4=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Account, Wallet의 의존성을 불필요하게 분리했던 점을 수정했습니다. Order, Wallet, Account는 하나의 트랜잭션에서 강하게 일관성이 필요한 점을 고려했습니다. --- .../configuration/bootstrap/DBInitRunner.java | 8 ++-- .../account/AccountExternalService.java | 35 ------------------ .../account/LockDepositService.java | 20 ---------- ...itory.java => OrderAccountRepository.java} | 3 +- .../persistentce/wallet/LockAssetService.java | 19 ---------- ...sitory.java => OrderWalletRepository.java} | 2 +- ....java => OrderWalletRepositoryCustom.java} | 2 +- ...a => OrderWalletRepositoryCustomImpl.java} | 2 +- .../wallet/WalletExternalService.java | 37 ------------------- .../coin/order/application/OrderService.java | 17 ++++++++- .../port/out/AccountUpdatePort.java | 5 --- .../port/out/WalletUpdatePort.java | 5 --- .../strategy/BuyOrderStrategy.java | 29 ++++++++++----- .../strategy/CreateOrderStrategy.java | 9 +++-- .../strategy/SellOrderStrategy.java | 34 ++++++++++++----- .../service/OrderGenerateService.java | 16 ++++---- .../cleanengine/coin/user/domain/Wallet.java | 12 ++++++ .../buyorder/BuyOrderIntegrationTest.java | 22 +++++------ .../sellorder/SellOrderIntegrationTest.java | 18 ++++----- 19 files changed, 111 insertions(+), 184 deletions(-) delete mode 100644 src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/account/AccountExternalService.java delete mode 100644 src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/account/LockDepositService.java rename src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/account/{AccountExternalRepository.java => OrderAccountRepository.java} (69%) delete mode 100644 src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/LockAssetService.java rename src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/{WalletExternalRepository.java => OrderWalletRepository.java} (61%) rename src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/{WalletExternalRepositoryCustom.java => OrderWalletRepositoryCustom.java} (81%) rename src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/{WalletExternalRepositoryCustomImpl.java => OrderWalletRepositoryCustomImpl.java} (92%) delete mode 100644 src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/WalletExternalService.java delete mode 100644 src/main/java/com/cleanengine/coin/order/application/port/out/AccountUpdatePort.java delete mode 100644 src/main/java/com/cleanengine/coin/order/application/port/out/WalletUpdatePort.java diff --git a/src/main/java/com/cleanengine/coin/configuration/bootstrap/DBInitRunner.java b/src/main/java/com/cleanengine/coin/configuration/bootstrap/DBInitRunner.java index 7177eb7d..d5529057 100644 --- a/src/main/java/com/cleanengine/coin/configuration/bootstrap/DBInitRunner.java +++ b/src/main/java/com/cleanengine/coin/configuration/bootstrap/DBInitRunner.java @@ -1,7 +1,7 @@ package com.cleanengine.coin.configuration.bootstrap; import com.cleanengine.coin.order.domain.Asset; -import com.cleanengine.coin.order.adapter.out.persistentce.wallet.WalletExternalRepository; +import com.cleanengine.coin.order.adapter.out.persistentce.wallet.OrderWalletRepository; import com.cleanengine.coin.order.adapter.out.persistentce.asset.AssetRepository; import com.cleanengine.coin.user.domain.Account; import com.cleanengine.coin.user.domain.User; @@ -24,7 +24,7 @@ public class DBInitRunner implements CommandLineRunner { private final AccountRepository accountRepository; private final UserRepository userRepository; - private final WalletExternalRepository walletExternalRepository; + private final OrderWalletRepository orderWalletRepository; private final AssetRepository assetRepository; @Transactional @@ -58,7 +58,7 @@ protected void initSellBotData(){ wallet2.setTicker("TRUMP"); wallet2.setAccountId(account.getId()); wallet2.setSize(500_000_000.0); - walletExternalRepository.saveAll(List.of(wallet, wallet2)); + orderWalletRepository.saveAll(List.of(wallet, wallet2)); } @Transactional @@ -80,7 +80,7 @@ protected void initBuyBotData() { wallet2.setTicker("TRUMP"); wallet2.setAccountId(account.getId()); wallet2.setSize(0.0); - walletExternalRepository.saveAll(List.of(wallet, wallet2)); + orderWalletRepository.saveAll(List.of(wallet, wallet2)); } @Transactional diff --git a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/account/AccountExternalService.java b/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/account/AccountExternalService.java deleted file mode 100644 index d70869ac..00000000 --- a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/account/AccountExternalService.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.cleanengine.coin.order.adapter.out.persistentce.account; - -import com.cleanengine.coin.common.error.DomainValidationException; -import com.cleanengine.coin.order.application.port.out.AccountUpdatePort; -import com.cleanengine.coin.user.domain.Account; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.FieldError; - -import java.util.List; - -@Service -@RequiredArgsConstructor -public class AccountExternalService implements AccountUpdatePort { - private final AccountExternalRepository accountRepository; - private final LockDepositService lockDepositService; - - // TODO 동시성 문제 고려해야 - @Override - @Transactional(propagation = Propagation.MANDATORY) - public void lockDepositForBuyOrder(Integer userId, Double orderAmount) throws RuntimeException { - if(orderAmount <= 0){ - throw new DomainValidationException("orderAmount must be greater than 0", - List.of(new FieldError("BuyOrder", "lockedDeposit", "orderAmount must be greater than 0"))); - } - Account account = accountRepository - .findByUserId(userId) - .orElseThrow(()-> - new DomainValidationException("Account not found", - List.of(new FieldError("account", "userId", "user might not exist")))); - lockDepositService.lockDeposit(account, orderAmount); - } -} diff --git a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/account/LockDepositService.java b/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/account/LockDepositService.java deleted file mode 100644 index 1e83ed90..00000000 --- a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/account/LockDepositService.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.cleanengine.coin.order.adapter.out.persistentce.account; - -import com.cleanengine.coin.common.error.DomainValidationException; -import com.cleanengine.coin.user.domain.Account; -import org.springframework.stereotype.Component; -import org.springframework.validation.FieldError; - -import java.util.List; - -@Component -public class LockDepositService { - public void lockDeposit(Account account, Double orderAmount){ - // TODO 원래라면 이 로직은 Account 내에 있어야 함. 각 엔티티가 자신의 invariant를 보장해야 하므로 - if(account.getCash() < orderAmount){ - throw new DomainValidationException("not enough cash", - List.of(new FieldError("account", "cash", "not enough cash"))); - } - account.setCash(account.getCash() - orderAmount); - } -} diff --git a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/account/AccountExternalRepository.java b/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/account/OrderAccountRepository.java similarity index 69% rename from src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/account/AccountExternalRepository.java rename to src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/account/OrderAccountRepository.java index d5e70575..0ad71a03 100644 --- a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/account/AccountExternalRepository.java +++ b/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/account/OrderAccountRepository.java @@ -5,7 +5,6 @@ import java.util.Optional; -public interface AccountExternalRepository extends CrudRepository { - // TODO null 대처 해야 +public interface OrderAccountRepository extends CrudRepository { Optional findByUserId(Integer userId); } diff --git a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/LockAssetService.java b/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/LockAssetService.java deleted file mode 100644 index 024d3f21..00000000 --- a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/LockAssetService.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.cleanengine.coin.order.adapter.out.persistentce.wallet; - -import com.cleanengine.coin.common.error.DomainValidationException; -import com.cleanengine.coin.user.domain.Wallet; -import org.springframework.stereotype.Component; -import org.springframework.validation.FieldError; - -import java.util.List; - -@Component -public class LockAssetService { - public void lockAsset(Wallet wallet, Double orderSize) { - if(wallet.getSize() < orderSize){ - throw new DomainValidationException("not enough asset", - List.of(new FieldError("wallet", "size", "not enough asset"))); - } - wallet.setSize(wallet.getSize() - orderSize); - } -} diff --git a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/WalletExternalRepository.java b/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/OrderWalletRepository.java similarity index 61% rename from src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/WalletExternalRepository.java rename to src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/OrderWalletRepository.java index d1b5e37e..6e3d3981 100644 --- a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/WalletExternalRepository.java +++ b/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/OrderWalletRepository.java @@ -3,5 +3,5 @@ import com.cleanengine.coin.user.domain.Wallet; import org.springframework.data.repository.CrudRepository; -public interface WalletExternalRepository extends CrudRepository, WalletExternalRepositoryCustom { +public interface OrderWalletRepository extends CrudRepository, OrderWalletRepositoryCustom { } diff --git a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/WalletExternalRepositoryCustom.java b/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/OrderWalletRepositoryCustom.java similarity index 81% rename from src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/WalletExternalRepositoryCustom.java rename to src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/OrderWalletRepositoryCustom.java index d6ff1ec1..9f73edaa 100644 --- a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/WalletExternalRepositoryCustom.java +++ b/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/OrderWalletRepositoryCustom.java @@ -4,6 +4,6 @@ import java.util.Optional; -public interface WalletExternalRepositoryCustom { +public interface OrderWalletRepositoryCustom { Optional findWalletBy(Integer userId, String ticker); } diff --git a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/WalletExternalRepositoryCustomImpl.java b/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/OrderWalletRepositoryCustomImpl.java similarity index 92% rename from src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/WalletExternalRepositoryCustomImpl.java rename to src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/OrderWalletRepositoryCustomImpl.java index f4968bfb..471adcc3 100644 --- a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/WalletExternalRepositoryCustomImpl.java +++ b/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/OrderWalletRepositoryCustomImpl.java @@ -13,7 +13,7 @@ @Repository @Transactional @RequiredArgsConstructor -public class WalletExternalRepositoryCustomImpl implements WalletExternalRepositoryCustom { +public class OrderWalletRepositoryCustomImpl implements OrderWalletRepositoryCustom { private final EntityManager em; @Override diff --git a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/WalletExternalService.java b/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/WalletExternalService.java deleted file mode 100644 index 039fd9be..00000000 --- a/src/main/java/com/cleanengine/coin/order/adapter/out/persistentce/wallet/WalletExternalService.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.cleanengine.coin.order.adapter.out.persistentce.wallet; - -import com.cleanengine.coin.common.error.DomainValidationException; -import com.cleanengine.coin.order.application.port.out.WalletUpdatePort; -import com.cleanengine.coin.user.domain.Wallet; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.validation.FieldError; - -import java.util.List; - -@Service -@RequiredArgsConstructor -public class WalletExternalService implements WalletUpdatePort { - private final WalletExternalRepository walletRepository; - private final LockAssetService lockAssetService; - - @Override - public void lockAssetForSellOrder(Integer userId, String ticker, Double orderSize) throws RuntimeException { - if(orderSize <= 0){ - throw new DomainValidationException("orderSize must be positive", - List.of(new FieldError("SellOrder", "orderSize", "orderSize must be positive"))); - } - Wallet wallet = findWalletBy(userId, ticker); - lockAssetService.lockAsset(wallet, orderSize); - } - - private Wallet findWalletBy(Integer userId, String ticker) { - Wallet wallet = walletRepository - .findWalletBy(userId, ticker) - .orElseThrow(()-> - new DomainValidationException("Wallet not found", - List.of(new FieldError("wallet", "userId", "user might not exist"), - new FieldError("wallet", "ticker", "ticker might be wrong")))); - return wallet; - } -} diff --git a/src/main/java/com/cleanengine/coin/order/application/OrderService.java b/src/main/java/com/cleanengine/coin/order/application/OrderService.java index 3d723e7c..ab11b755 100644 --- a/src/main/java/com/cleanengine/coin/order/application/OrderService.java +++ b/src/main/java/com/cleanengine/coin/order/application/OrderService.java @@ -3,7 +3,9 @@ import com.cleanengine.coin.order.application.dto.OrderCommand; import com.cleanengine.coin.order.application.dto.OrderInfo; import com.cleanengine.coin.order.application.strategy.CreateOrderStrategy; -import jakarta.validation.Valid; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import jakarta.validation.Validator; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; @@ -12,6 +14,7 @@ import java.time.LocalDateTime; import java.util.List; +import java.util.Set; import static com.cleanengine.coin.common.CommonValues.BUY_ORDER_BOT_ID; import static com.cleanengine.coin.common.CommonValues.SELL_ORDER_BOT_ID; @@ -21,9 +24,11 @@ @Validated public class OrderService { private final List> createOrderStrategies; + private final Validator validator; @Transactional - public OrderInfo createOrder(@Valid OrderCommand.CreateOrder createOrder){ + public OrderInfo createOrder(OrderCommand.CreateOrder createOrder){ + validateCreateOrder(createOrder); CreateOrderStrategy createOrderStrategy = createOrderStrategies.stream() .filter(strategy -> strategy.supports(createOrder.isBuyOrder())).findFirst().orElseThrow(); @@ -39,4 +44,12 @@ public OrderInfo createOrderWithBot(String ticker, Boolean isBuyOrder, Double return createOrder(createOrder); } + + protected void validateCreateOrder(OrderCommand.CreateOrder createOrder) { + Set> violations = validator.validate(createOrder); + + if (!violations.isEmpty()) { + throw new ConstraintViolationException(violations); + } + } } diff --git a/src/main/java/com/cleanengine/coin/order/application/port/out/AccountUpdatePort.java b/src/main/java/com/cleanengine/coin/order/application/port/out/AccountUpdatePort.java deleted file mode 100644 index 8f8d40a9..00000000 --- a/src/main/java/com/cleanengine/coin/order/application/port/out/AccountUpdatePort.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.cleanengine.coin.order.application.port.out; - -public interface AccountUpdatePort { - void lockDepositForBuyOrder(Integer userId, Double orderAmount) throws RuntimeException; -} diff --git a/src/main/java/com/cleanengine/coin/order/application/port/out/WalletUpdatePort.java b/src/main/java/com/cleanengine/coin/order/application/port/out/WalletUpdatePort.java deleted file mode 100644 index a12fbc66..00000000 --- a/src/main/java/com/cleanengine/coin/order/application/port/out/WalletUpdatePort.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.cleanengine.coin.order.application.port.out; - -public interface WalletUpdatePort { - void lockAssetForSellOrder(Integer userId, String ticker, Double orderSize) throws RuntimeException; -} diff --git a/src/main/java/com/cleanengine/coin/order/application/strategy/BuyOrderStrategy.java b/src/main/java/com/cleanengine/coin/order/application/strategy/BuyOrderStrategy.java index c03211af..ef23efea 100644 --- a/src/main/java/com/cleanengine/coin/order/application/strategy/BuyOrderStrategy.java +++ b/src/main/java/com/cleanengine/coin/order/application/strategy/BuyOrderStrategy.java @@ -1,22 +1,25 @@ package com.cleanengine.coin.order.application.strategy; -import com.cleanengine.coin.order.adapter.out.persistentce.account.AccountExternalRepository; +import com.cleanengine.coin.common.error.DomainValidationException; +import com.cleanengine.coin.order.adapter.out.persistentce.account.OrderAccountRepository; import com.cleanengine.coin.order.adapter.out.persistentce.order.command.BuyOrderRepository; -import com.cleanengine.coin.order.adapter.out.persistentce.wallet.WalletExternalRepository; +import com.cleanengine.coin.order.adapter.out.persistentce.wallet.OrderWalletRepository; import com.cleanengine.coin.order.application.AssetService; import com.cleanengine.coin.order.application.dto.OrderInfo; -import com.cleanengine.coin.order.application.port.out.AccountUpdatePort; import com.cleanengine.coin.order.application.port.out.PublishOrderCreatedPort; import com.cleanengine.coin.order.domain.BuyOrder; import com.cleanengine.coin.order.domain.Order; import com.cleanengine.coin.order.domain.domainservice.CreateBuyOrderDomainService; import com.cleanengine.coin.order.domain.domainservice.CreateOrderDomainService; +import com.cleanengine.coin.user.domain.Account; import org.springframework.stereotype.Component; +import org.springframework.validation.FieldError; + +import java.util.List; @Component public class BuyOrderStrategy extends CreateOrderStrategy> { private final BuyOrderRepository buyOrderRepository; - private final AccountUpdatePort accountUpdatePort; private final CreateBuyOrderDomainService createOrderDomainService; @Override @@ -31,7 +34,15 @@ public void saveOrder(BuyOrder order) { @Override protected void keepHoldings(BuyOrder order) throws RuntimeException { - accountUpdatePort.lockDepositForBuyOrder(order.getUserId(), order.getLockedDeposit()); + Double lockedDeposit = order.getLockedDeposit(); + + Account account = accountRepository.findByUserId(order.getUserId()) + .orElseThrow(() -> new DomainValidationException("Account not found", + List.of(new FieldError("account", "userId", "user might not exist")))); + + account.decreaseCash(lockedDeposit); + + accountRepository.save(account); } @Override @@ -46,14 +57,12 @@ protected OrderInfo.BuyOrderInfo extractOrderInfo(Order order) { public BuyOrderStrategy(PublishOrderCreatedPort publishOrderCreatedPort, AssetService assetService, - WalletExternalRepository walletRepository, - AccountExternalRepository accountRepository, + OrderWalletRepository orderWalletRepository, + OrderAccountRepository orderAccountRepository, BuyOrderRepository buyOrderRepository, - AccountUpdatePort accountUpdatePort, CreateBuyOrderDomainService createOrderDomainService) { - super(publishOrderCreatedPort, assetService, walletRepository, accountRepository); + super(publishOrderCreatedPort, assetService, orderWalletRepository, orderAccountRepository); this.buyOrderRepository = buyOrderRepository; - this.accountUpdatePort = accountUpdatePort; this.createOrderDomainService = createOrderDomainService; } } diff --git a/src/main/java/com/cleanengine/coin/order/application/strategy/CreateOrderStrategy.java b/src/main/java/com/cleanengine/coin/order/application/strategy/CreateOrderStrategy.java index cfb42d28..ee6c4c59 100644 --- a/src/main/java/com/cleanengine/coin/order/application/strategy/CreateOrderStrategy.java +++ b/src/main/java/com/cleanengine/coin/order/application/strategy/CreateOrderStrategy.java @@ -1,6 +1,8 @@ package com.cleanengine.coin.order.application.strategy; import com.cleanengine.coin.common.error.DomainValidationException; +import com.cleanengine.coin.order.adapter.out.persistentce.account.OrderAccountRepository; +import com.cleanengine.coin.order.adapter.out.persistentce.wallet.OrderWalletRepository; import com.cleanengine.coin.order.application.AssetService; import com.cleanengine.coin.order.application.dto.OrderCommand; import com.cleanengine.coin.order.application.dto.OrderInfo; @@ -8,8 +10,6 @@ import com.cleanengine.coin.order.application.port.out.PublishOrderCreatedPort; import com.cleanengine.coin.order.domain.Order; import com.cleanengine.coin.order.domain.domainservice.CreateOrderDomainService; -import com.cleanengine.coin.order.adapter.out.persistentce.account.AccountExternalRepository; -import com.cleanengine.coin.order.adapter.out.persistentce.wallet.WalletExternalRepository; import com.cleanengine.coin.user.domain.Account; import com.cleanengine.coin.user.domain.Wallet; import lombok.AllArgsConstructor; @@ -21,8 +21,8 @@ public abstract class CreateOrderStrategy> { protected final PublishOrderCreatedPort publishOrderCreatedPort; protected final AssetService assetService; - protected final WalletExternalRepository walletRepository; - protected final AccountExternalRepository accountRepository; + protected final OrderWalletRepository walletRepository; + protected final OrderAccountRepository accountRepository; public S processCreatingOrder(OrderCommand.CreateOrder createOrderCommand){ validateTicker(createOrderCommand.ticker()); @@ -58,6 +58,7 @@ protected T createOrder(OrderCommand.CreateOrder createOrderCommand){ return order; } + // TODO 책임이 너무 많은 protected void createWalletIfNeeded(Integer userId, String ticker){ if(walletRepository.findWalletBy(userId, ticker).isEmpty()){ Account account = accountRepository.findByUserId(userId).orElseThrow(); diff --git a/src/main/java/com/cleanengine/coin/order/application/strategy/SellOrderStrategy.java b/src/main/java/com/cleanengine/coin/order/application/strategy/SellOrderStrategy.java index cc64b6e5..bb16c60c 100644 --- a/src/main/java/com/cleanengine/coin/order/application/strategy/SellOrderStrategy.java +++ b/src/main/java/com/cleanengine/coin/order/application/strategy/SellOrderStrategy.java @@ -1,22 +1,25 @@ package com.cleanengine.coin.order.application.strategy; -import com.cleanengine.coin.order.adapter.out.persistentce.account.AccountExternalRepository; +import com.cleanengine.coin.common.error.DomainValidationException; +import com.cleanengine.coin.order.adapter.out.persistentce.account.OrderAccountRepository; import com.cleanengine.coin.order.adapter.out.persistentce.order.command.SellOrderRepository; -import com.cleanengine.coin.order.adapter.out.persistentce.wallet.WalletExternalRepository; +import com.cleanengine.coin.order.adapter.out.persistentce.wallet.OrderWalletRepository; import com.cleanengine.coin.order.application.AssetService; import com.cleanengine.coin.order.application.dto.OrderInfo; import com.cleanengine.coin.order.application.port.out.PublishOrderCreatedPort; -import com.cleanengine.coin.order.application.port.out.WalletUpdatePort; import com.cleanengine.coin.order.domain.Order; import com.cleanengine.coin.order.domain.SellOrder; import com.cleanengine.coin.order.domain.domainservice.CreateOrderDomainService; import com.cleanengine.coin.order.domain.domainservice.CreateSellOrderDomainService; +import com.cleanengine.coin.user.domain.Wallet; import org.springframework.stereotype.Component; +import org.springframework.validation.FieldError; + +import java.util.List; @Component public class SellOrderStrategy extends CreateOrderStrategy { private final SellOrderRepository sellOrderRepository; - private final WalletUpdatePort walletUpdatePort; private final CreateSellOrderDomainService createOrderDomainService; @Override @@ -31,7 +34,20 @@ public void saveOrder(SellOrder order) { @Override protected void keepHoldings(SellOrder order) throws RuntimeException { - walletUpdatePort.lockAssetForSellOrder(order.getUserId(), order.getTicker(), order.getOrderSize()); + Integer userId = order.getUserId(); + String ticker = order.getTicker(); + Double orderSize = order.getOrderSize(); + + Wallet wallet = walletRepository + .findWalletBy(userId, ticker) + .orElseThrow(()-> + new DomainValidationException("Wallet not found", + List.of(new FieldError("wallet", "userId", "user might not exist"), + new FieldError("wallet", "ticker", "ticker might be wrong")))); + + wallet.decreaseSize(orderSize); + + walletRepository.save(wallet); } @Override @@ -46,14 +62,12 @@ protected OrderInfo.SellOrderInfo extractOrderInfo(Order order) { public SellOrderStrategy(PublishOrderCreatedPort publishOrderCreatedPort, AssetService assetService, - WalletExternalRepository walletRepository, - AccountExternalRepository accountRepository, + OrderWalletRepository orderWalletRepository, + OrderAccountRepository orderAccountRepository, SellOrderRepository sellOrderRepository, - WalletUpdatePort walletUpdatePort, CreateSellOrderDomainService createOrderDomainService) { - super(publishOrderCreatedPort, assetService, walletRepository, accountRepository); + super(publishOrderCreatedPort, assetService, orderWalletRepository, orderAccountRepository); this.sellOrderRepository = sellOrderRepository; - this.walletUpdatePort = walletUpdatePort; this.createOrderDomainService = createOrderDomainService; } } diff --git a/src/main/java/com/cleanengine/coin/realitybot/service/OrderGenerateService.java b/src/main/java/com/cleanengine/coin/realitybot/service/OrderGenerateService.java index 3ba308b2..849d769b 100644 --- a/src/main/java/com/cleanengine/coin/realitybot/service/OrderGenerateService.java +++ b/src/main/java/com/cleanengine/coin/realitybot/service/OrderGenerateService.java @@ -2,8 +2,8 @@ import com.cleanengine.coin.common.error.DomainValidationException; import com.cleanengine.coin.order.application.OrderService; -import com.cleanengine.coin.order.adapter.out.persistentce.account.AccountExternalRepository; -import com.cleanengine.coin.order.adapter.out.persistentce.wallet.WalletExternalRepository; +import com.cleanengine.coin.order.adapter.out.persistentce.account.OrderAccountRepository; +import com.cleanengine.coin.order.adapter.out.persistentce.wallet.OrderWalletRepository; import com.cleanengine.coin.trade.entity.Trade; import com.cleanengine.coin.trade.repository.TradeRepository; import com.cleanengine.coin.user.domain.Account; @@ -31,8 +31,8 @@ public class OrderGenerateService { private final OrderService orderService; private final TradeRepository tradeRepository; private final VWAPerrorInJectionScheduler vwaPerrorInJectionScheduler; - private final WalletExternalRepository walletExternalRepository; - private final AccountExternalRepository accountExternalRepository; + private final OrderWalletRepository orderWalletRepository; + private final OrderAccountRepository accountExternalRepository; private String ticker; @@ -183,12 +183,12 @@ private void createOrderWithFallback(String ticker,boolean isBuy, double volume, protected void resetBot(String ticker){ this.ticker = ticker; - Wallet wallet = walletExternalRepository.findWalletBy(SELL_ORDER_BOT_ID,ticker).get(); + Wallet wallet = orderWalletRepository.findWalletBy(SELL_ORDER_BOT_ID,ticker).get(); wallet.setSize(500_000_000.0); - Wallet wallet2 = walletExternalRepository.findWalletBy(BUY_ORDER_BOT_ID,ticker).get(); + Wallet wallet2 = orderWalletRepository.findWalletBy(BUY_ORDER_BOT_ID,ticker).get(); wallet2.setSize(0.0); - walletExternalRepository.save(wallet); - walletExternalRepository.save(wallet2); + orderWalletRepository.save(wallet); + orderWalletRepository.save(wallet2); Account account = accountExternalRepository.findByUserId(SELL_ORDER_BOT_ID).get(); account.setCash(0.0); diff --git a/src/main/java/com/cleanengine/coin/user/domain/Wallet.java b/src/main/java/com/cleanengine/coin/user/domain/Wallet.java index f189c134..df793cc8 100644 --- a/src/main/java/com/cleanengine/coin/user/domain/Wallet.java +++ b/src/main/java/com/cleanengine/coin/user/domain/Wallet.java @@ -43,4 +43,16 @@ public static Wallet generateEmptyWallet(String ticker, Integer accountId){ wallet.setRoi(0.0); return wallet; } + + public void decreaseSize(Double orderSize) { + if(orderSize <= 0){ + throw new IllegalArgumentException("orderSize must be greater than zero."); + } + + if(this.getSize() < orderSize){ + throw new IllegalArgumentException("Cannot decrease size. Available size: " + this.getSize() + ", requested: " + orderSize); + } + + this.size = this.getSize() - orderSize; + } } diff --git a/src/test/java/com/cleanengine/coin/order/integration/buyorder/BuyOrderIntegrationTest.java b/src/test/java/com/cleanengine/coin/order/integration/buyorder/BuyOrderIntegrationTest.java index 7a82204d..7c4e6a81 100644 --- a/src/test/java/com/cleanengine/coin/order/integration/buyorder/BuyOrderIntegrationTest.java +++ b/src/test/java/com/cleanengine/coin/order/integration/buyorder/BuyOrderIntegrationTest.java @@ -1,8 +1,8 @@ package com.cleanengine.coin.order.integration.buyorder; import com.cleanengine.coin.common.error.DomainValidationException; -import com.cleanengine.coin.order.adapter.out.persistentce.account.AccountExternalRepository; -import com.cleanengine.coin.order.adapter.out.persistentce.wallet.WalletExternalRepository; +import com.cleanengine.coin.order.adapter.out.persistentce.account.OrderAccountRepository; +import com.cleanengine.coin.order.adapter.out.persistentce.wallet.OrderWalletRepository; import com.cleanengine.coin.order.application.OrderService; import com.cleanengine.coin.order.application.dto.OrderCommand; import com.cleanengine.coin.order.application.dto.OrderInfo; @@ -27,10 +27,10 @@ public class BuyOrderIntegrationTest { OrderService orderService; @Autowired - AccountExternalRepository accountRepository; + OrderAccountRepository orderAccountRepository; @Autowired - WalletExternalRepository walletRepository; + OrderWalletRepository orderWalletRepository; //TODO 3,2가 예약어로 사용하는 만큼 1을 insert하는 테스트가 깨질 수 있다. 또한, sql로 초기화보다 EntityManager나 Repository로 초기화하는게 나은듯 @DisplayName("충분한 돈이 있는 유저가 시장가 매수주문 생성시 주문이 정상 생성됨.") @@ -41,7 +41,7 @@ void givenEnoughMoneyUser_WhenCreateMarketBuyOrder_ThenBuyOrderIsCreated() { true, true, null, 30.0, LocalDateTime.now(),false); OrderInfo.BuyOrderInfo buyOrderInfo = (OrderInfo.BuyOrderInfo) orderService.createOrder(command); - Account account = accountRepository.findByUserId(3).orElseThrow(); + Account account = orderAccountRepository.findByUserId(3).orElseThrow(); assertNotNull(buyOrderInfo.getId()); assertEquals(200000-30.0, account.getCash()); @@ -55,30 +55,30 @@ void givenEnoughMoneyUser_WhenCreateLimitBuyOrder_ThenSellOrderIsCreated() { true, false, 30.0, 40.0, LocalDateTime.now(),false); OrderInfo.BuyOrderInfo buyOrderInfo = (OrderInfo.BuyOrderInfo) orderService.createOrder(command); - Account account = accountRepository.findByUserId(3).orElseThrow(); + Account account = orderAccountRepository.findByUserId(3).orElseThrow(); assertNotNull(buyOrderInfo.getId()); assertEquals(200000-30.0*40.0, account.getCash()); } - @DisplayName("돈이 없는 유저가 시장가 매수주문 생성시 DomainValidationException을 반환함.") + @DisplayName("돈이 없는 유저가 시장가 매수주문 생성시 IllegalArgumentException을 반환함.") @Sql("classpath:db/user/user_zero_holdings.sql") @Test void givenZeroMoneyUser_WhenCreateMarketBuyOrder_ThenExceptionIsThrown() { OrderCommand.CreateOrder command = new OrderCommand.CreateOrder("BTC", 3, true, true, null, 40.0, LocalDateTime.now(),false); - assertThrows(DomainValidationException.class, () -> orderService.createOrder(command)); + assertThrows(IllegalArgumentException.class, () -> orderService.createOrder(command)); } - @DisplayName("돈이 없는 유저가 지정가 매수주문 생성시 DomainValidationException을 반환함.") + @DisplayName("돈이 없는 유저가 지정가 매수주문 생성시 IllegalArgumentException을 반환함.") @Sql("classpath:db/user/user_zero_holdings.sql") @Test void givenZeroMoneyUser_WhenCreateLimitBuyOrder_ThenExceptionIsThrown() { OrderCommand.CreateOrder command = new OrderCommand.CreateOrder("BTC", 3, true, false, 30.0, 40.0, LocalDateTime.now(),false); - assertThrows(DomainValidationException.class, () -> orderService.createOrder(command)); + assertThrows(IllegalArgumentException.class, () -> orderService.createOrder(command)); } @DisplayName("price를 누락한 시장가 매수주문이 들어올 경우 DomainValidationException을 반환함.") @@ -117,7 +117,7 @@ void givenUserWithoutWallet_WhenCreateOrder_ThenWalletIsCreated() { orderService.createOrder(command); - Wallet wallet = walletRepository.findWalletBy(3, "BTC").orElseThrow(); + Wallet wallet = orderWalletRepository.findWalletBy(3, "BTC").orElseThrow(); assertNotNull(wallet); assertEquals("BTC", wallet.getTicker()); } diff --git a/src/test/java/com/cleanengine/coin/order/integration/sellorder/SellOrderIntegrationTest.java b/src/test/java/com/cleanengine/coin/order/integration/sellorder/SellOrderIntegrationTest.java index b50a3424..90aaf4be 100644 --- a/src/test/java/com/cleanengine/coin/order/integration/sellorder/SellOrderIntegrationTest.java +++ b/src/test/java/com/cleanengine/coin/order/integration/sellorder/SellOrderIntegrationTest.java @@ -1,7 +1,7 @@ package com.cleanengine.coin.order.integration.sellorder; import com.cleanengine.coin.common.error.DomainValidationException; -import com.cleanengine.coin.order.adapter.out.persistentce.wallet.WalletExternalRepository; +import com.cleanengine.coin.order.adapter.out.persistentce.wallet.OrderWalletRepository; import com.cleanengine.coin.order.application.OrderService; import com.cleanengine.coin.order.application.dto.OrderCommand; import com.cleanengine.coin.order.application.dto.OrderInfo; @@ -25,7 +25,7 @@ public class SellOrderIntegrationTest { OrderService orderService; @Autowired - WalletExternalRepository walletRepository; + OrderWalletRepository orderWalletRepository; @DisplayName("충분한 가상화폐가 있는 유저가 시장가 매도주문 생성시 주문이 생성됨.") @Sql("classpath:db/user/user_enough_holdings.sql") @@ -35,7 +35,7 @@ void givenEnoughMoneyUser_WhenCreateMarketSellOrder_ThenSellOrderIsCreated() { false, true, 30.0, null, LocalDateTime.now(),false); OrderInfo.SellOrderInfo sellOrderInfo = (OrderInfo.SellOrderInfo) orderService.createOrder(command); - Wallet wallet = walletRepository.findWalletBy(3, "BTC").orElseThrow(); + Wallet wallet = orderWalletRepository.findWalletBy(3, "BTC").orElseThrow(); assertNotNull(sellOrderInfo.getId()); assertEquals(200000-30.0, wallet.getSize()); @@ -49,7 +49,7 @@ void givenEnoughMoneyUser_WhenCreateLimitSellOrder_ThenSellOrderIsCreated() { false, false, 30.0, 40.0, LocalDateTime.now(),false); OrderInfo.SellOrderInfo sellOrderInfo = (OrderInfo.SellOrderInfo) orderService.createOrder(command); - Wallet wallet = walletRepository.findWalletBy(3, "BTC").orElseThrow(); + Wallet wallet = orderWalletRepository.findWalletBy(3, "BTC").orElseThrow(); assertNotNull(sellOrderInfo.getId()); assertEquals(200000-30.0, wallet.getSize()); @@ -62,20 +62,20 @@ void givenZeroMoneyUser_WhenCreateMarketSellOrder_ThenExceptionIsThrown() { OrderCommand.CreateOrder command = new OrderCommand.CreateOrder("BTC", 3, false, true, 30.0, null, LocalDateTime.now(),false); - assertThrows(DomainValidationException.class, () -> orderService.createOrder(command)); + assertThrows(IllegalArgumentException.class, () -> orderService.createOrder(command)); } - @DisplayName("가상화폐가 없는 유저가 지정가 매도주문 생성시 DomainValidationException을 반환함.") + @DisplayName("가상화폐가 없는 유저가 지정가 매도주문 생성시 IllegalArgumentException을 반환함.") @Sql("classpath:db/user/user_zero_holdings.sql") @Test void givenZeroMoneyUser_WhenCreateLimitSellOrder_ThenExceptionIsThrown() { OrderCommand.CreateOrder command = new OrderCommand.CreateOrder("BTC", 3, false, false, 30.0, 40.0, LocalDateTime.now(),false); - assertThrows(DomainValidationException.class, () -> orderService.createOrder(command)); + assertThrows(IllegalArgumentException.class, () -> orderService.createOrder(command)); } - @DisplayName("orderSize를 누락한 시장가 매도주문이 들어올 경우 DomainValidationException을 반환함.") + @DisplayName("orderSize를 누락한 시장가 매도주문이 들어올 경우 IllegalArgumentException을 반환함.") @Test void givenCommandWithoutOrderSize_WhenCreateMarketSellOrder_ThenExceptionIsThrown() { OrderCommand.CreateOrder command = new OrderCommand.CreateOrder("BTC", 3, @@ -115,7 +115,7 @@ void givenUserWithoutWallet_WhenCreateOrder_ThenWalletIsCreated() { System.out.println(e.getMessage()); } - Wallet wallet = walletRepository.findWalletBy(3, "BTC").orElseThrow(); + Wallet wallet = orderWalletRepository.findWalletBy(3, "BTC").orElseThrow(); assertNotNull(wallet); assertEquals("BTC", wallet.getTicker()); } From 6da5480c458c900b301d0efbc781992d235a9278 Mon Sep 17 00:00:00 2001 From: Junh-b Date: Mon, 2 Jun 2025 11:08:43 +0900 Subject: [PATCH 7/9] =?UTF-8?q?test:=20OrderCommand=20ValidationTest=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NotNull과 같은 개발자의 추가적인 설정이 불필요한 Annotation을 제외한 제약들에 대해 테스트를 수행했습니다. --- .../dto/OrderCommandValidationTest.java | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/test/java/com/cleanengine/coin/order/application/dto/OrderCommandValidationTest.java diff --git a/src/test/java/com/cleanengine/coin/order/application/dto/OrderCommandValidationTest.java b/src/test/java/com/cleanengine/coin/order/application/dto/OrderCommandValidationTest.java new file mode 100644 index 00000000..f4705a6e --- /dev/null +++ b/src/test/java/com/cleanengine/coin/order/application/dto/OrderCommandValidationTest.java @@ -0,0 +1,84 @@ +package com.cleanengine.coin.order.application.dto; + +import com.cleanengine.coin.base.ValidatorTest; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class OrderCommandValidationTest { + @Nested + class CreateOrderValidationTest extends ValidatorTest { + @DisplayName("길이가 10이 넘는 ticker를 가진 CreateOrder 검증시 Exception을 반환함") + @Test + void validateCreateOrderWithLongTickerName_returnsException() { + String longTicker = "a".repeat(11); + + OrderCommand.CreateOrder createOrder = new OrderCommand.CreateOrder( + longTicker, 3, true, false, + 30.0, 50.0, LocalDateTime.now(), false); + + List constraintViolationInfos = validate(createOrder); + assertEquals(1, constraintViolationInfos.size()); + + ConstraintViolationInfo violationInfo = constraintViolationInfos.get(0); + assertEquals("ticker", violationInfo.getFieldName()); + assertEquals(longTicker, violationInfo.getInvalidValue()); + } + + @DisplayName("길이가 0인 ticker를 가진 CreateOrder 검증시 Exception을 반환함") + @Test + void validateCreateOrderWithEmptyTickerName_returnsException() { + String emptyTicker = ""; + + OrderCommand.CreateOrder createOrder = new OrderCommand.CreateOrder( + emptyTicker, 3, true, false, + 30.0, 50.0, LocalDateTime.now(), false); + + List constraintViolationInfos = validate(createOrder); + assertEquals(1, constraintViolationInfos.size()); + + ConstraintViolationInfo violationInfo = constraintViolationInfos.get(0); + assertEquals("ticker", violationInfo.getFieldName()); + assertEquals(emptyTicker, violationInfo.getInvalidValue()); + } + + @DisplayName("orderSize가 0인 CreateOrder 검증시 Exception을 반환함") + @Test + void validateCreateOrderWithZeroOrderSize_returnsException() { + Double zeroOrderSize = 0.0; + + OrderCommand.CreateOrder createOrder = new OrderCommand.CreateOrder( + "BTC", 3, true, false, + zeroOrderSize, 50.0, LocalDateTime.now(), false); + + List constraintViolationInfos = validate(createOrder); + assertEquals(1, constraintViolationInfos.size()); + + ConstraintViolationInfo violationInfo = constraintViolationInfos.get(0); + assertEquals("orderSize", violationInfo.getFieldName()); + assertEquals(zeroOrderSize, violationInfo.getInvalidValue()); + } + + @DisplayName("price가 0인 CreateOrder 검증시 Exception을 반환함") + @Test + void validateCreateOrderWithZeroPrice_returnsException() { + Double zeroPrice = 0.0; + + OrderCommand.CreateOrder createOrder = new OrderCommand.CreateOrder( + "BTC", 3, true, false, + 50.0, zeroPrice, LocalDateTime.now(), false); + + List constraintViolationInfos = validate(createOrder); + assertEquals(1, constraintViolationInfos.size()); + + ConstraintViolationInfo violationInfo = constraintViolationInfos.get(0); + assertEquals("price", violationInfo.getFieldName()); + assertEquals(zeroPrice, violationInfo.getInvalidValue()); + } + } +} From d65da3d2250d40aca0a35f2ef430c4ca99580d19 Mon Sep 17 00:00:00 2001 From: Junh-b Date: Mon, 2 Jun 2025 11:09:22 +0900 Subject: [PATCH 8/9] =?UTF-8?q?refactor:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=9A=A9=20CustomMockUser=20Annotation=20id=20=EC=B0=B8?= =?UTF-8?q?=EC=A1=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit id가 CommonValues로부터 값을 참조해 가독성을 좋게 만들었습니다. --- .../cleanengine/coin/tool/annotation/WithCustomMockUser.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/cleanengine/coin/tool/annotation/WithCustomMockUser.java b/src/test/java/com/cleanengine/coin/tool/annotation/WithCustomMockUser.java index 6112304a..1c3a1945 100644 --- a/src/test/java/com/cleanengine/coin/tool/annotation/WithCustomMockUser.java +++ b/src/test/java/com/cleanengine/coin/tool/annotation/WithCustomMockUser.java @@ -5,9 +5,11 @@ import java.lang.annotation.Retention; +import static com.cleanengine.coin.common.CommonValues.SELL_ORDER_BOT_ID; + @Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) @WithSecurityContext(factory = WithCustomMockUserSecurityContextFactory.class) public @interface WithCustomMockUser { String name() default "user"; - int id() default 1; + int id() default SELL_ORDER_BOT_ID; } From 5f574865c2d68c7d957ce336ee0c8e1ef71ab0e3 Mon Sep 17 00:00:00 2001 From: Junh-b Date: Wed, 4 Jun 2025 18:15:17 +0900 Subject: [PATCH 9/9] =?UTF-8?q?remove:=20=EC=82=AD=EC=A0=9C=EB=90=98?= =?UTF-8?q?=EC=96=B4=EC=95=BC=20=ED=96=88=EB=8D=98=20dependency=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/cleanengine/coin/order/application/OrderService.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/com/cleanengine/coin/order/application/OrderService.java b/src/main/java/com/cleanengine/coin/order/application/OrderService.java index 286ad7a2..ab11b755 100644 --- a/src/main/java/com/cleanengine/coin/order/application/OrderService.java +++ b/src/main/java/com/cleanengine/coin/order/application/OrderService.java @@ -2,10 +2,6 @@ import com.cleanengine.coin.order.application.dto.OrderCommand; import com.cleanengine.coin.order.application.dto.OrderInfo; -import com.cleanengine.coin.common.error.BusinessException; -import com.cleanengine.coin.common.response.ErrorStatus; -import com.cleanengine.coin.order.adapter.out.persistentce.order.command.BuyOrderRepository; -import com.cleanengine.coin.order.adapter.out.persistentce.order.command.SellOrderRepository; import com.cleanengine.coin.order.application.strategy.CreateOrderStrategy; import jakarta.validation.ConstraintViolation; import jakarta.validation.ConstraintViolationException;