diff --git a/apps/commerce-api/src/main/java/com/loopers/application/brand/BrandFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/brand/BrandFacade.java
new file mode 100644
index 000000000..64bae65a1
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/brand/BrandFacade.java
@@ -0,0 +1,18 @@
+package com.loopers.application.brand;
+
+import com.loopers.domain.brand.BrandService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+public class BrandFacade {
+
+    private final BrandService brandService;
+
+    public BrandInfo getBrand(Long id) {
+        return BrandInfo.from(brandService.getBrand(id));
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/brand/BrandInfo.java b/apps/commerce-api/src/main/java/com/loopers/application/brand/BrandInfo.java
new file mode 100644
index 000000000..80d2ef379
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/brand/BrandInfo.java
@@ -0,0 +1,12 @@
+package com.loopers.application.brand;
+
+import com.loopers.domain.brand.Brand;
+
+public record BrandInfo (
+        Long id,
+        String name
+) {
+    public static BrandInfo from(Brand brand) {
+        return new BrandInfo(brand.getId(), brand.getName());
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/example/ExampleFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/example/ExampleFacade.java
deleted file mode 100644
index 552a9ad62..000000000
--- a/apps/commerce-api/src/main/java/com/loopers/application/example/ExampleFacade.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.loopers.application.example;
-
-import com.loopers.domain.example.ExampleModel;
-import com.loopers.domain.example.ExampleService;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Component;
-
-@RequiredArgsConstructor
-@Component
-public class ExampleFacade {
-    private final ExampleService exampleService;
-
-    public ExampleInfo getExample(Long id) {
-        ExampleModel example = exampleService.getExample(id);
-        return ExampleInfo.from(example);
-    }
-}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/example/ExampleInfo.java b/apps/commerce-api/src/main/java/com/loopers/application/example/ExampleInfo.java
deleted file mode 100644
index 877aba96c..000000000
--- a/apps/commerce-api/src/main/java/com/loopers/application/example/ExampleInfo.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.loopers.application.example;
-
-import com.loopers.domain.example.ExampleModel;
-
-public record ExampleInfo(Long id, String name, String description) {
-    public static ExampleInfo from(ExampleModel model) {
-        return new ExampleInfo(
-            model.getId(),
-            model.getName(),
-            model.getDescription()
-        );
-    }
-}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/like/LikeCommand.java b/apps/commerce-api/src/main/java/com/loopers/application/like/LikeCommand.java
new file mode 100644
index 000000000..6278ab45a
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/like/LikeCommand.java
@@ -0,0 +1,7 @@
+package com.loopers.application.like;
+
+public record LikeCommand(
+        String userId,
+        Long productId
+) {
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/like/LikeFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/like/LikeFacade.java
new file mode 100644
index 000000000..0abbc492f
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/like/LikeFacade.java
@@ -0,0 +1,34 @@
+package com.loopers.application.like;
+
+import com.loopers.domain.like.LikeService;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.product.ProductService;
+import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+public class LikeFacade {
+
+    private final LikeService likeService;
+    private final UserService userService;
+    private final ProductService productService;
+
+    @Transactional
+    public void addLike(String userId, Long productId) {
+        User user = userService.getUserByUserId(userId);
+        Product product = productService.getProduct(productId);
+        likeService.addLike(user, product);
+    }
+
+    @Transactional
+    public void removeLike(String userId, Long productId) {
+        User user = userService.getUserByUserId(userId);
+        Product product = productService.getProduct(productId);
+        likeService.removeLike(user, product);
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java
new file mode 100644
index 000000000..f030a0fc0
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java
@@ -0,0 +1,110 @@
+package com.loopers.application.order;
+
+import com.loopers.domain.coupon.Coupon;
+import com.loopers.domain.coupon.CouponService;
+import com.loopers.domain.order.Order;
+import com.loopers.domain.order.OrderService;
+import com.loopers.domain.point.PointService;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.product.ProductService;
+import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserService;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import lombok.RequiredArgsConstructor;
+import org.springframework.orm.ObjectOptimisticLockingFailureException;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+@Component
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+public class OrderFacade {
+
+    private final OrderService orderService;
+    private final UserService userService;
+    private final ProductService productService;
+    private final PointService pointService;
+    private final CouponService couponService;
+
+    @Transactional
+    public OrderInfo placeOrder(OrderPlaceCommand command) {
+        User user = userService.getUserByUserId(command.userId());
+
+        List<Long> productIds = command.items().stream()
+                .map(OrderPlaceCommand.OrderItemCommand::productId)
+                .sorted()
+                .toList();
+
+        List<Product> products = productService.getProductsByIdsWithPessimisticLock(productIds);
+        Map<Long, Product> productMap = products.stream()
+                .collect(Collectors.toMap(Product::getId, Function.identity()));
+
+        validateAndDecreaseStock(command.items(), productMap);
+
+        Order order = Order.create(user);
+
+        for (OrderPlaceCommand.OrderItemCommand item : command.items()) {
+            Product product = productMap.get(item.productId());
+            order.addOrderItem(product, item.quantity());
+        }
+
+        Long totalAmount = order.getTotalAmountValue();
+        Long discountAmount = 0L;
+        Long couponId = command.couponId();
+
+        if (couponId != null) {
+            Coupon coupon = couponService.getCouponWithOptimisticLock(couponId);
+            couponService.validateCouponUsable(coupon, user);
+
+            discountAmount = coupon.calculateDiscount(totalAmount);
+            coupon.use();
+            couponService.save(coupon);
+        }
+        long finalAmount = totalAmount - discountAmount;
+        pointService.usePointWithLock(user.getUserIdValue(), finalAmount);
+
+        order.completePayment();
+        Order savedOrder = orderService.save(order);
+
+        return OrderInfo.from(savedOrder);
+    }
+
+    private void validateAndDecreaseStock(List<OrderPlaceCommand.OrderItemCommand> items, Map<Long, Product> productMap) {
+        for (OrderPlaceCommand.OrderItemCommand item : items) {
+            Product product = productMap.get(item.productId());
+
+            if (product == null) {
+                throw new CoreException(ErrorType.NOT_FOUND, "상품을 찾을 수 없습니다.");
+            }
+
+            if (!product.isStockAvailable(item.quantity())) {
+                throw new CoreException(ErrorType.BAD_REQUEST,
+                        String.format("상품 '%s'의 재고가 부족합니다.", product.getName()));
+            }
+
+            product.decreaseStock(item.quantity());
+        }
+    }
+
+    public List<OrderInfo> getMyOrders(String userId) {
+        User user = userService.getUserByUserId(userId);
+        List<Order> orders = orderService.getOrdersByUser(user);
+
+        return orders.stream()
+                .map(OrderInfo::from)
+                .toList();
+    }
+
+    public OrderInfo getOrderDetail(Long orderId, String userId) {
+        User user = userService.getUserByUserId(userId);
+        Order order = orderService.getOrderByIdAndUser(orderId, user);
+
+        return OrderInfo.from(order);
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/order/OrderInfo.java b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderInfo.java
new file mode 100644
index 000000000..b53ebea95
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderInfo.java
@@ -0,0 +1,48 @@
+package com.loopers.application.order;
+
+import com.loopers.domain.order.Order;
+import com.loopers.domain.order.OrderStatus;
+import com.loopers.domain.order.OrderItem;
+
+import java.time.ZonedDateTime;
+import java.util.List;
+
+public record OrderInfo(
+        Long orderId,
+        String userId,
+        Long totalAmount,
+        OrderStatus status,
+        ZonedDateTime paidAt,
+        List<OrderItemInfo> items
+) {
+    public record OrderItemInfo(
+            Long productId,
+            String productName,
+            Integer quantity,
+            Long unitPrice,
+            Long totalPrice
+    ) {
+        public static OrderItemInfo from(OrderItem item) {
+            return new OrderItemInfo(
+                    item.getProduct().getId(),
+                    item.getProduct().getName(),
+                    item.getQuantity(),
+                    item.getUnitPriceValue(),
+                    item.calculateAmount().getValue()
+            );
+        }
+    }
+
+    public static OrderInfo from(Order order) {
+        return new OrderInfo(
+                order.getId(),
+                order.getUser().getUserIdValue(),
+                order.getTotalAmountValue(),
+                order.getStatus(),
+                order.getPaidAt(),
+                order.getOrderItems().stream()
+                        .map(OrderItemInfo::from)
+                        .toList()
+        );
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/order/OrderPlaceCommand.java b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderPlaceCommand.java
new file mode 100644
index 000000000..1ce0b4671
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/order/OrderPlaceCommand.java
@@ -0,0 +1,14 @@
+package com.loopers.application.order;
+
+import java.util.List;
+
+public record OrderPlaceCommand(
+        String userId,
+        List<OrderItemCommand> items,
+        Long couponId
+) {
+    public record OrderItemCommand(
+            Long productId,
+            Integer quantity
+    ) {}
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/point/PointCommand.java b/apps/commerce-api/src/main/java/com/loopers/application/point/PointCommand.java
new file mode 100644
index 000000000..df056e17b
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/point/PointCommand.java
@@ -0,0 +1,7 @@
+package com.loopers.application.point;
+
+public record PointCommand(
+        String userId,
+        Long amount
+) {
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/point/PointFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/point/PointFacade.java
index 59497c95d..d70cfdbd4 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/point/PointFacade.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/point/PointFacade.java
@@ -23,7 +23,10 @@ public Point getPoint(String userId) {
     }
 
     @Transactional
-    public void chargePoint(String userId, Long amount) {
-        pointService.chargePoint(userId, amount);
+    public void chargePoint(PointCommand command) {
+        pointService.chargePoint(
+                command.userId(),
+                command.amount()
+        );
     }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/product/ProductDetailInfo.java b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductDetailInfo.java
new file mode 100644
index 000000000..1923cd82e
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductDetailInfo.java
@@ -0,0 +1,25 @@
+package com.loopers.application.product;
+
+import com.loopers.domain.product.Product;
+
+public record ProductDetailInfo(
+        Long productId,
+        String productName,
+        Long price,
+        Integer stock,
+        Long brandId,
+        String brandName,
+        Long likeCount
+) {
+    public static ProductDetailInfo of(Product product, Long likeCount) {
+        return new ProductDetailInfo(
+                product.getId(),
+                product.getName(),
+                product.getPriceValue(),
+                product.getStockValue(),
+                product.getBrand().getId(),
+                product.getBrand().getName(),
+                likeCount
+        );
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java
new file mode 100644
index 000000000..8b8415ba4
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java
@@ -0,0 +1,39 @@
+package com.loopers.application.product;
+
+import com.loopers.domain.brand.BrandService;
+import com.loopers.domain.like.LikeService;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.product.ProductSearchCondition;
+import com.loopers.domain.product.ProductService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Map;
+
+@Component
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+public class ProductFacade {
+
+    private final ProductService productService;
+    private final LikeService likeService;
+
+    public ProductDetailInfo getProductDetail(Long productId) {
+        Product product = productService.getProduct(productId);
+        Long likeCount = likeService.getLikeCount(product);
+        return ProductDetailInfo.of(product, likeCount);
+    }
+
+    public ProductListInfo getProducts(ProductGetListCommand command) {
+        ProductSearchCondition condition = new ProductSearchCondition(
+                command.brandId(),
+                command.pageable()
+        );
+
+        Page<Product> productPage = productService.getProducts(condition);
+        Map<Long, Long> likeCountMap = likeService.getLikeCounts(productPage.getContent());
+        return ProductListInfo.of(productPage, likeCountMap);
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/product/ProductGetListCommand.java b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductGetListCommand.java
new file mode 100644
index 000000000..2d3737ebe
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductGetListCommand.java
@@ -0,0 +1,9 @@
+package com.loopers.application.product;
+
+import org.springframework.data.domain.Pageable;
+
+public record ProductGetListCommand(
+        Long brandId,
+        Pageable pageable
+) {
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/product/ProductListInfo.java b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductListInfo.java
new file mode 100644
index 000000000..b79bf6e51
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/product/ProductListInfo.java
@@ -0,0 +1,52 @@
+package com.loopers.application.product;
+
+import com.loopers.domain.product.Product;
+import org.springframework.data.domain.Page;
+
+import java.util.List;
+import java.util.Map;
+
+public record ProductListInfo(
+        List<ProductContent> contents,
+        int page,
+        int size,
+        long totalElements,
+        int totalPages
+) {
+    public static ProductListInfo of(Page<Product> productPage, Map<Long, Long> likeCountMap) {
+        List<ProductContent> contents = productPage.getContent().stream()
+                .map(product -> ProductContent.of(
+                        product,
+                        likeCountMap.getOrDefault(product.getId(), 0L))
+                )
+                .toList();
+
+        return new ProductListInfo(
+                contents,
+                productPage.getNumber(),
+                productPage.getSize(),
+                productPage.getTotalElements(),
+                productPage.getTotalPages()
+        );
+    }
+
+    public record ProductContent(
+            Long id,
+            String name,
+            Long price,
+            Long brandId,
+            String brandName,
+            Long likeCount
+    ) {
+        public static ProductContent of(Product product, Long likeCount) {
+            return new ProductContent(
+                    product.getId(),
+                    product.getName(),
+                    product.getPriceValue(),
+                    product.getBrand().getId(),
+                    product.getBrand().getName(),
+                    likeCount
+            );
+        }
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/user/UserCommand.java b/apps/commerce-api/src/main/java/com/loopers/application/user/UserCommand.java
new file mode 100644
index 000000000..8f3ffe4bd
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/application/user/UserCommand.java
@@ -0,0 +1,11 @@
+package com.loopers.application.user;
+
+import com.loopers.domain.user.Gender;
+
+public record UserCommand(
+        String userId,
+        String email,
+        String birthDate,
+        Gender gender
+) {
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/user/UserFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/user/UserFacade.java
index 0b748f81b..79918c7b8 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/user/UserFacade.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/user/UserFacade.java
@@ -2,7 +2,6 @@
 
 
 import com.loopers.application.point.PointFacade;
-import com.loopers.domain.user.Gender;
 import com.loopers.domain.user.User;
 import com.loopers.domain.user.UserService;
 import lombok.RequiredArgsConstructor;
@@ -11,15 +10,21 @@
 
 @Component
 @RequiredArgsConstructor
+@Transactional(readOnly = true)
 public class UserFacade {
 
 	private final UserService userService;
     private final PointFacade pointFacade;
 
     @Transactional
-    public UserInfo signUp(String userId, String email, String birthDate, Gender gender) {
-        User user = userService.signUp(userId, email, birthDate, gender);
-        pointFacade.createPointForUser(userId);
+    public UserInfo signUp(UserCommand command) {
+        User user = userService.signUp(
+                command.userId(),
+                command.email(),
+                command.birthDate(),
+                command.gender()
+        );
+        pointFacade.createPointForUser(user.getUserId());
         return UserInfo.from(user);
     }
 
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/brand/Brand.java b/apps/commerce-api/src/main/java/com/loopers/domain/brand/Brand.java
new file mode 100644
index 000000000..ff0784971
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/brand/Brand.java
@@ -0,0 +1,41 @@
+package com.loopers.domain.brand;
+
+import com.loopers.domain.BaseEntity;
+import com.loopers.domain.product.Product;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import jakarta.persistence.*;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Entity
+@Getter
+@Table(name = "brands")
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class Brand extends BaseEntity {
+
+    @Column(name = "name", unique = true, nullable = false, length = 50)
+    private String name;
+
+    @OneToMany(mappedBy = "brand", fetch = FetchType.LAZY)
+    private List<Product> products = new ArrayList<>();
+
+    private Brand(String name) {
+        validateName(name);
+        this.name = name;
+    }
+
+    public static Brand create(String name) {
+        return new Brand(name);
+    }
+
+    private void validateName(String name) {
+        if (name == null || name.isBlank()) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "브랜드명은 비어있을 수 없습니다.");
+        }
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandRepository.java
new file mode 100644
index 000000000..132dfcc08
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandRepository.java
@@ -0,0 +1,8 @@
+package com.loopers.domain.brand;
+
+import java.util.Optional;
+
+public interface BrandRepository {
+    Brand save(Brand brand);
+    Optional<Brand> findById(Long id);
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandService.java b/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandService.java
new file mode 100644
index 000000000..177c87601
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandService.java
@@ -0,0 +1,20 @@
+package com.loopers.domain.brand;
+
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+public class BrandService {
+
+    private final BrandRepository brandRepository;
+
+    public Brand getBrand(Long id) {
+        return brandRepository.findById(id)
+                .orElseThrow(() -> new CoreException(ErrorType.NOT_FOUND, "브랜드를 찾을 수 없습니다."));
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/coupon/Coupon.java b/apps/commerce-api/src/main/java/com/loopers/domain/coupon/Coupon.java
new file mode 100644
index 000000000..d41ed001a
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/coupon/Coupon.java
@@ -0,0 +1,90 @@
+package com.loopers.domain.coupon;
+
+import com.loopers.domain.BaseEntity;
+import com.loopers.domain.user.User;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import jakarta.persistence.*;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+@Entity
+@Getter
+@Table(name = "coupons")
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class Coupon extends BaseEntity {
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "user_id", nullable = false)
+    private User user;
+
+    @Column(name = "name", nullable = false)
+    private String name;
+
+    @Enumerated(EnumType.STRING)
+    @Column(name = "discount_type", nullable = false)
+    private DiscountType discountType;
+
+    @Column(name = "discount_value", nullable = false)
+    private Long discountValue;
+
+    @Column(name = "is_used", nullable = false)
+    private Boolean isUsed = false;
+
+    @Version
+    private Long version;
+
+    private Coupon(User user, String name, DiscountType discountType, Long discountValue) {
+        validateFields(user, name, discountType, discountValue);
+        this.user = user;
+        this.name = name;
+        this.discountType = discountType;
+        this.discountValue = discountValue;
+        this.isUsed = false;
+    }
+
+    public static Coupon create(User user, String name, DiscountType discountType, Long discountValue) {
+        return new Coupon(user, name, discountType, discountValue);
+    }
+
+    public Long calculateDiscount(Long originalAmount) {
+        if (Boolean.TRUE.equals(this.isUsed)) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "이미 사용된 쿠폰입니다.");
+        }
+
+        return switch (discountType) {
+            case FIXED_AMOUNT -> Math.min(discountValue, originalAmount);
+            case PERCENTAGE -> (originalAmount * discountValue) / 100;
+        };
+    }
+
+    public void use() {
+        if (Boolean.TRUE.equals(this.isUsed)) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "이미 사용된 쿠폰입니다.");
+        }
+        this.isUsed = true;
+    }
+
+    public boolean canUse() {
+        return !this.isUsed;
+    }
+
+    private void validateFields(User user, String name, DiscountType discountType, Long discountValue) {
+        if (user == null) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "사용자는 필수입니다.");
+        }
+        if (name == null || name.isBlank()) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "쿠폰명은 필수입니다.");
+        }
+        if (discountType == null) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "할인 타입은 필수입니다.");
+        }
+        if (discountValue == null || discountValue <= 0) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "할인 값은 0보다 커야 합니다.");
+        }
+        if (discountType == DiscountType.PERCENTAGE && discountValue > 100) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "정률 할인은 100% 이하여야 합니다.");
+        }
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/coupon/CouponRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/coupon/CouponRepository.java
new file mode 100644
index 000000000..5db17a401
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/coupon/CouponRepository.java
@@ -0,0 +1,13 @@
+package com.loopers.domain.coupon;
+
+import com.loopers.domain.user.User;
+
+import java.util.Optional;
+
+public interface CouponRepository {
+    Coupon save(Coupon coupon);
+    Optional<Coupon> findById(Long id);
+    Optional<Coupon> findByIdWithOptimisticLock(Long id);
+    Optional<Coupon> findByIdWithPessimisticLock(Long id);
+    Optional<Coupon> findByIdAndUser(Long id, User user);
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/coupon/CouponService.java b/apps/commerce-api/src/main/java/com/loopers/domain/coupon/CouponService.java
new file mode 100644
index 000000000..2d9782389
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/coupon/CouponService.java
@@ -0,0 +1,42 @@
+package com.loopers.domain.coupon;
+
+import com.loopers.domain.user.User;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+public class CouponService {
+
+    private final CouponRepository couponRepository;
+
+    @Transactional
+    public Coupon getCouponWithOptimisticLock(Long id) {
+        return couponRepository.findByIdWithOptimisticLock(id)
+                .orElseThrow(() -> new CoreException(ErrorType.NOT_FOUND, "쿠폰을 찾을 수 없습니다."));
+    }
+
+    @Transactional
+    public Coupon getCouponWithPessimisticLock(Long id) {
+        return couponRepository.findByIdWithPessimisticLock(id)
+                .orElseThrow(() -> new CoreException(ErrorType.NOT_FOUND, "쿠폰을 찾을 수 없습니다."));
+    }
+
+    public void validateCouponUsable(Coupon coupon, User user) {
+        if (!coupon.getUser().equals(user)) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "본인의 쿠폰만 사용할 수 있습니다.");
+        }
+        if (!coupon.canUse()) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "사용할 수 없는 쿠폰입니다.");
+        }
+    }
+
+    @Transactional
+    public Coupon save(Coupon coupon) {
+        return couponRepository.save(coupon);
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/coupon/DiscountType.java b/apps/commerce-api/src/main/java/com/loopers/domain/coupon/DiscountType.java
new file mode 100644
index 000000000..0743084b2
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/coupon/DiscountType.java
@@ -0,0 +1,6 @@
+package com.loopers.domain.coupon;
+
+public enum DiscountType {
+    FIXED_AMOUNT, // 정액
+    PERCENTAGE // 정률
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/example/ExampleModel.java b/apps/commerce-api/src/main/java/com/loopers/domain/example/ExampleModel.java
deleted file mode 100644
index c588c4a8a..000000000
--- a/apps/commerce-api/src/main/java/com/loopers/domain/example/ExampleModel.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.loopers.domain.example;
-
-import com.loopers.domain.BaseEntity;
-import com.loopers.support.error.CoreException;
-import com.loopers.support.error.ErrorType;
-import jakarta.persistence.Entity;
-import jakarta.persistence.Table;
-
-@Entity
-@Table(name = "example")
-public class ExampleModel extends BaseEntity {
-
-    private String name;
-    private String description;
-
-    protected ExampleModel() {}
-
-    public ExampleModel(String name, String description) {
-        if (name == null || name.isBlank()) {
-            throw new CoreException(ErrorType.BAD_REQUEST, "이름은 비어있을 수 없습니다.");
-        }
-        if (description == null || description.isBlank()) {
-            throw new CoreException(ErrorType.BAD_REQUEST, "설명은 비어있을 수 없습니다.");
-        }
-
-        this.name = name;
-        this.description = description;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public void update(String newDescription) {
-        if (newDescription == null || newDescription.isBlank()) {
-            throw new CoreException(ErrorType.BAD_REQUEST, "설명은 비어있을 수 없습니다.");
-        }
-        this.description = newDescription;
-    }
-}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/example/ExampleRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/example/ExampleRepository.java
deleted file mode 100644
index 3625e5662..000000000
--- a/apps/commerce-api/src/main/java/com/loopers/domain/example/ExampleRepository.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.loopers.domain.example;
-
-import java.util.Optional;
-
-public interface ExampleRepository {
-    Optional<ExampleModel> find(Long id);
-}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/example/ExampleService.java b/apps/commerce-api/src/main/java/com/loopers/domain/example/ExampleService.java
deleted file mode 100644
index c0e8431e8..000000000
--- a/apps/commerce-api/src/main/java/com/loopers/domain/example/ExampleService.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.loopers.domain.example;
-
-import com.loopers.support.error.CoreException;
-import com.loopers.support.error.ErrorType;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
-
-@RequiredArgsConstructor
-@Component
-public class ExampleService {
-
-    private final ExampleRepository exampleRepository;
-
-    @Transactional(readOnly = true)
-    public ExampleModel getExample(Long id) {
-        return exampleRepository.find(id)
-            .orElseThrow(() -> new CoreException(ErrorType.NOT_FOUND, "[id = " + id + "] 예시를 찾을 수 없습니다."));
-    }
-}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/like/Like.java b/apps/commerce-api/src/main/java/com/loopers/domain/like/Like.java
new file mode 100644
index 000000000..1ae5e97fc
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/like/Like.java
@@ -0,0 +1,38 @@
+package com.loopers.domain.like;
+
+import com.loopers.domain.BaseEntity;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.user.User;
+import jakarta.persistence.*;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+@Entity
+@Getter
+@Table(name = "likes", uniqueConstraints = {
+        @UniqueConstraint(
+                name = "uk_user_product",
+                columnNames = {"user_id", "product_id"}
+        )
+})
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class Like extends BaseEntity {
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "user_id", nullable = false)
+    private User user;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "product_id", nullable = false)
+    private Product product;
+
+    private Like(User user, Product product) {
+        this.user = user;
+        this.product = product;
+    }
+
+    public static Like create(User user, Product product) {
+        return new Like(user, product);
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeRepository.java
new file mode 100644
index 000000000..4dd482228
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeRepository.java
@@ -0,0 +1,17 @@
+package com.loopers.domain.like;
+
+import com.loopers.domain.product.Product;
+import com.loopers.domain.user.User;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+public interface LikeRepository {
+    Like save(Like like);
+    void delete(Like like);
+    Optional<Like> findByUserAndProduct(User user, Product product);
+    boolean existsByUserAndProduct(User user, Product product);
+    Long countByProduct(Product product);
+    Map<Long, Long> findLikeCounts(List<Product> products);
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeService.java b/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeService.java
new file mode 100644
index 000000000..edf3c06e5
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/like/LikeService.java
@@ -0,0 +1,43 @@
+package com.loopers.domain.like;
+
+import com.loopers.domain.product.Product;
+import com.loopers.domain.user.User;
+import lombok.RequiredArgsConstructor;
+import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Map;
+
+@Service
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+public class LikeService {
+
+    private final LikeRepository likeRepository;
+
+    @Transactional
+    public void addLike(User user, Product product) {
+        Like like = Like.create(user, product);
+        // 중복 좋아요 방지를 위해 예외 무시
+        try {
+            likeRepository.save(like);
+        } catch (DataIntegrityViolationException ignored) {
+        }
+    }
+
+    @Transactional
+    public void removeLike(User user, Product product) {
+        likeRepository.findByUserAndProduct(user, product)
+                .ifPresent(likeRepository::delete);
+    }
+
+    public Long getLikeCount(Product product) {
+        return likeRepository.countByProduct(product);
+    }
+
+    public Map<Long, Long> getLikeCounts(List<Product> products) {
+        return likeRepository.findLikeCounts(products);
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java
new file mode 100644
index 000000000..1d13dfb14
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java
@@ -0,0 +1,97 @@
+package com.loopers.domain.order;
+
+import com.loopers.domain.BaseEntity;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.user.User;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import jakarta.persistence.*;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.List;
+
+@Entity
+@Getter
+@Table(name = "orders")
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class Order extends BaseEntity {
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "user_id", nullable = false)
+    private User user;
+
+    @Embedded
+    private OrderTotalAmount totalAmount;
+
+    @Enumerated(EnumType.STRING)
+    @Column(name = "status", nullable = false, length = 20)
+    private OrderStatus status;
+
+    @Column(name = "paid_at")
+    private ZonedDateTime paidAt;
+
+    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
+    private List<OrderItem> orderItems = new ArrayList<>();
+
+    private Order(User user) {
+        validateUser(user);
+        this.user = user;
+        this.totalAmount = OrderTotalAmount.zero();
+        this.status = OrderStatus.PENDING;
+    }
+
+    public static Order create(User user) {
+        return new Order(user);
+    }
+
+    public void addOrderItem(Product product, Integer quantity) {
+        if (product == null) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "상품은 필수입니다.");
+        }
+        if (quantity == null || quantity <= 0) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "수량은 1 이상이어야 합니다.");
+        }
+
+        OrderItem orderItem = OrderItem.create(product, quantity);
+        this.orderItems.add(orderItem);
+        orderItem.assignOrder(this);
+
+        recalculateTotalAmount();
+    }
+
+    public void completePayment() {
+        validateBeforePayment();
+        this.status = OrderStatus.PAID;
+        this.paidAt = ZonedDateTime.now();
+    }
+
+    private void recalculateTotalAmount() {
+        this.totalAmount = this.orderItems.stream()
+                .map(OrderItem::calculateAmount)
+                .reduce(OrderTotalAmount.zero(), OrderTotalAmount::add);
+    }
+
+    private void validateBeforePayment() {
+        if (this.orderItems.isEmpty()) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "주문 항목이 비어있습니다.");
+        }
+    }
+
+    private void validateUser(User user) {
+        if (user == null) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "사용자는 필수입니다.");
+        }
+    }
+
+    public Long getTotalAmountValue() {
+        return this.totalAmount.getValue();
+    }
+
+    public List<OrderItem> getOrderItems() {
+        return List.copyOf(orderItems);
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.java
new file mode 100644
index 000000000..80d04bef4
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.java
@@ -0,0 +1,53 @@
+package com.loopers.domain.order;
+
+import com.loopers.domain.BaseEntity;
+import com.loopers.domain.product.Product;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import jakarta.persistence.*;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+@Entity
+@Getter
+@Table(name = "order_items")
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class OrderItem extends BaseEntity {
+
+    @ManyToOne(fetch = FetchType.LAZY, optional = false)
+    @JoinColumn(name = "order_id", nullable = false)
+    private Order order;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "product_id", nullable = false)
+    private Product product;
+
+    @Column(name = "quantity", nullable = false)
+    private Integer quantity;
+
+    @Embedded
+    private OrderItemPrice orderItemPrice;
+
+    private OrderItem(Product product, Integer quantity) {
+        this.product = product;
+        this.quantity = quantity;
+        this.orderItemPrice = OrderItemPrice.of(product.getPriceValue());
+    }
+
+    public static OrderItem create(Product product, Integer quantity) {
+        return new OrderItem(product, quantity);
+    }
+
+    void assignOrder(Order order) {
+        this.order = order;
+    }
+
+    public OrderTotalAmount calculateAmount() {
+        return OrderTotalAmount.of(this.orderItemPrice.getValue() * this.quantity);
+    }
+
+    public Long getUnitPriceValue() {
+        return this.orderItemPrice.getValue();
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItemPrice.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItemPrice.java
new file mode 100644
index 000000000..b72b9db94
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItemPrice.java
@@ -0,0 +1,38 @@
+package com.loopers.domain.order;
+
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import jakarta.persistence.Column;
+import jakarta.persistence.Embeddable;
+import lombok.AccessLevel;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+@Embeddable
+@Getter
+@EqualsAndHashCode
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class OrderItemPrice {
+
+    @Column(name = "item_price", nullable = false)
+    private Long value;
+
+    private OrderItemPrice(Long value) {
+        validate(value);
+        this.value = value;
+    }
+
+    public static OrderItemPrice of(Long value) {
+        return new OrderItemPrice(value);
+    }
+
+    private void validate(Long value) {
+        if (value == null) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "단가는 필수입니다.");
+        }
+        if (value < 0) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "단가는 0 이상이어야 합니다.");
+        }
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderRepository.java
new file mode 100644
index 000000000..a1432a4bf
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderRepository.java
@@ -0,0 +1,11 @@
+package com.loopers.domain.order;
+
+
+import java.util.List;
+import java.util.Optional;
+
+public interface OrderRepository {
+    Order save(Order order);
+    Optional<Order> findByIdAndUser(Long orderId, Long userId);
+    List<Order> findAllByUser(Long userId);
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderService.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderService.java
new file mode 100644
index 000000000..91d49c8a2
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderService.java
@@ -0,0 +1,31 @@
+package com.loopers.domain.order;
+
+import com.loopers.domain.user.User;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+@Service
+@RequiredArgsConstructor
+public class OrderService {
+
+    private final OrderRepository orderRepository;
+
+    @Transactional
+    public Order save(Order order) {
+        return orderRepository.save(order);
+    }
+
+    public List<Order> getOrdersByUser(User user) {
+        return orderRepository.findAllByUser(user.getId());
+    }
+
+    public Order getOrderByIdAndUser(Long orderId, User user) {
+        return orderRepository.findByIdAndUser(orderId, user.getId())
+                .orElseThrow(() -> new CoreException(ErrorType.NOT_FOUND, "주문을 찾을 수 없습니다."));
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderStatus.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderStatus.java
new file mode 100644
index 000000000..a5dc41adf
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderStatus.java
@@ -0,0 +1,6 @@
+package com.loopers.domain.order;
+
+public enum OrderStatus {
+    PENDING,
+    PAID,
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderTotalAmount.java b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderTotalAmount.java
new file mode 100644
index 000000000..76aa55a06
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/order/OrderTotalAmount.java
@@ -0,0 +1,50 @@
+package com.loopers.domain.order;
+
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import jakarta.persistence.Column;
+import jakarta.persistence.Embeddable;
+import lombok.AccessLevel;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+@Embeddable
+@Getter
+@EqualsAndHashCode
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class OrderTotalAmount {
+
+    @Column(name = "total_amount", nullable = false)
+    private Long value;
+
+    private OrderTotalAmount(Long value) {
+        validate(value);
+        this.value = value;
+    }
+
+    public static OrderTotalAmount of(Long value) {
+        return new OrderTotalAmount(value);
+    }
+
+    public static OrderTotalAmount zero() {
+        return new OrderTotalAmount(0L);
+    }
+
+    private void validate(Long value) {
+        if (value == null) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "주문 총액은 필수입니다.");
+        }
+        if (value < 0) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "주문 총액은 0 이상이어야 합니다.");
+        }
+    }
+
+    public OrderTotalAmount add(OrderTotalAmount other) {
+        if (this.value > Long.MAX_VALUE - other.value) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "주문 총액이 허용 범위를 초과했습니다.");
+        }
+
+        return new OrderTotalAmount(this.value + other.value);
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/point/Point.java b/apps/commerce-api/src/main/java/com/loopers/domain/point/Point.java
index 2d65da53c..a06c69634 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/point/Point.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/point/Point.java
@@ -4,9 +4,7 @@
 import com.loopers.domain.BaseEntity;
 import com.loopers.support.error.CoreException;
 import com.loopers.support.error.ErrorType;
-import jakarta.persistence.Column;
-import jakarta.persistence.Entity;
-import jakarta.persistence.Table;
+import jakarta.persistence.*;
 import lombok.AccessLevel;
 import lombok.Getter;
 import lombok.NoArgsConstructor;
@@ -20,32 +18,36 @@ public class Point extends BaseEntity {
     @Column(name = "user_id", unique = true, nullable = false, length = 10)
     private String userId;
 
-    @Column(name = "amount", nullable = false)
-    private Long amount;
+    @Embedded
+    private PointBalance balance;
 
-    private Point(String userId, Long amount) {
+    @Version
+    private Long version;
+
+    private Point(String userId, PointBalance balance) {
         validateUserId(userId);
-        validateAmount(amount);
         this.userId = userId;
-        this.amount = amount;
+        this.balance = balance;
     }
 
     public static Point create(String userId) {
-        return new Point(userId, 0L);
+        return new Point(userId, PointBalance.zero());
     }
 
     public static Point create(String userId, Long initialAmount) {
-        return new Point(userId, initialAmount);
+        return new Point(userId, PointBalance.of(initialAmount));
     }
 
     public void charge(Long amount) {
-        if (amount == null) {
-            throw new CoreException(ErrorType.BAD_REQUEST, "포인트 금액은 필수입니다.");
-        }
-        if (amount <= 0) {
-            throw new CoreException(ErrorType.BAD_REQUEST, "충전 금액은 0보다 커야 합니다.");
-        }
-        this.amount += amount;
+        this.balance = this.balance.charge(amount);
+    }
+
+    public void use(Long amount) {
+        this.balance = this.balance.use(amount);
+    }
+
+    public Long getBalanceValue() {
+        return this.balance.getValue();
     }
 
     private void validateUserId(String userId) {
@@ -53,13 +55,4 @@ private void validateUserId(String userId) {
             throw new CoreException(ErrorType.BAD_REQUEST, "사용자 ID는 비어있을 수 없습니다.");
         }
     }
-
-    private void validateAmount(Long amount) {
-        if (amount == null) {
-            throw new CoreException(ErrorType.BAD_REQUEST, "포인트 금액은 필수입니다.");
-        }
-        if (amount < 0) {
-            throw new CoreException(ErrorType.BAD_REQUEST, "포인트 금액은 0 이상이어야 합니다.");
-        }
-    }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/point/PointBalance.java b/apps/commerce-api/src/main/java/com/loopers/domain/point/PointBalance.java
new file mode 100644
index 000000000..da17d6a8d
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/point/PointBalance.java
@@ -0,0 +1,62 @@
+package com.loopers.domain.point;
+
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import jakarta.persistence.Column;
+import jakarta.persistence.Embeddable;
+import lombok.AccessLevel;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+@Getter
+@Embeddable
+@EqualsAndHashCode
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class PointBalance {
+
+    @Column(name = "balance", nullable = false)
+    private Long value;
+
+    private PointBalance(Long value) {
+        validate(value);
+        this.value = value;
+    }
+
+    public static PointBalance of(Long value) {
+        return new PointBalance(value);
+    }
+
+    public static PointBalance zero() {
+        return new PointBalance(0L);
+    }
+
+    private void validate(Long value) {
+        if (value == null) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "포인트 잔액은 필수입니다.");
+        }
+        if (value < 0) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "포인트 잔액은 0 이상이어야 합니다.");
+        }
+    }
+
+    public PointBalance charge(Long amount) {
+        if (amount == null || amount <= 0) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "충전 금액은 0보다 커야 합니다.");
+        }
+        if (this.value > Long.MAX_VALUE - amount) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "충전 가능한 최대 금액을 초과했습니다.");
+        }
+        return new PointBalance(this.value + amount);
+    }
+
+    public PointBalance use(Long amount) {
+        if (amount == null || amount <= 0) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "사용 금액은 0보다 커야 합니다.");
+        }
+        if (this.value < amount) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "보유 포인트가 부족합니다.");
+        }
+        return new PointBalance(this.value - amount);
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/point/PointRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/point/PointRepository.java
index 552172b3d..f0384ebd9 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/point/PointRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/point/PointRepository.java
@@ -6,4 +6,6 @@ public interface PointRepository {
     Point save(Point point);
     Optional<Point> findByUserId(String userId);
     boolean existsByUserId(String userId);
+
+    Optional<Point> findByUserIdWithPessimisticLock(String userId);
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java b/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java
index 8b76d2d32..4f40ec35e 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/point/PointService.java
@@ -27,16 +27,30 @@ public Point getPoint(String userId) {
                 .orElseThrow(() -> new CoreException(ErrorType.NOT_FOUND, "포인트를 찾을 수 없습니다."));
     }
 
-    public Long getPointAmount(String userId) {
-        Point point = getPoint(userId);  // 여기서 예외 발생 가능
-        return point.getAmount();
+    @Transactional
+    public void chargePoint(String userId, Long amount) {
+        Point point = getPoint(userId);
+        point.charge(amount);
+        pointRepository.save(point);
     }
 
     @Transactional
-    public void chargePoint(String userId, Long amount) {
-        Point point = pointRepository.findByUserId(userId)
+    public void usePoint(String userId, Long amount) {
+        Point point = getPoint(userId);
+        point.use(amount);
+        pointRepository.save(point);
+    }
+
+    @Transactional
+    public Point getPointWithPessimisticLock(String userId) {
+        return pointRepository.findByUserIdWithPessimisticLock(userId)
                 .orElseThrow(() -> new CoreException(ErrorType.NOT_FOUND, "포인트를 찾을 수 없습니다."));
-        point.charge(amount);
+    }
+
+    @Transactional
+    public void usePointWithLock(String userId, Long amount) {
+        Point point = getPointWithPessimisticLock(userId);
+        point.use(amount);
         pointRepository.save(point);
     }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/Product.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/Product.java
new file mode 100644
index 000000000..d5604cea3
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/Product.java
@@ -0,0 +1,86 @@
+package com.loopers.domain.product;
+
+import com.loopers.domain.BaseEntity;
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.like.Like;
+import com.loopers.domain.order.OrderItem;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Entity
+@Getter
+@Table(name = "products")
+@NoArgsConstructor(access = lombok.AccessLevel.PROTECTED)
+public class Product extends BaseEntity {
+
+    @Column(name = "name", nullable = false, length = 100)
+    private String name;
+
+    @Embedded
+    private ProductPrice price;
+
+    @Embedded
+    private Stock stock;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "brand_id", nullable = false)
+    private Brand brand;
+
+    @OneToMany(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true)
+    private List<Like> likes = new ArrayList<>();
+
+    @OneToMany(mappedBy = "product", fetch = FetchType.LAZY)
+    private List<OrderItem> orderItems = new ArrayList<>();
+
+    @Version
+    private Long version;
+
+    private Product(String name, ProductPrice price, Integer stock, Brand brand) {
+        validateRequiredFields(name, price, stock, brand);
+        this.name = name;
+        this.price = price;
+        this.stock = Stock.of(stock);
+        this.brand = brand;
+    }
+
+    public static Product create(String name, Long price, Integer stock, Brand brand) {
+        return new Product(name, ProductPrice.of(price), stock, brand);
+    }
+
+    private void validateRequiredFields(String name, ProductPrice price, Integer stock, Brand brand) {
+        if (name == null || name.isBlank()) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "상품명은 필수입니다.");
+        }
+        if (price == null) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "가격은 필수입니다.");
+        }
+        if (stock == null) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "재고는 필수입니다.");
+        }
+        if (brand == null) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "브랜드는 필수입니다.");
+        }
+    }
+
+    public void decreaseStock(Integer quantity) {
+        this.stock = this.stock.decrease(quantity);
+    }
+
+    public boolean isStockAvailable(Integer quantity) {
+        return this.stock.isAvailable(quantity);
+    }
+
+    public Integer getStockValue() {
+        return this.stock.getValue();
+    }
+
+    public Long getPriceValue() {
+        return this.price.getValue();
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductPrice.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductPrice.java
new file mode 100644
index 000000000..a8df4be93
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductPrice.java
@@ -0,0 +1,38 @@
+package com.loopers.domain.product;
+
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import jakarta.persistence.Column;
+import jakarta.persistence.Embeddable;
+import lombok.AccessLevel;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+@Getter
+@Embeddable
+@EqualsAndHashCode
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class ProductPrice {
+
+    @Column(name = "price", nullable = false)
+    private Long value;
+
+    private ProductPrice(Long value) {
+        validate(value);
+        this.value = value;
+    }
+
+    public static ProductPrice of(Long amount) {
+        return new ProductPrice(amount);
+    }
+
+    private void validate(Long value) {
+        if (value == null) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "상품 가격은 필수입니다.");
+        }
+        if (value < 0) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "상품 가격은 0 이상이어야 합니다.");
+        }
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductRepository.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductRepository.java
new file mode 100644
index 000000000..b72f7bdae
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductRepository.java
@@ -0,0 +1,18 @@
+package com.loopers.domain.product;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
+public interface ProductRepository {
+    Product save(Product product);
+    Optional<Product> findById(Long id);
+    List<Product> findAllByIds(Collection<Long> ids);
+    Page<Product> findProducts(Pageable pageable, Long brandId);
+
+    Optional<Product> findByIdWithPessimisticLock(Long id);
+    List<Product> findAllByIdsWithPessimisticLock(Collection<Long> ids);
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductSearchCondition.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductSearchCondition.java
new file mode 100644
index 000000000..672337ae7
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductSearchCondition.java
@@ -0,0 +1,17 @@
+package com.loopers.domain.product;
+
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+
+public record ProductSearchCondition(
+        Long brandId,
+        Pageable pageable
+) {
+    public PageRequest toPageRequest() {
+        return PageRequest.of(
+                pageable.getPageNumber(),
+                pageable.getPageSize(),
+                pageable.getSort()
+        );
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
new file mode 100644
index 000000000..aca710c3b
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
@@ -0,0 +1,51 @@
+package com.loopers.domain.product;
+
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Collection;
+import java.util.List;
+
+@Service
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+public class ProductService {
+
+    private final ProductRepository productRepository;
+
+    public Product getProduct(Long id) {
+        return productRepository.findById(id)
+                .orElseThrow(() -> new CoreException(ErrorType.NOT_FOUND, "상품을 찾을 수 없습니다."));
+    }
+
+    public List<Product> getProductsByIds(Collection<Long> ids) {
+        List<Product> products = productRepository.findAllByIds(ids);
+        if (products.size() != ids.size()) {
+            throw new CoreException(ErrorType.NOT_FOUND, "일부 상품을 찾을 수 없습니다.");
+        }
+        return products;
+    }
+
+    public Page<Product> getProducts(ProductSearchCondition condition) {
+        return productRepository.findProducts(condition.pageable(), condition.brandId());
+    }
+
+    @Transactional
+    public Product getProductWithPessimisticLock(Long id) {
+        return productRepository.findByIdWithPessimisticLock(id)
+                .orElseThrow(() -> new CoreException(ErrorType.NOT_FOUND, "상품을 찾을 수 없습니다."));
+    }
+
+    @Transactional
+    public List<Product> getProductsByIdsWithPessimisticLock(Collection<Long> ids) {
+        List<Product> products = productRepository.findAllByIdsWithPessimisticLock(ids);
+        if (products.size() != ids.size()) {
+            throw new CoreException(ErrorType.NOT_FOUND, "일부 상품을 찾을 수 없습니다.");
+        }
+        return products;
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/Stock.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/Stock.java
new file mode 100644
index 000000000..170ee70ee
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/product/Stock.java
@@ -0,0 +1,53 @@
+package com.loopers.domain.product;
+
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import jakarta.persistence.Column;
+import jakarta.persistence.Embeddable;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+@Getter
+@Embeddable
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class Stock {
+
+    @Column(name = "stock", nullable = false)
+    private Integer value;
+
+    private Stock(Integer value) {
+        validate(value);
+        this.value = value;
+    }
+
+    public static Stock of(Integer value) {
+        return new Stock(value);
+    }
+
+    private void validate(Integer value) {
+        if (value == null) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "재고 수량은 필수입니다.");
+        }
+        if (value < 0) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "재고 수량은 음수일 수 없습니다.");
+        }
+    }
+
+    public Stock decrease(Integer quantity) {
+        if (quantity == null || quantity <= 0) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "차감 수량은 0보다 커야 합니다.");
+        }
+        if (this.value < quantity) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "재고가 부족합니다.");
+        }
+        return new Stock(this.value - quantity);
+    }
+
+    public boolean isAvailable(Integer quantity) {
+        if (quantity == null || quantity <= 0) {
+            throw new CoreException(ErrorType.BAD_REQUEST, "요청 수량은 0보다 커야 합니다.");
+        }
+        return this.value >= quantity;
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/user/User.java b/apps/commerce-api/src/main/java/com/loopers/domain/user/User.java
index e9e968d9b..503794262 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/user/User.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/user/User.java
@@ -18,9 +18,9 @@
 @NoArgsConstructor(access = AccessLevel.PROTECTED)
 public class User extends BaseEntity {
 
-    private static final Pattern USER_ID_PATTERN = Pattern.compile("^[a-zA-Z0-9]{1,10}$");
+    private static final Pattern USER_ID_PATTERN = Pattern.compile("^[a-zA-Z0-9]{1,20}$");
 
-    @Column(name = "user_id", unique = true, nullable = false, length = 10)
+    @Column(name = "user_id", unique = true, nullable = false, length = 20)
     private String userId;
 
     @Embedded
@@ -61,7 +61,7 @@ private void validateUserId(String userId) {
             throw new CoreException(ErrorType.BAD_REQUEST, "ID는 비어있을 수 없습니다.");
         }
         if (!USER_ID_PATTERN.matcher(userId).matches()) {
-            throw new CoreException(ErrorType.BAD_REQUEST, "ID는 영문 및 숫자 10자 이내여야 합니다.");
+            throw new CoreException(ErrorType.BAD_REQUEST, "ID는 영문 및 숫자 20자 이내여야 합니다.");
         }
     }
 
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandJpaRepository.java
new file mode 100644
index 000000000..72daca8e5
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandJpaRepository.java
@@ -0,0 +1,7 @@
+package com.loopers.infrastructure.brand;
+
+import com.loopers.domain.brand.Brand;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface BrandJpaRepository extends JpaRepository<Brand, Long> {
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandRepositoryImpl.java
new file mode 100644
index 000000000..3f48eac6b
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandRepositoryImpl.java
@@ -0,0 +1,25 @@
+package com.loopers.infrastructure.brand;
+
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.brand.BrandRepository;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Repository;
+
+import java.util.Optional;
+
+@Repository
+@RequiredArgsConstructor
+public class BrandRepositoryImpl implements BrandRepository {
+
+    private final BrandJpaRepository brandJpaRepository;
+
+    @Override
+    public Brand save(Brand brand) {
+        return brandJpaRepository.save(brand);
+    }
+
+    @Override
+    public Optional<Brand> findById(Long id) {
+        return brandJpaRepository.findById(id);
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/coupon/CouponJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/coupon/CouponJpaRepository.java
new file mode 100644
index 000000000..8579cbadd
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/coupon/CouponJpaRepository.java
@@ -0,0 +1,24 @@
+package com.loopers.infrastructure.coupon;
+
+import com.loopers.domain.coupon.Coupon;
+import com.loopers.domain.user.User;
+import jakarta.persistence.LockModeType;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Lock;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+import java.util.Optional;
+
+public interface CouponJpaRepository extends JpaRepository<Coupon, Long> {
+
+    @Lock(LockModeType.OPTIMISTIC)
+    @Query("SELECT c FROM Coupon c WHERE c.id = :id")
+    Optional<Coupon> findByIdWithOptimisticLock(@Param("id") Long id);
+
+    @Lock(LockModeType.PESSIMISTIC_WRITE)
+    @Query("SELECT c FROM Coupon c WHERE c.id = :id")
+    Optional<Coupon> findByIdWithPessimisticLock(@Param("id") Long id);
+
+    Optional<Coupon> findByIdAndUser(Long id, User user);
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/coupon/CouponRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/coupon/CouponRepositoryImpl.java
new file mode 100644
index 000000000..75ecada4a
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/coupon/CouponRepositoryImpl.java
@@ -0,0 +1,41 @@
+package com.loopers.infrastructure.coupon;
+
+import com.loopers.domain.coupon.Coupon;
+import com.loopers.domain.coupon.CouponRepository;
+import com.loopers.domain.user.User;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Repository;
+
+import java.util.Optional;
+
+@Repository
+@RequiredArgsConstructor
+public class CouponRepositoryImpl implements CouponRepository {
+
+    private final CouponJpaRepository jpaRepository;
+
+    @Override
+    public Coupon save(Coupon coupon) {
+        return jpaRepository.save(coupon);
+    }
+
+    @Override
+    public Optional<Coupon> findById(Long id) {
+        return jpaRepository.findById(id);
+    }
+
+    @Override
+    public Optional<Coupon> findByIdWithOptimisticLock(Long id) {
+        return jpaRepository.findByIdWithOptimisticLock(id);
+    }
+
+    @Override
+    public Optional<Coupon> findByIdWithPessimisticLock(Long id) {
+        return jpaRepository.findByIdWithPessimisticLock(id);
+    }
+
+    @Override
+    public Optional<Coupon> findByIdAndUser(Long id, User user) {
+        return jpaRepository.findByIdAndUser(id, user);
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/example/ExampleJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/example/ExampleJpaRepository.java
deleted file mode 100644
index ce6d3ead0..000000000
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/example/ExampleJpaRepository.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.loopers.infrastructure.example;
-
-import com.loopers.domain.example.ExampleModel;
-import org.springframework.data.jpa.repository.JpaRepository;
-
-public interface ExampleJpaRepository extends JpaRepository<ExampleModel, Long> {}
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/example/ExampleRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/example/ExampleRepositoryImpl.java
deleted file mode 100644
index 37f2272f0..000000000
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/example/ExampleRepositoryImpl.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.loopers.infrastructure.example;
-
-import com.loopers.domain.example.ExampleModel;
-import com.loopers.domain.example.ExampleRepository;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Component;
-
-import java.util.Optional;
-
-@RequiredArgsConstructor
-@Component
-public class ExampleRepositoryImpl implements ExampleRepository {
-    private final ExampleJpaRepository exampleJpaRepository;
-
-    @Override
-    public Optional<ExampleModel> find(Long id) {
-        return exampleJpaRepository.findById(id);
-    }
-}
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java
new file mode 100644
index 000000000..41377a956
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java
@@ -0,0 +1,23 @@
+package com.loopers.infrastructure.like;
+
+import com.loopers.domain.like.Like;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.user.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+public interface LikeJpaRepository extends JpaRepository<Like, Long> {
+
+    Optional<Like> findByUserAndProduct(User user, Product product);
+    boolean existsByUserAndProduct(User user, Product product);
+    Long countByProduct(Product product);
+
+    @Query("SELECT l.product.id as productId, COUNT(l) as likeCount " +
+            "FROM Like l WHERE l.product IN :products GROUP BY l.product.id")
+    List<Map<String, Object>> findLikeCountsByProducts(@Param("products") List<Product> products);
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeRepositoryImpl.java
new file mode 100644
index 000000000..9ae3ab1cf
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeRepositoryImpl.java
@@ -0,0 +1,59 @@
+package com.loopers.infrastructure.like;
+
+import com.loopers.domain.like.Like;
+import com.loopers.domain.like.LikeRepository;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.user.User;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Repository;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+@Repository
+@RequiredArgsConstructor
+public class LikeRepositoryImpl implements LikeRepository {
+
+    private final LikeJpaRepository jpaRepository;
+
+    @Override
+    public Like save(Like like) {
+        return jpaRepository.save(like);
+    }
+
+    @Override
+    public void delete(Like like) {
+        jpaRepository.delete(like);
+    }
+
+    @Override
+    public Optional<Like> findByUserAndProduct(User user, Product product) {
+        return jpaRepository.findByUserAndProduct(user, product);
+    }
+
+    @Override
+    public boolean existsByUserAndProduct(User user, Product product) {
+        return jpaRepository.existsByUserAndProduct(user, product);
+    }
+
+    @Override
+    public Long countByProduct(Product product) {
+        return jpaRepository.countByProduct(product);
+    }
+
+    @Override
+    public Map<Long, Long> findLikeCounts(List<Product> products) {
+        if (products == null || products.isEmpty()) {
+            return Collections.emptyMap();
+        }
+
+        return jpaRepository.findLikeCountsByProducts(products).stream()
+                .collect(Collectors.toMap(
+                        map -> ((Number) map.get("productId")).longValue(),
+                        map -> ((Number) map.get("likeCount")).longValue()
+                ));
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderJpaRepository.java
new file mode 100644
index 000000000..a94f29570
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderJpaRepository.java
@@ -0,0 +1,22 @@
+package com.loopers.infrastructure.order;
+
+import com.loopers.domain.order.Order;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+import java.util.List;
+import java.util.Optional;
+
+public interface OrderJpaRepository extends JpaRepository<Order, Long> {
+
+    @Query("SELECT DISTINCT o FROM Order o " +
+            "JOIN FETCH o.orderItems oi " +
+            "JOIN FETCH oi.product p " +
+            "JOIN FETCH p.brand " +
+            "WHERE o.id = :orderId AND o.user.id = :userId")
+    Optional<Order> findByIdAndUser(@Param("orderId") Long orderId, @Param("userId") Long userId);
+
+    @Query("SELECT o FROM Order o WHERE o.user.id = :userId ORDER BY o.createdAt DESC")
+    List<Order> findAllByUser(@Param("userId") Long userId);
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderRepositoryImpl.java
new file mode 100644
index 000000000..43ea47af6
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderRepositoryImpl.java
@@ -0,0 +1,31 @@
+package com.loopers.infrastructure.order;
+
+import com.loopers.domain.order.Order;
+import com.loopers.domain.order.OrderRepository;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Optional;
+
+@Repository
+@RequiredArgsConstructor
+public class OrderRepositoryImpl implements OrderRepository {
+
+    private final OrderJpaRepository jpaRepository;
+
+    @Override
+    public Order save(Order order) {
+        return jpaRepository.save(order);
+    }
+
+    @Override
+    public Optional<Order> findByIdAndUser(Long orderId, Long userId) {
+        return jpaRepository.findByIdAndUser(orderId, userId);
+    }
+
+    @Override
+    public List<Order> findAllByUser(Long userId) {
+        return jpaRepository.findAllByUser(userId);
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointJpaRepository.java
index 7c93d9b31..6b6362a38 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointJpaRepository.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointJpaRepository.java
@@ -1,11 +1,19 @@
 package com.loopers.infrastructure.point;
 
 import com.loopers.domain.point.Point;
+import jakarta.persistence.LockModeType;
 import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Lock;
+import org.springframework.data.jpa.repository.Query;
 
 import java.util.Optional;
 
 public interface PointJpaRepository extends JpaRepository<Point, Long> {
     Optional<Point> findByUserId(String userId);
+
     boolean existsByUserId(String userId);
+
+    @Lock(LockModeType.PESSIMISTIC_WRITE)
+    @Query("SELECT p FROM Point p WHERE p.userId = :userId")
+    Optional<Point> findByUserIdWithPessimisticLock(String userId);
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointRepositoryImpl.java
index 9f7324e48..9f5309937 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointRepositoryImpl.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/point/PointRepositoryImpl.java
@@ -27,4 +27,9 @@ public Optional<Point> findByUserId(String userId) {
     public boolean existsByUserId(String userId) {
         return pointJpaRepository.existsByUserId(userId);
     }
+
+    @Override
+    public Optional<Point> findByUserIdWithPessimisticLock(String userId) {
+        return pointJpaRepository.findByUserIdWithPessimisticLock(userId);
+    }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductJpaRepository.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductJpaRepository.java
new file mode 100644
index 000000000..ea6a242a8
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductJpaRepository.java
@@ -0,0 +1,34 @@
+package com.loopers.infrastructure.product;
+
+import com.loopers.domain.product.Product;
+import jakarta.persistence.LockModeType;
+import org.springframework.data.jpa.repository.EntityGraph;
+import org.springframework.data.jpa.repository.Lock;
+import org.springframework.data.repository.query.Param;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
+public interface ProductJpaRepository extends JpaRepository<Product, Long> {
+
+    @Query("SELECT p FROM Product p WHERE p.id IN :ids")
+    List<Product> findAllByIds(@Param("ids") Collection<Long> ids);
+
+    @EntityGraph(attributePaths = {"brand"})
+    @Query("SELECT p FROM Product p " +
+            "WHERE (:brandId IS NULL OR p.brand.id = :brandId)")
+    Page<Product> findProducts(@Param("brandId") Long brandId, Pageable pageable);
+
+    @Lock(LockModeType.PESSIMISTIC_WRITE)
+    @Query("SELECT p FROM Product p WHERE p.id = :id")
+    Optional<Product> findByIdWithPessimisticLock(@Param("id") Long id);
+
+    @Lock(LockModeType.PESSIMISTIC_WRITE)
+    @Query("SELECT p FROM Product p WHERE p.id IN :ids")
+    List<Product> findAllByIdsWithPessimisticLock(@Param("ids") Collection<Long> ids);
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductRepositoryImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductRepositoryImpl.java
new file mode 100644
index 000000000..b50081489
--- /dev/null
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductRepositoryImpl.java
@@ -0,0 +1,49 @@
+package com.loopers.infrastructure.product;
+
+import com.loopers.domain.product.Product;
+import com.loopers.domain.product.ProductRepository;
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Repository;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
+@Repository
+@RequiredArgsConstructor
+public class ProductRepositoryImpl implements ProductRepository {
+
+    private final ProductJpaRepository jpaRepository;
+
+    @Override
+    public Product save(Product product) {
+        return jpaRepository.save(product);
+    }
+
+    @Override
+    public Optional<Product> findById(Long id) {
+        return jpaRepository.findById(id);
+    }
+
+    @Override
+    public List<Product> findAllByIds(Collection<Long> ids) {
+        return jpaRepository.findAllByIds(ids);
+    }
+
+    @Override
+    public Page<Product> findProducts(Pageable pageable, Long brandId) {
+        return jpaRepository.findProducts(brandId, pageable);
+    }
+
+    @Override
+    public Optional<Product> findByIdWithPessimisticLock(Long id) {
+        return jpaRepository.findByIdWithPessimisticLock(id);
+    }
+
+    @Override
+    public List<Product> findAllByIdsWithPessimisticLock(Collection<Long> ids) {
+        return jpaRepository.findAllByIdsWithPessimisticLock(ids);
+    }
+}
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/example/ExampleV1ApiSpec.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/example/ExampleV1ApiSpec.java
deleted file mode 100644
index 219e3101e..000000000
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/example/ExampleV1ApiSpec.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.loopers.interfaces.api.example;
-
-import com.loopers.interfaces.api.ApiResponse;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.media.Schema;
-import io.swagger.v3.oas.annotations.tags.Tag;
-
-@Tag(name = "Example V1 API", description = "Loopers 예시 API 입니다.")
-public interface ExampleV1ApiSpec {
-
-    @Operation(
-        summary = "예시 조회",
-        description = "ID로 예시를 조회합니다."
-    )
-    ApiResponse<ExampleV1Dto.ExampleResponse> getExample(
-        @Schema(name = "예시 ID", description = "조회할 예시의 ID")
-        Long exampleId
-    );
-}
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/example/ExampleV1Controller.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/example/ExampleV1Controller.java
deleted file mode 100644
index 917376016..000000000
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/example/ExampleV1Controller.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.loopers.interfaces.api.example;
-
-import com.loopers.application.example.ExampleFacade;
-import com.loopers.application.example.ExampleInfo;
-import com.loopers.interfaces.api.ApiResponse;
-import lombok.RequiredArgsConstructor;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-@RequiredArgsConstructor
-@RestController
-@RequestMapping("/api/v1/examples")
-public class ExampleV1Controller implements ExampleV1ApiSpec {
-
-    private final ExampleFacade exampleFacade;
-
-    @GetMapping("/{exampleId}")
-    @Override
-    public ApiResponse<ExampleV1Dto.ExampleResponse> getExample(
-        @PathVariable(value = "exampleId") Long exampleId
-    ) {
-        ExampleInfo info = exampleFacade.getExample(exampleId);
-        ExampleV1Dto.ExampleResponse response = ExampleV1Dto.ExampleResponse.from(info);
-        return ApiResponse.success(response);
-    }
-}
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/example/ExampleV1Dto.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/example/ExampleV1Dto.java
deleted file mode 100644
index 4ecf0eea5..000000000
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/example/ExampleV1Dto.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.loopers.interfaces.api.example;
-
-import com.loopers.application.example.ExampleInfo;
-
-public class ExampleV1Dto {
-    public record ExampleResponse(Long id, String name, String description) {
-        public static ExampleResponse from(ExampleInfo info) {
-            return new ExampleResponse(
-                info.id(),
-                info.name(),
-                info.description()
-            );
-        }
-    }
-}
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/point/PointV1Controller.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/point/PointV1Controller.java
index 7209911d5..dfcc30b4c 100644
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/point/PointV1Controller.java
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/point/PointV1Controller.java
@@ -1,5 +1,6 @@
 package com.loopers.interfaces.api.point;
 
+import com.loopers.application.point.PointCommand;
 import com.loopers.application.point.PointFacade;
 import com.loopers.domain.point.Point;
 import com.loopers.interfaces.api.ApiResponse;
@@ -25,8 +26,8 @@ public ApiResponse<PointV1Dto.PointResponse> getPoint(
             throw new CoreException(ErrorType.BAD_REQUEST, "X-USER-ID 헤더는 필수입니다.");
         }
 
-        Long amount = pointFacade.getPointAmount(userId);
-        return ApiResponse.success(PointV1Dto.PointResponse.of(userId, amount));
+        Point point = pointFacade.getPoint(userId);
+        return ApiResponse.success(PointV1Dto.PointResponse.of(userId, point.getBalanceValue()));
     }
 
     @PostMapping("/charge")
@@ -39,9 +40,11 @@ public ApiResponse<PointV1Dto.PointResponse> chargePoint(
             throw new CoreException(ErrorType.BAD_REQUEST, "X-USER-ID 헤더는 필수입니다.");
         }
 
-        pointFacade.chargePoint(userId, request.amount());
+        PointCommand command = request.toCommand(userId);
+
+        pointFacade.chargePoint(command);
         Point point = pointFacade.getPoint(userId);
 
-        return ApiResponse.success(PointV1Dto.PointResponse.of(userId, point.getAmount()));
+        return ApiResponse.success(PointV1Dto.PointResponse.of(userId, point.getBalanceValue()));
     }
 }
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/point/PointV1Dto.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/point/PointV1Dto.java
index 108b7a161..376683728 100644
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/point/PointV1Dto.java
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/point/PointV1Dto.java
@@ -1,5 +1,6 @@
 package com.loopers.interfaces.api.point;
 
+import com.loopers.application.point.PointCommand;
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Positive;
 
@@ -9,6 +10,12 @@ public record ChargeRequest(
             @Positive(message = "충전 금액은 0보다 커야 합니다.")
             Long amount
     ) {
+        public PointCommand toCommand(String userId) {
+            return new PointCommand(
+                    userId,
+                    this.amount
+            );
+        }
     }
 
     public record PointResponse(
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/user/UserV1Controller.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/user/UserV1Controller.java
index de62d826b..636b49940 100644
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/user/UserV1Controller.java
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/user/UserV1Controller.java
@@ -1,5 +1,6 @@
 package com.loopers.interfaces.api.user;
 
+import com.loopers.application.user.UserCommand;
 import com.loopers.application.user.UserFacade;
 import com.loopers.application.user.UserInfo;
 import com.loopers.interfaces.api.ApiResponse;
@@ -20,12 +21,8 @@ public class UserV1Controller implements UserV1ApiSpec {
     public ApiResponse<UserV1Dto.UserResponse> signUp(
             @RequestBody @Valid UserV1Dto.RegisterRequest request
     ) {
-        UserInfo userInfo = userFacade.signUp(
-                request.userId(),
-                request.email(),
-                request.birthDate(),
-                request.gender()
-        );
+        UserCommand command = request.toCommand();
+        UserInfo userInfo = userFacade.signUp(command);
         return ApiResponse.success(UserV1Dto.UserResponse.from(userInfo));
     }
 
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/user/UserV1Dto.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/user/UserV1Dto.java
index efe1f1468..1b9b48f9a 100644
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/user/UserV1Dto.java
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/user/UserV1Dto.java
@@ -1,5 +1,6 @@
 package com.loopers.interfaces.api.user;
 
+import com.loopers.application.user.UserCommand;
 import com.loopers.application.user.UserInfo;
 import com.loopers.domain.user.Gender;
 import jakarta.validation.constraints.NotBlank;
@@ -23,6 +24,14 @@ public record RegisterRequest(
             @NotNull(message = "성별은 필수입니다.")
             Gender gender
     ) {
+        public UserCommand toCommand() {
+            return new UserCommand(
+                    this.userId,
+                    this.email,
+                    this.birthDate,
+                    this.gender
+            );
+        }
     }
 
     public record UserResponse(
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandServiceIntegrationTest.java
new file mode 100644
index 000000000..d5469b432
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandServiceIntegrationTest.java
@@ -0,0 +1,61 @@
+package com.loopers.domain.brand;
+
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import com.loopers.utils.DatabaseCleanUp;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@SpringBootTest
+class BrandServiceIntegrationTest {
+
+    @Autowired
+    private BrandService brandService;
+
+    @MockitoSpyBean
+    private BrandRepository brandRepository;
+
+    @Autowired
+    private DatabaseCleanUp databaseCleanUp;
+
+    @AfterEach
+    void tearDown() {
+        databaseCleanUp.truncateAllTables();
+    }
+
+    @DisplayName("ID로 브랜드 조회를 할 수 있다.")
+    @Test
+    void getBrand() {
+        // arrange
+        Brand savedBrand = brandRepository.save(Brand.create("Test Brand"));
+
+        // act
+        Brand foundBrand = brandService.getBrand(savedBrand.getId());
+
+        // assert
+        assertAll(
+                () -> assertThat(foundBrand).isNotNull(),
+                () -> assertThat(foundBrand.getId()).isEqualTo(savedBrand.getId()),
+                () -> assertThat(foundBrand.getName()).isEqualTo("Test Brand")
+        );
+    }
+
+    @DisplayName("존재하지 않는 ID로 브랜드 조회 시, NOT_FOUND 예외가 발생한다.")
+    @Test
+    void getBrand_throwsNotFound() {
+        // act & assert
+        CoreException exception = assertThrows(CoreException.class, () -> {
+            brandService.getBrand(-1L);
+        });
+
+        assertThat(exception.getErrorType()).isEqualTo(ErrorType.NOT_FOUND);
+    }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandTest.java
new file mode 100644
index 000000000..badc1f8be
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/brand/BrandTest.java
@@ -0,0 +1,50 @@
+package com.loopers.domain.brand;
+
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class BrandTest {
+
+    @Nested
+    @DisplayName("브랜드 생성")
+    class CreateBrand {
+
+        @DisplayName("유효한 이름으로 브랜드를 생성할 수 있다.")
+        @Test
+        void createBrand() {
+            // act
+            Brand brand = Brand.create("Test Brand");
+            // assert
+            assertThat(brand.getName()).isEqualTo("Test Brand");
+        }
+
+        @DisplayName("브랜드명이 null이거나 공백이면 BAD_REQUEST 예외가 발생한다.")
+        @ParameterizedTest
+        @ValueSource(strings = {"", "   "})
+        void throwsException_whenNameIsBlank(String invalidName) {
+            // act & assert
+            assertThatThrownBy(() -> Brand.create(invalidName))
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+        }
+
+        @DisplayName("브랜드명이 null이어도 BAD_REQUEST 예외가 발생한다.")
+        @Test
+        void throwsException_whenNameIsNull() {
+            // act & assert
+            assertThatThrownBy(() -> Brand.create(null))
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+        }
+    }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/coupon/CouponConcurrencyTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/coupon/CouponConcurrencyTest.java
new file mode 100644
index 000000000..17d7f1d74
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/coupon/CouponConcurrencyTest.java
@@ -0,0 +1,101 @@
+package com.loopers.domain.coupon;
+
+import com.loopers.domain.user.Gender;
+import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserService;
+import com.loopers.utils.DatabaseCleanUp;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.orm.ObjectOptimisticLockingFailureException;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@SpringBootTest
+class CouponConcurrencyTest {
+
+    @Autowired
+    private CouponService couponService;
+
+    @Autowired
+    private CouponRepository couponRepository;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private DatabaseCleanUp databaseCleanUp;
+
+    private User user;
+
+    @BeforeEach
+    void setUp() {
+        databaseCleanUp.truncateAllTables();
+        user = userService.signUp("testUser", "test@mail.com", "1990-01-01", Gender.MALE);
+    }
+
+    @AfterEach
+    void tearDown() {
+        databaseCleanUp.truncateAllTables();
+    }
+
+    @DisplayName("낙관적 락: 동일 쿠폰을 100번 동시 사용 시도하면, 1번만 성공한다")
+    @Test
+    void shouldAllowOnlyOneUse_whenHundredThreadsUseOptimisticLock() throws InterruptedException {
+        // given
+        Coupon coupon = couponRepository.save(
+                Coupon.create(user, "5000원 할인", DiscountType.FIXED_AMOUNT, 5000L)
+        );
+
+        int threadCount = 100;
+        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
+        CountDownLatch latch = new CountDownLatch(threadCount);
+        AtomicInteger successCount = new AtomicInteger(0);
+        AtomicInteger optimisticLockFailCount = new AtomicInteger(0);
+        AtomicInteger alreadyUsedFailCount = new AtomicInteger(0);
+
+        // act
+        for (int i = 0; i < threadCount; i++) {
+            executor.submit(() -> {
+                try {
+                    Coupon foundCoupon = couponService.getCouponWithOptimisticLock(coupon.getId());
+                    foundCoupon.use();
+                    couponService.save(foundCoupon);
+                    successCount.incrementAndGet();
+                } catch (ObjectOptimisticLockingFailureException e) {
+                    // version 충돌로 실패
+                    optimisticLockFailCount.incrementAndGet();
+                } catch (Exception e) {
+                    // 이미 사용된 쿠폰 예외
+                    if (e.getMessage() != null && e.getMessage().contains("이미 사용된 쿠폰")) {
+                        alreadyUsedFailCount.incrementAndGet();
+                    }
+                } finally {
+                    latch.countDown();
+                }
+            });
+        }
+
+        latch.await(10, TimeUnit.SECONDS);
+        executor.shutdown();
+        executor.awaitTermination(5, TimeUnit.SECONDS);
+
+        // assert
+        Coupon finalCoupon = couponRepository.findById(coupon.getId()).orElseThrow();
+
+        assertThat(successCount.get()).isEqualTo(1);
+        assertThat(successCount.get() + optimisticLockFailCount.get() + alreadyUsedFailCount.get())
+                .isEqualTo(threadCount);
+        assertThat(finalCoupon.getIsUsed()).isTrue();
+        assertThat(finalCoupon.canUse()).isFalse();
+    }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/coupon/CouponServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/coupon/CouponServiceIntegrationTest.java
new file mode 100644
index 000000000..8262e0a75
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/coupon/CouponServiceIntegrationTest.java
@@ -0,0 +1,217 @@
+package com.loopers.domain.coupon;
+
+import com.loopers.domain.user.Gender;
+import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserService;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import com.loopers.utils.DatabaseCleanUp;
+import org.junit.jupiter.api.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+@SpringBootTest
+class CouponServiceIntegrationTest {
+
+    @Autowired
+    private CouponService couponService;
+
+    @MockitoSpyBean
+    private CouponRepository couponRepository;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private DatabaseCleanUp databaseCleanUp;
+
+    private User user1;
+    private User user2;
+
+    @BeforeEach
+    void setUp() {
+        user1 = userService.signUp("ser01", "coupon1@mail.com", "1990-01-01", Gender.MALE);
+        user2 = userService.signUp("user02", "coupon2@mail.com", "1990-01-02", Gender.FEMALE);
+    }
+
+    @AfterEach
+    void tearDown() {
+        databaseCleanUp.truncateAllTables();
+    }
+
+    @DisplayName("쿠폰 저장")
+    @Nested
+    class SaveCoupon {
+
+        @DisplayName("쿠폰을 저장할 수 있다.")
+        @Test
+        void shouldSaveCouponSuccessfully() {
+            // arrange
+            Coupon coupon = Coupon.create(user1, "5000원 할인", DiscountType.FIXED_AMOUNT, 5000L);
+
+            // act
+            Coupon savedCoupon = couponService.save(coupon);
+
+            // assert
+            verify(couponRepository, times(1)).save(any(Coupon.class));
+            assertAll(
+                    () -> assertThat(savedCoupon).isNotNull(),
+                    () -> assertThat(savedCoupon.getId()).isNotNull(),
+                    () -> assertThat(savedCoupon.getUser()).isEqualTo(user1),
+                    () -> assertThat(savedCoupon.getName()).isEqualTo("5000원 할인"),
+                    () -> assertThat(savedCoupon.getIsUsed()).isFalse()
+            );
+        }
+    }
+
+    @DisplayName("쿠폰 조회 (비관적 락)")
+    @Nested
+    class GetCouponWithPessimisticLock {
+
+        @DisplayName("비관적 락으로 쿠폰을 조회할 수 있다.")
+        @Test
+        void shouldRetrieveCouponWithPessimisticLock_successfully() {
+            // arrange
+            Coupon coupon = Coupon.create(user1, "5000원 할인", DiscountType.FIXED_AMOUNT, 5000L);
+            Coupon savedCoupon = couponService.save(coupon);
+
+            // act
+            Coupon foundCoupon = couponService.getCouponWithPessimisticLock(savedCoupon.getId());
+
+            // assert
+            verify(couponRepository, times(1)).findByIdWithPessimisticLock(savedCoupon.getId());
+            assertAll(
+                    () -> assertThat(foundCoupon).isNotNull(),
+                    () -> assertThat(foundCoupon.getId()).isEqualTo(savedCoupon.getId()),
+                    () -> assertThat(foundCoupon.getName()).isEqualTo("5000원 할인")
+            );
+        }
+
+        @DisplayName("존재하지 않는 쿠폰 ID로 조회 시 NOT_FOUND 예외가 발생한다.")
+        @Test
+        void throwsNotFoundException_whenCouponNotExists() {
+            // act & assert
+            CoreException exception = assertThrows(CoreException.class, () -> {
+                couponService.getCouponWithPessimisticLock(-99L);
+            });
+
+            assertThat(exception.getErrorType()).isEqualTo(ErrorType.NOT_FOUND);
+        }
+    }
+
+    @DisplayName("쿠폰 조회 (낙관적 락)")
+    @Nested
+    class GetCouponWithOptimisticLock {
+
+        @DisplayName("낙관적 락으로 쿠폰을 조회할 수 있다.")
+        @Test
+        void getCouponWithOptimisticLock() {
+            // arrange
+            Coupon coupon = Coupon.create(user1, "20% 할인", DiscountType.PERCENTAGE, 20L);
+            Coupon savedCoupon = couponService.save(coupon);
+
+            // act
+            Coupon foundCoupon = couponService.getCouponWithOptimisticLock(savedCoupon.getId());
+
+            // assert
+            verify(couponRepository, times(1)).findByIdWithOptimisticLock(savedCoupon.getId());
+            assertAll(
+                    () -> assertThat(foundCoupon).isNotNull(),
+                    () -> assertThat(foundCoupon.getId()).isEqualTo(savedCoupon.getId()),
+                    () -> assertThat(foundCoupon.getName()).isEqualTo("20% 할인")
+            );
+        }
+    }
+
+    @DisplayName("쿠폰 사용 가능 여부 검증 (validateCouponUsable)")
+    @Nested
+    class ValidateCouponUsable {
+
+        @DisplayName("본인 소유의 미사용 쿠폰은 검증을 통과한다.")
+        @Test
+        void validateCouponUsable_success() {
+            // arrange
+            Coupon coupon = Coupon.create(user1, "5000원 할인", DiscountType.FIXED_AMOUNT, 5000L);
+            Coupon savedCoupon = couponService.save(coupon);
+
+            // act & assert - 예외 발생하지 않음
+            couponService.validateCouponUsable(savedCoupon, user1);
+        }
+
+        @DisplayName("타인 소유의 쿠폰은 BAD_REQUEST 예외가 발생한다.")
+        @Test
+        void throwsBadRequestException_whenNotOwner() {
+            // arrange
+            Coupon coupon = Coupon.create(user1, "5000원 할인", DiscountType.FIXED_AMOUNT, 5000L);
+            Coupon savedCoupon = couponService.save(coupon);
+
+            // act & assert
+            CoreException exception = assertThrows(CoreException.class, () -> {
+                couponService.validateCouponUsable(savedCoupon, user2);
+            });
+
+            assertThat(exception.getErrorType()).isEqualTo(ErrorType.BAD_REQUEST);
+        }
+
+        @DisplayName("이미 사용된 쿠폰은 BAD_REQUEST 예외가 발생한다.")
+        @Test
+        void throwsBadRequestException_whenAlreadyUsed() {
+            // arrange
+            Coupon coupon = Coupon.create(user1, "5000원 할인", DiscountType.FIXED_AMOUNT, 5000L);
+            coupon.use(); // 쿠폰 사용 처리
+            Coupon savedCoupon = couponService.save(coupon);
+
+            // act & assert
+            CoreException exception = assertThrows(CoreException.class, () -> {
+                couponService.validateCouponUsable(savedCoupon, user1);
+            });
+
+            assertThat(exception.getErrorType()).isEqualTo(ErrorType.BAD_REQUEST);
+        }
+    }
+
+    @DisplayName("쿠폰 사용 흐름")
+    @Nested
+    class CouponUsageFlow {
+
+        @DisplayName("쿠폰을 사용하고 저장하면 데이터베이스에 반영된다.")
+        @Test
+        void useCoupon_andSave() {
+            // arrange
+            Coupon coupon = Coupon.create(user1, "5000원 할인", DiscountType.FIXED_AMOUNT, 5000L);
+            Coupon savedCoupon = couponService.save(coupon);
+
+            // act
+            savedCoupon.use();
+            couponService.save(savedCoupon);
+
+            // assert
+            Coupon foundCoupon = couponRepository.findById(savedCoupon.getId()).orElseThrow();
+            assertAll(
+                    () -> assertThat(foundCoupon.getIsUsed()).isTrue(),
+                    () -> assertThat(foundCoupon.canUse()).isFalse()
+            );
+        }
+
+        @DisplayName("쿠폰 사용 후 재사용 시도 시 BAD_REQUEST 예외가 발생한다.")
+        @Test
+        void throwsException_whenReuseUsedCoupon() {
+            // arrange
+            Coupon coupon = Coupon.create(user1, "5000원 할인", DiscountType.FIXED_AMOUNT, 5000L);
+            Coupon savedCoupon = couponService.save(coupon);
+            savedCoupon.use();
+            couponService.save(savedCoupon);
+
+            // act & assert
+            Coupon foundCoupon = couponRepository.findById(savedCoupon.getId()).orElseThrow();
+            assertThrows(CoreException.class, foundCoupon::use);
+        }
+    }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/coupon/CouponTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/coupon/CouponTest.java
new file mode 100644
index 000000000..cde2862fe
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/coupon/CouponTest.java
@@ -0,0 +1,264 @@
+package com.loopers.domain.coupon;
+
+import com.loopers.domain.user.Gender;
+import com.loopers.domain.user.User;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class CouponTest {
+
+    private User dummyUser;
+
+    @BeforeEach
+    void setUp() {
+        dummyUser = User.create("testuser", "test@mail.com", "1990-01-01", Gender.MALE);
+    }
+
+    @Nested
+    @DisplayName("쿠폰 생성 (Coupon.create)")
+    class CreateCoupon {
+
+        @DisplayName("정액 할인 쿠폰을 생성할 수 있다.")
+        @Test
+        void createFixedAmountCoupon() {
+            // act
+            Coupon coupon = Coupon.create(dummyUser, "5000원 할인", DiscountType.FIXED_AMOUNT, 5000L);
+
+            // assert
+            assertThat(coupon.getUser()).isEqualTo(dummyUser);
+            assertThat(coupon.getName()).isEqualTo("5000원 할인");
+            assertThat(coupon.getDiscountType()).isEqualTo(DiscountType.FIXED_AMOUNT);
+            assertThat(coupon.getDiscountValue()).isEqualTo(5000L);
+            assertThat(coupon.getIsUsed()).isFalse();
+            assertThat(coupon.canUse()).isTrue();
+        }
+
+        @DisplayName("정률 할인 쿠폰을 생성할 수 있다.")
+        @Test
+        void createPercentageCoupon() {
+            // act
+            Coupon coupon = Coupon.create(dummyUser, "20% 할인", DiscountType.PERCENTAGE, 20L);
+
+            // assert
+            assertThat(coupon.getUser()).isEqualTo(dummyUser);
+            assertThat(coupon.getName()).isEqualTo("20% 할인");
+            assertThat(coupon.getDiscountType()).isEqualTo(DiscountType.PERCENTAGE);
+            assertThat(coupon.getDiscountValue()).isEqualTo(20L);
+            assertThat(coupon.getIsUsed()).isFalse();
+        }
+
+        @DisplayName("사용자가 null이면 BAD_REQUEST 예외가 발생한다.")
+        @Test
+        void throwsException_whenUserIsNull() {
+            // act & assert
+            assertThatThrownBy(() -> Coupon.create(null, "쿠폰", DiscountType.FIXED_AMOUNT, 1000L))
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+        }
+
+        @DisplayName("쿠폰명이 null이면 BAD_REQUEST 예외가 발생한다.")
+        @Test
+        void throwsException_whenNameIsNull() {
+            // act & assert
+            assertThatThrownBy(() -> Coupon.create(dummyUser, null, DiscountType.FIXED_AMOUNT, 1000L))
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+        }
+
+        @DisplayName("쿠폰명이 빈 문자열이면 BAD_REQUEST 예외가 발생한다.")
+        @Test
+        void throwsException_whenNameIsBlank() {
+            // act & assert
+            assertThatThrownBy(() -> Coupon.create(dummyUser, "  ", DiscountType.FIXED_AMOUNT, 1000L))
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+        }
+
+        @DisplayName("할인 타입이 null이면 BAD_REQUEST 예외가 발생한다.")
+        @Test
+        void throwsException_whenDiscountTypeIsNull() {
+            // act & assert
+            assertThatThrownBy(() -> Coupon.create(dummyUser, "쿠폰", null, 1000L))
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+        }
+
+        @DisplayName("할인 값이 null이면 BAD_REQUEST 예외가 발생한다.")
+        @Test
+        void throwsException_whenDiscountValueIsNull() {
+            // act & assert
+            assertThatThrownBy(() -> Coupon.create(dummyUser, "쿠폰", DiscountType.FIXED_AMOUNT, null))
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+        }
+
+        @DisplayName("할인 값이 0 이하면 BAD_REQUEST 예외가 발생한다.")
+        @Test
+        void throwsException_whenDiscountValueIsZeroOrNegative() {
+            // act & assert
+            assertThatThrownBy(() -> Coupon.create(dummyUser, "쿠폰", DiscountType.FIXED_AMOUNT, 0L))
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+
+            assertThatThrownBy(() -> Coupon.create(dummyUser, "쿠폰", DiscountType.FIXED_AMOUNT, -1000L))
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+        }
+
+        @DisplayName("정률 할인이 100%를 초과하면 BAD_REQUEST 예외가 발생한다.")
+        @Test
+        void throwsException_whenPercentageExceeds100() {
+            // act & assert
+            assertThatThrownBy(() -> Coupon.create(dummyUser, "쿠폰", DiscountType.PERCENTAGE, 101L))
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+        }
+    }
+
+    @Nested
+    @DisplayName("할인 금액 계산 (calculateDiscount)")
+    class CalculateDiscount {
+
+        @DisplayName("정액 할인 쿠폰은 고정 금액을 할인한다.")
+        @Test
+        void calculateDiscount_fixedAmount() {
+            // arrange
+            Coupon coupon = Coupon.create(dummyUser, "5000원 할인", DiscountType.FIXED_AMOUNT, 5000L);
+
+            // act
+            Long discount = coupon.calculateDiscount(10000L);
+
+            // assert
+            assertThat(discount).isEqualTo(5000L);
+        }
+
+        @DisplayName("정액 할인 금액이 주문 금액보다 크면 주문 금액만큼만 할인한다.")
+        @Test
+        void calculateDiscount_fixedAmount_exceedsOriginal() {
+            // arrange
+            Coupon coupon = Coupon.create(dummyUser, "5000원 할인", DiscountType.FIXED_AMOUNT, 5000L);
+
+            // act
+            Long discount = coupon.calculateDiscount(3000L);
+
+            // assert
+            assertThat(discount).isEqualTo(3000L); // 원래 금액보다 많이 할인하지 않음
+        }
+
+        @DisplayName("정률 할인 쿠폰은 비율만큼 할인한다.")
+        @Test
+        void calculateDiscount_percentage() {
+            // arrange
+            Coupon coupon = Coupon.create(dummyUser, "20% 할인", DiscountType.PERCENTAGE, 20L);
+
+            // act
+            Long discount = coupon.calculateDiscount(10000L);
+
+            // assert
+            assertThat(discount).isEqualTo(2000L); // 10000 * 20% = 2000
+        }
+
+        @DisplayName("정률 할인 쿠폰 계산 시 소수점은 버린다.")
+        @Test
+        void calculateDiscount_percentage_truncatesDecimal() {
+            // arrange
+            Coupon coupon = Coupon.create(dummyUser, "15% 할인", DiscountType.PERCENTAGE, 15L);
+
+            // act
+            Long discount = coupon.calculateDiscount(10000L);
+
+            // assert
+            assertThat(discount).isEqualTo(1500L); // 10000 * 15% = 1500
+        }
+
+        @DisplayName("이미 사용된 쿠폰으로 할인 계산 시 BAD_REQUEST 예외가 발생한다.")
+        @Test
+        void throwsException_whenCouponAlreadyUsed() {
+            // arrange
+            Coupon coupon = Coupon.create(dummyUser, "5000원 할인", DiscountType.FIXED_AMOUNT, 5000L);
+            coupon.use();
+
+            // act & assert
+            assertThatThrownBy(() -> coupon.calculateDiscount(10000L))
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+        }
+    }
+
+    @Nested
+    @DisplayName("쿠폰 사용 (use)")
+    class UseCoupon {
+
+        @DisplayName("쿠폰을 사용할 수 있다.")
+        @Test
+        void useCoupon() {
+            // arrange
+            Coupon coupon = Coupon.create(dummyUser, "5000원 할인", DiscountType.FIXED_AMOUNT, 5000L);
+            assertThat(coupon.getIsUsed()).isFalse();
+            assertThat(coupon.canUse()).isTrue();
+
+            // act
+            coupon.use();
+
+            // assert
+            assertThat(coupon.getIsUsed()).isTrue();
+            assertThat(coupon.canUse()).isFalse();
+        }
+
+        @DisplayName("이미 사용된 쿠폰을 다시 사용하려 하면 BAD_REQUEST 예외가 발생한다.")
+        @Test
+        void throwsException_whenAlreadyUsed() {
+            // arrange
+            Coupon coupon = Coupon.create(dummyUser, "5000원 할인", DiscountType.FIXED_AMOUNT, 5000L);
+            coupon.use();
+
+            // act & assert
+            assertThatThrownBy(coupon::use)
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+        }
+    }
+
+    @Nested
+    @DisplayName("쿠폰 사용 가능 여부 (canUse)")
+    class CanUseCoupon {
+
+        @DisplayName("새로 생성된 쿠폰은 사용 가능하다.")
+        @Test
+        void newCoupon_canUse() {
+            // arrange
+            Coupon coupon = Coupon.create(dummyUser, "5000원 할인", DiscountType.FIXED_AMOUNT, 5000L);
+
+            // act & assert
+            assertThat(coupon.canUse()).isTrue();
+        }
+
+        @DisplayName("사용된 쿠폰은 사용 불가능하다.")
+        @Test
+        void usedCoupon_cannotUse() {
+            // arrange
+            Coupon coupon = Coupon.create(dummyUser, "5000원 할인", DiscountType.FIXED_AMOUNT, 5000L);
+            coupon.use();
+
+            // act & assert
+            assertThat(coupon.canUse()).isFalse();
+        }
+    }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/example/ExampleModelTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/example/ExampleModelTest.java
deleted file mode 100644
index 44ca7576e..000000000
--- a/apps/commerce-api/src/test/java/com/loopers/domain/example/ExampleModelTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package com.loopers.domain.example;
-
-import com.loopers.support.error.CoreException;
-import com.loopers.support.error.ErrorType;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.assertAll;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-class ExampleModelTest {
-    @DisplayName("예시 모델을 생성할 때, ")
-    @Nested
-    class Create {
-        @DisplayName("제목과 설명이 모두 주어지면, 정상적으로 생성된다.")
-        @Test
-        void createsExampleModel_whenNameAndDescriptionAreProvided() {
-            // arrange
-            String name = "제목";
-            String description = "설명";
-
-            // act
-            ExampleModel exampleModel = new ExampleModel(name, description);
-
-            // assert
-            assertAll(
-                () -> assertThat(exampleModel.getId()).isNotNull(),
-                () -> assertThat(exampleModel.getName()).isEqualTo(name),
-                () -> assertThat(exampleModel.getDescription()).isEqualTo(description)
-            );
-        }
-
-        @DisplayName("제목이 빈칸으로만 이루어져 있으면, BAD_REQUEST 예외가 발생한다.")
-        @Test
-        void throwsBadRequestException_whenTitleIsBlank() {
-            // arrange
-            String name = "   ";
-
-            // act
-            CoreException result = assertThrows(CoreException.class, () -> {
-                new ExampleModel(name, "설명");
-            });
-
-            // assert
-            assertThat(result.getErrorType()).isEqualTo(ErrorType.BAD_REQUEST);
-        }
-
-        @DisplayName("설명이 비어있으면, BAD_REQUEST 예외가 발생한다.")
-        @Test
-        void throwsBadRequestException_whenDescriptionIsEmpty() {
-            // arrange
-            String description = "";
-
-            // act
-            CoreException result = assertThrows(CoreException.class, () -> {
-                new ExampleModel("제목", description);
-            });
-
-            // assert
-            assertThat(result.getErrorType()).isEqualTo(ErrorType.BAD_REQUEST);
-        }
-    }
-}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/example/ExampleServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/example/ExampleServiceIntegrationTest.java
deleted file mode 100644
index bbd5fdbe1..000000000
--- a/apps/commerce-api/src/test/java/com/loopers/domain/example/ExampleServiceIntegrationTest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package com.loopers.domain.example;
-
-import com.loopers.infrastructure.example.ExampleJpaRepository;
-import com.loopers.support.error.CoreException;
-import com.loopers.support.error.ErrorType;
-import com.loopers.utils.DatabaseCleanUp;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.assertAll;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-@SpringBootTest
-class ExampleServiceIntegrationTest {
-    @Autowired
-    private ExampleService exampleService;
-
-    @Autowired
-    private ExampleJpaRepository exampleJpaRepository;
-
-    @Autowired
-    private DatabaseCleanUp databaseCleanUp;
-
-    @AfterEach
-    void tearDown() {
-        databaseCleanUp.truncateAllTables();
-    }
-
-    @DisplayName("예시를 조회할 때,")
-    @Nested
-    class Get {
-        @DisplayName("존재하는 예시 ID를 주면, 해당 예시 정보를 반환한다.")
-        @Test
-        void returnsExampleInfo_whenValidIdIsProvided() {
-            // arrange
-            ExampleModel exampleModel = exampleJpaRepository.save(
-                new ExampleModel("예시 제목", "예시 설명")
-            );
-
-            // act
-            ExampleModel result = exampleService.getExample(exampleModel.getId());
-
-            // assert
-            assertAll(
-                () -> assertThat(result).isNotNull(),
-                () -> assertThat(result.getId()).isEqualTo(exampleModel.getId()),
-                () -> assertThat(result.getName()).isEqualTo(exampleModel.getName()),
-                () -> assertThat(result.getDescription()).isEqualTo(exampleModel.getDescription())
-            );
-        }
-
-        @DisplayName("존재하지 않는 예시 ID를 주면, NOT_FOUND 예외가 발생한다.")
-        @Test
-        void throwsException_whenInvalidIdIsProvided() {
-            // arrange
-            Long invalidId = 999L; // Assuming this ID does not exist
-
-            // act
-            CoreException exception = assertThrows(CoreException.class, () -> {
-                exampleService.getExample(invalidId);
-            });
-
-            // assert
-            assertThat(exception.getErrorType()).isEqualTo(ErrorType.NOT_FOUND);
-        }
-    }
-}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeConcurrencyTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeConcurrencyTest.java
new file mode 100644
index 000000000..0b93b3e28
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeConcurrencyTest.java
@@ -0,0 +1,160 @@
+package com.loopers.domain.like;
+
+import com.loopers.application.like.LikeFacade;
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.brand.BrandRepository;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.product.ProductRepository;
+import com.loopers.domain.user.Gender;
+import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserService;
+import com.loopers.utils.DatabaseCleanUp;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.*;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@SpringBootTest
+class LikeConcurrencyTest {
+
+    @Autowired
+    private LikeFacade likeFacade;
+
+    @Autowired
+    private LikeService likeService;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private BrandRepository brandRepository;
+
+    @Autowired
+    private ProductRepository productRepository;
+
+    @Autowired
+    private LikeRepository likeRepository;
+
+    @Autowired
+    private DatabaseCleanUp databaseCleanUp;
+
+    private Product product;
+    private List<User> users;
+
+    @BeforeEach
+    void setUp() {
+        databaseCleanUp.truncateAllTables();
+
+        Brand brand = brandRepository.save(Brand.create("Brand"));
+        product = productRepository.save(Product.create("Product", 1000L, 100, brand));
+
+        users = new ArrayList<>();
+        for (int i = 0; i < 100; i++) {
+            String userId = "likeUser" + String.format("%03d", i);
+            users.add(userService.signUp(userId, userId + "@mail.com", "1990-01-01", Gender.MALE));
+        }
+    }
+
+    @AfterEach
+    void tearDown() {
+        databaseCleanUp.truncateAllTables();
+    }
+
+    @DisplayName("동일 유저가 좋아요 100번 동시 클릭 시, 1개만 생성된다.")
+    @Test
+    void shouldCreateOnlyOneLike_whenSameUserClicksHundredTimesSimultaneously()
+            throws InterruptedException {
+        // given
+        int threadCount = 100;
+        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
+
+        CountDownLatch startLatch = new CountDownLatch(1);
+        CountDownLatch doneLatch = new CountDownLatch(threadCount);
+
+        User sameUser = users.get(0);
+        ConcurrentLinkedQueue<Exception> exceptions = new ConcurrentLinkedQueue<>();
+
+        // when
+        for (int i = 0; i < threadCount; i++) {
+            executor.submit(() -> {
+                try {
+                    startLatch.await(5, TimeUnit.SECONDS);
+
+                    likeFacade.addLike(sameUser.getUserIdValue(), product.getId());
+
+                } catch (Exception e) {
+                    exceptions.add(e);
+                } finally {
+                    doneLatch.countDown();
+                }
+            });
+        }
+
+        Thread.sleep(100);
+        startLatch.countDown();
+
+        boolean completed = doneLatch.await(10, TimeUnit.SECONDS);
+        assertThat(completed).isTrue();
+
+        executor.shutdown();
+        executor.awaitTermination(5, TimeUnit.SECONDS);
+
+        // then
+        Long likeCount = likeService.getLikeCount(product);
+        Long dbLikeCount = likeRepository.countByProduct(product);
+
+        assertThat(likeCount).isEqualTo(1L);
+        assertThat(dbLikeCount).isEqualTo(1L);
+
+        if (!exceptions.isEmpty()) {
+            System.out.println("멱등성으로 무시된 요청 수: " + exceptions.size());
+            exceptions.forEach(e -> {
+                assertThat(e)
+                        .satisfiesAnyOf(
+                                ex -> assertThat(ex).isInstanceOf(
+                                        org.springframework.dao.DataIntegrityViolationException.class),
+                                ex -> assertThat(ex).isInstanceOf(
+                                        org.springframework.transaction.UnexpectedRollbackException.class)
+                        );
+            });
+        }
+    }
+
+    @DisplayName("100명의 유저가 동일한 상품에 동시에 '좋아요'를 누를때 정상 처리된다.")
+    @Test
+    void testLikeConcurrency() throws InterruptedException {
+        // given
+        int threadCount = 100;
+        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
+        CountDownLatch latch = new CountDownLatch(threadCount);
+
+        // act
+        for (int i = 0; i < threadCount; i++) {
+            User user = users.get(i);
+            executor.submit(() -> {
+                try {
+                    likeFacade.addLike(user.getUserIdValue(), product.getId());
+                } catch (Exception e) {
+                    System.err.println("좋아요 동시성 테스트 실패: " + e.getMessage());
+                } finally {
+                    latch.countDown();
+                }
+            });
+        }
+        latch.await();
+        executor.shutdown();
+
+        // assert
+        Long likeCount = likeService.getLikeCount(product);
+        assertThat(likeCount).isEqualTo(100L);
+        assertThat(likeRepository.countByProduct(product)).isEqualTo(100L);
+    }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java
new file mode 100644
index 000000000..206f3d23f
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceIntegrationTest.java
@@ -0,0 +1,144 @@
+package com.loopers.domain.like;
+
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.brand.BrandRepository;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.product.ProductRepository;
+import com.loopers.domain.user.Gender;
+import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserService;
+import com.loopers.utils.DatabaseCleanUp;
+import org.junit.jupiter.api.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+@SpringBootTest
+class LikeServiceIntegrationTest {
+    @Autowired
+    private LikeService likeService;
+
+    @MockitoSpyBean
+    private LikeRepository likeRepository;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private BrandRepository brandRepository;
+
+    @Autowired
+    private ProductRepository productRepository;
+
+    @Autowired
+    private DatabaseCleanUp databaseCleanUp;
+
+    private User user1, user2;
+    private Product product1, product2;
+
+    @BeforeEach
+    void setUp() {
+        // 테스트용 사용자 생성
+        user1 = userService.signUp("likeUser01", "like@mail.com", "1990-01-01", Gender.MALE);
+        user2 = userService.signUp("likeUser02", "like@mail.com", "1990-01-02", Gender.FEMALE);
+
+        // 테스트용 상품 생성
+        Brand brand = brandRepository.save(Brand.create("Like Brand"));
+        product1 = productRepository.save(Product.create("Like Product 1", 1000L, 10, brand));
+        product2 = productRepository.save(Product.create("Like Product 2", 2000L, 20, brand));
+    }
+
+    @AfterEach
+    void tearDown() {
+        databaseCleanUp.truncateAllTables();
+    }
+
+    @DisplayName("좋아요 등록/취소/중복 방지")
+    @Nested
+    class CoreLikeFlow {
+
+        @DisplayName("좋아요를 등록할 수 있다.")
+        @Test
+        void addLike() {
+            // act
+            likeService.addLike(user1, product1);
+
+            // assert
+            verify(likeRepository, times(1)).save(any(Like.class));
+            assertThat(likeRepository.existsByUserAndProduct(user1, product1)).isTrue();
+        }
+
+        @DisplayName("중복 좋아요 방지를 위한 멱등성 처리가 구현되었다.")
+        @Test
+        void addLike_idempotent() {
+            // arrange
+            likeService.addLike(user1, product1); // 1번째 호출
+
+            // act
+            likeService.addLike(user1, product1); // 2번째 (중복) 호출
+
+            // assert
+            // 1. Service 로직(existsByUserAndProduct)에 의해 save는 총 1번만 호출되어야 함
+            verify(likeRepository, times(1)).save(any(Like.class));
+            // 2. existsByUserAndProduct는 총 2번 호출됨 (첫번째 시도 + 두번째 중복 체크)
+            verify(likeRepository, times(2)).existsByUserAndProduct(user1, product1);
+        }
+
+        @DisplayName("좋아요를 취소할 수 있다.")
+        @Test
+        void removeLike() {
+            // arrange (미리 좋아요 추가)
+            likeService.addLike(user1, product1);
+            assertThat(likeRepository.existsByUserAndProduct(user1, product1)).isTrue();
+
+            // act
+            likeService.removeLike(user1, product1);
+
+            // assert
+            verify(likeRepository, times(1)).delete(any(Like.class));
+            assertThat(likeRepository.existsByUserAndProduct(user1, product1)).isFalse();
+        }
+
+        @DisplayName("좋아요를 누르지 않은 상품을 취소해도 에러가 발생하지 않는다.")
+        @Test
+        void removeLike_nonExistent() {
+            // arrange
+            assertThat(likeRepository.existsByUserAndProduct(user1, product1)).isFalse();
+
+            // act
+            likeService.removeLike(user1, product1);
+
+            // assert
+            verify(likeRepository, never()).delete(any(Like.class));
+        }
+    }
+
+
+    @DisplayName("좋아요 수 조회")
+    @Nested
+    class GetLikeCount {
+
+        @DisplayName("특정 상품의 좋아요 수를 조회할 수 있다.")
+        @Test
+        void getLikeCount() {
+            // arrange
+            likeService.addLike(user1, product1); // product1 (1)
+            likeService.addLike(user2, product1); // product1 (2)
+            likeService.addLike(user1, product2); // product2 (1)
+
+            // act
+            Long count1 = likeService.getLikeCount(product1);
+            Long count2 = likeService.getLikeCount(product2);
+
+            // assert
+            verify(likeRepository, times(1)).countByProduct(product1);
+            verify(likeRepository, times(1)).countByProduct(product2);
+            assertThat(count1).isEqualTo(2L);
+            assertThat(count2).isEqualTo(1L);
+        }
+    }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeTest.java
new file mode 100644
index 000000000..e46c11ef8
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/like/LikeTest.java
@@ -0,0 +1,29 @@
+package com.loopers.domain.like;
+
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.user.Gender;
+import com.loopers.domain.user.User;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class LikeTest {
+
+    @DisplayName("사용자와 상품으로 좋아요를 생성할 수 있다.")
+    @Test
+    void createLike() {
+        // arrange
+        User dummyUser = User.create("testuser", "test@mail.com", "1990-01-01", Gender.MALE);
+        Brand dummyBrand = Brand.create("Dummy Brand");
+        Product dummyProduct = Product.create("Test Product", 1000L, 10, dummyBrand);
+
+        // act
+        Like like = Like.create(dummyUser, dummyProduct);
+
+        // assert
+        assertThat(like.getUser()).isEqualTo(dummyUser);
+        assertThat(like.getProduct()).isEqualTo(dummyProduct);
+    }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderConcurrencyTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderConcurrencyTest.java
new file mode 100644
index 000000000..4b20a02d2
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderConcurrencyTest.java
@@ -0,0 +1,203 @@
+package com.loopers.domain.order;
+
+import com.loopers.application.order.OrderFacade;
+import com.loopers.application.order.OrderPlaceCommand;
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.brand.BrandRepository;
+import com.loopers.domain.coupon.Coupon;
+import com.loopers.domain.coupon.CouponRepository;
+import com.loopers.domain.coupon.DiscountType;
+import com.loopers.domain.point.Point;
+import com.loopers.domain.point.PointRepository;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.product.ProductRepository;
+import com.loopers.domain.user.Gender;
+import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserService;
+import com.loopers.utils.DatabaseCleanUp;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@SpringBootTest
+class OrderConcurrencyTest {
+
+    @Autowired
+    private OrderFacade orderFacade;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private BrandRepository brandRepository;
+
+    @Autowired
+    private ProductRepository productRepository;
+
+    @Autowired
+    private PointRepository pointRepository;
+
+    @Autowired
+    private CouponRepository couponRepository;
+
+    @Autowired
+    private DatabaseCleanUp databaseCleanUp;
+
+    private User user;
+
+    @BeforeEach
+    void setUp() {
+        databaseCleanUp.truncateAllTables();
+        user = userService.signUp("testUser", "test@mail.com", "1990-01-01", Gender.MALE);
+    }
+
+    @AfterEach
+    void tearDown() {
+        databaseCleanUp.truncateAllTables();
+    }
+
+    private Product setupProduct(int stock, long price) {
+        Brand brand = brandRepository.save(Brand.create("Brand"));
+        return productRepository.save(Product.create("Product", price, stock, brand));
+    }
+
+    private void setupPoint(long amount) {
+        pointRepository.save(Point.create(user.getUserIdValue(), amount));
+    }
+
+    private Coupon setupCoupon(long amount) {
+        return couponRepository.save(Coupon.create(user, amount + "원 할인", DiscountType.FIXED_AMOUNT, amount));
+    }
+
+    @DisplayName("100명이 재고 100개 상품을 동시 주문하면, 모두 성공하고 재고는 정확히 0이 된다")
+    @Test
+    void testStockConcurrency() throws InterruptedException {
+        // given
+        Product product = setupProduct(100, 1000L);
+        setupPoint(100 * 1000L + 1000L);
+
+        int threadCount = 100;
+        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
+        CountDownLatch latch = new CountDownLatch(threadCount);
+        AtomicInteger successCount = new AtomicInteger(0);
+
+        // act
+        for (int i = 0; i < threadCount; i++) {
+            executor.submit(() -> {
+                try {
+                    OrderPlaceCommand command = new OrderPlaceCommand(
+                            user.getUserIdValue(),
+                            List.of(new OrderPlaceCommand.OrderItemCommand(product.getId(), 1)),
+                            null
+                    );
+                    orderFacade.placeOrder(command);
+                    successCount.incrementAndGet();
+                } catch (Exception e) {
+                    System.err.println("재고 동시성 테스트 실패: " + e.getMessage());
+                } finally {
+                    latch.countDown();
+                }
+            });
+        }
+        latch.await();
+        executor.shutdown();
+
+        // assert
+        Product foundProduct = productRepository.findById(product.getId()).orElseThrow();
+        assertThat(successCount.get()).isEqualTo(100);
+        assertThat(foundProduct.getStockValue()).isZero();
+    }
+
+    @DisplayName("동일 유저가 동일 쿠폰으로 100번 동시 주문하면, 1번만 성공하고 쿠폰은 사용 처리된다")
+    @Test
+    void testCouponConcurrency() throws InterruptedException {
+        // given
+        Product product = setupProduct(1000, 1000L);
+        setupPoint(100 * 1000L);
+        Coupon coupon = setupCoupon(100L); // 100원 할인 쿠폰
+
+        int threadCount = 100;
+        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
+        CountDownLatch latch = new CountDownLatch(threadCount);
+        AtomicInteger successCount = new AtomicInteger(0);
+        AtomicInteger failCount = new AtomicInteger(0);
+
+        // act
+        for (int i = 0; i < threadCount; i++) {
+            executor.submit(() -> {
+                try {
+                    OrderPlaceCommand command = new OrderPlaceCommand(
+                            user.getUserIdValue(),
+                            List.of(new OrderPlaceCommand.OrderItemCommand(product.getId(), 1)),
+                            coupon.getId() // 동일 쿠폰
+                    );
+                    orderFacade.placeOrder(command);
+                    successCount.incrementAndGet();
+                } catch (Exception e) {
+                    failCount.incrementAndGet(); // 락 경합 또는 이미 사용된 쿠폰
+                } finally {
+                    latch.countDown();
+                }
+            });
+        }
+        latch.await();
+        executor.shutdown();
+
+        // assert
+        Coupon foundCoupon = couponRepository.findById(coupon.getId()).orElseThrow();
+        assertThat(successCount.get()).isEqualTo(1);
+        assertThat(failCount.get()).isEqualTo(99);
+        assertThat(foundCoupon.getIsUsed()).isTrue();
+    }
+
+    @DisplayName("동일 유저가 100개 주문을 동시 요청하면, 포인트는 정확한 금액만큼 차감된다")
+    @Test
+    void testPointConcurrency() throws InterruptedException {
+        // given
+        Product product = setupProduct(1000, 100L);
+        long initialPoint = 100L * 100L;
+        setupPoint(initialPoint);
+
+        int threadCount = 100;
+        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
+        CountDownLatch latch = new CountDownLatch(threadCount);
+        AtomicInteger successCount = new AtomicInteger(0);
+
+        // act
+        for (int i = 0; i < threadCount; i++) {
+            executor.submit(() -> {
+                try {
+                    OrderPlaceCommand command = new OrderPlaceCommand(
+                            user.getUserIdValue(),
+                            List.of(new OrderPlaceCommand.OrderItemCommand(product.getId(), 1)),
+                            null
+                    );
+                    orderFacade.placeOrder(command);
+                    successCount.incrementAndGet();
+                } catch (Exception e) {
+                    System.err.println("포인트 동시성 테스트 실패: " + e.getMessage());
+                } finally {
+                    latch.countDown();
+                }
+            });
+        }
+        latch.await();
+        executor.shutdown();
+
+        // assert
+        Point foundPoint = pointRepository.findByUserId(user.getUserIdValue()).orElseThrow();
+        assertThat(successCount.get()).isEqualTo(100);
+        assertThat(foundPoint.getBalanceValue()).isZero();
+    }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderItemTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderItemTest.java
new file mode 100644
index 000000000..aaae3acc6
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderItemTest.java
@@ -0,0 +1,94 @@
+package com.loopers.domain.order;
+
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.product.Product;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class OrderItemTest {
+
+    private Product dummyProduct;
+
+    @BeforeEach
+    void setUp() {
+        Brand dummyBrand = Brand.create("Dummy Brand");
+        dummyProduct = Product.create("Test Product", 1000L, 10, dummyBrand);
+    }
+
+    @Nested
+    @DisplayName("주문 항목 생성 (OrderItem.create)")
+    class CreateOrderItem {
+
+        @DisplayName("상품과 수량으로 주문 항목을 생성할 수 있다.")
+        @Test
+        void createOrderItem() {
+            // act
+            OrderItem item = OrderItem.create(dummyProduct, 3);
+
+            // assert
+            assertThat(item.getProduct()).isEqualTo(dummyProduct);
+            assertThat(item.getQuantity()).isEqualTo(3);
+            assertThat(item.getOrder()).isNull();
+        }
+
+        @DisplayName("생성 시 상품의 현재 가격이 단가(OrderItemPrice)로 스냅샷된다.")
+        @Test
+        void createOrderItem_snapshotsPrice() {
+            // act
+            OrderItem item = OrderItem.create(dummyProduct, 3);
+
+            // assert
+            assertThat(item.getUnitPriceValue()).isEqualTo(1000L);
+        }
+
+        @DisplayName("단가가 null이면 (상품 가격이 null) BAD_REQUEST 예외가 발생한다.")
+        @Test
+        void throwsException_whenPriceIsNull() {
+            // arrange
+            Product nullPriceProduct = Product.create("Test", 0L, 10, Brand.create("b"));
+
+            assertThatThrownBy(() -> OrderItemPrice.of(null))
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+        }
+    }
+
+    @Nested
+    @DisplayName("금액 계산")
+    class CalculateAmount {
+
+        @DisplayName("주문 항목의 총 금액 (단가 * 수량)을 계산할 수 있다.")
+        @Test
+        void calculateAmount() {
+            // arrange
+            OrderItem item = OrderItem.create(dummyProduct, 3);
+
+            // act
+            OrderTotalAmount amount = item.calculateAmount();
+
+            // assert
+            assertThat(amount.getValue()).isEqualTo(3000L);
+        }
+
+        @DisplayName("수량이 1개일 때 총 금액을 계산할 수 있다.")
+        @Test
+        void calculateAmount_whenQuantityIsOne() {
+            // arrange
+            OrderItem item = OrderItem.create(dummyProduct, 1);
+
+            // act
+            OrderTotalAmount amount = item.calculateAmount();
+
+            // assert
+            assertThat(amount.getValue()).isEqualTo(1000L);
+        }
+    }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderRollbackTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderRollbackTest.java
new file mode 100644
index 000000000..7d54f528d
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderRollbackTest.java
@@ -0,0 +1,124 @@
+package com.loopers.domain.order;
+
+import com.loopers.application.order.OrderFacade;
+import com.loopers.application.order.OrderPlaceCommand;
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.brand.BrandRepository;
+import com.loopers.domain.coupon.Coupon;
+import com.loopers.domain.coupon.CouponRepository;
+import com.loopers.domain.coupon.DiscountType;
+import com.loopers.domain.point.Point;
+import com.loopers.domain.point.PointRepository;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.product.ProductRepository;
+import com.loopers.domain.user.Gender;
+import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserService;
+import com.loopers.support.error.CoreException;
+import com.loopers.utils.DatabaseCleanUp;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@SpringBootTest
+class OrderRollbackTest {
+
+    @Autowired
+    private OrderFacade orderFacade;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private BrandRepository brandRepository;
+
+    @Autowired
+    private ProductRepository productRepository;
+
+    @Autowired
+    private CouponRepository couponRepository;
+
+    @Autowired
+    private PointRepository pointRepository;
+
+    @Autowired
+    private DatabaseCleanUp databaseCleanUp;
+
+    private User user;
+    private Brand brand;
+    private Product product1;
+    private Product product2;
+    private Coupon coupon;
+    private Point point;
+
+    @BeforeEach
+    void setUp() {
+        databaseCleanUp.truncateAllTables();
+
+        user = userService.signUp("testUser", "test@mail.com", "1990-01-01", Gender.MALE);
+        point = pointRepository.save(Point.create(user.getUserIdValue(), 10000L));
+
+        brand = brandRepository.save(Brand.create("Test Brand"));
+        product1 = productRepository.save(Product.create("Product 1", 5000L, 10, brand));
+        product2 = productRepository.save(Product.create("Product 2", 1000L, 0, brand));
+
+        coupon = couponRepository.save(Coupon.create(user, "1000원 할인", DiscountType.FIXED_AMOUNT, 1000L));
+    }
+
+    @AfterEach
+    void tearDown() {
+        databaseCleanUp.truncateAllTables();
+    }
+
+    @DisplayName("재고 부족 시, 사용하려던 쿠폰과 포인트가 롤백되어야 한다.")
+    @Test
+    void placeOrder_Rolls_Back_When_Stock_Is_Insufficient() {
+        // arrange
+        OrderPlaceCommand command = new OrderPlaceCommand(
+                user.getUserIdValue(),
+                List.of(new OrderPlaceCommand.OrderItemCommand(product2.getId(), 1)),
+                coupon.getId()
+        );
+
+        // act & assert
+        assertThrows(CoreException.class, () -> {
+            orderFacade.placeOrder(command);
+        });
+
+        Coupon foundCoupon = couponRepository.findById(coupon.getId()).orElseThrow();
+        assertThat(foundCoupon.getIsUsed()).isFalse();
+
+        Point foundPoint = pointRepository.findByUserId(user.getUserIdValue()).orElseThrow();
+        assertThat(foundPoint.getBalanceValue()).isEqualTo(10000L);
+    }
+
+    @DisplayName("포인트 부족 시, 사용하려던 쿠폰과 재고가 롤백되어야 한다.")
+    @Test
+    void placeOrder_Rolls_Back_When_Point_Is_Insufficient() {
+        // arrange
+        OrderPlaceCommand command = new OrderPlaceCommand(
+                user.getUserIdValue(),
+                List.of(new OrderPlaceCommand.OrderItemCommand(product1.getId(), 3)),
+                coupon.getId()
+        );
+
+        // act & assert
+        assertThrows(CoreException.class, () -> {
+            orderFacade.placeOrder(command);
+        });
+
+        Product foundProduct = productRepository.findById(product1.getId()).orElseThrow();
+        assertThat(foundProduct.getStockValue()).isEqualTo(10);
+
+        Coupon foundCoupon = couponRepository.findById(coupon.getId()).orElseThrow();
+        assertThat(foundCoupon.getIsUsed()).isFalse();
+    }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceIntegrationTest.java
new file mode 100644
index 000000000..9d3f8f8ff
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceIntegrationTest.java
@@ -0,0 +1,158 @@
+package com.loopers.domain.order;
+
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.brand.BrandRepository;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.product.ProductRepository;
+import com.loopers.domain.user.Gender;
+import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserService;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import com.loopers.utils.DatabaseCleanUp;
+import org.junit.jupiter.api.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+@SpringBootTest
+class OrderServiceIntegrationTest {
+
+    @Autowired
+    private OrderService orderService;
+
+    @MockitoSpyBean
+    private OrderRepository orderRepository;
+
+    @Autowired
+    private UserService userService;
+
+    @MockitoSpyBean
+    private BrandRepository brandRepository;
+
+    @MockitoSpyBean
+    private ProductRepository productRepository;
+
+    @Autowired
+    private DatabaseCleanUp databaseCleanUp;
+
+    private User user1, user2;
+    private Product product1;
+    private Order order1;
+    private Order order2;
+
+    @BeforeEach
+    void setUp() {
+        databaseCleanUp.truncateAllTables();
+
+        user1 = userService.signUp("user01", "order1@mail.com", "1990-01-01", Gender.MALE);
+        user2 = userService.signUp("user02", "order2@mail.com", "1990-01-01", Gender.FEMALE);
+
+        Brand brand = brandRepository.save(Brand.create("Order Brand"));
+        product1 = productRepository.save(Product.create("Order Product 1", 1000L, 10, brand));
+
+        Order order1ToSave = Order.create(user1);
+        order1ToSave.addOrderItem(product1, 1);
+        order1 = orderService.save(order1ToSave);
+
+        Order order2ToSave = Order.create(user2);
+        order2ToSave.addOrderItem(product1, 2);
+        order2 = orderService.save(order2ToSave);
+    }
+
+    @DisplayName("주문 저장")
+    @Nested
+    class SaveOrder {
+
+        @DisplayName("주문 저장 시, 주문 항목(OrderItem)도 Cascade로 함께 저장된다.")
+        @Test
+        void saveOrder_and_CascadeItems() {
+            // arrange
+            Order newOrder = Order.create(user1);
+            newOrder.addOrderItem(product1, 5);
+            newOrder.completePayment();
+
+            // act
+            Order savedOrder = orderService.save(newOrder);
+
+            // assert
+            verify(orderRepository, times(3)).save(any(Order.class));
+            Order foundOrder = orderRepository.findByIdAndUser(savedOrder.getId(), user1.getId())
+                    .orElseThrow(() -> new AssertionError("Order should be found"));
+
+            assertAll(
+                    () -> assertThat(foundOrder.getStatus()).isEqualTo(OrderStatus.PAID),
+                    () -> assertThat(foundOrder.getTotalAmountValue()).isEqualTo(5000L),
+                    () -> assertThat(foundOrder.getOrderItems()).hasSize(1)
+            );
+        }
+    }
+
+    @DisplayName("주문 조회")
+    @Nested
+    class GetOrder {
+
+        @DisplayName("특정 사용자의 모든 주문 목록을 조회할 수 있다.")
+        @Test
+        void getOrdersByUser() {
+            // act
+            List<Order> user1Orders = orderService.getOrdersByUser(user1);
+            List<Order> user2Orders = orderService.getOrdersByUser(user2);
+
+            // assert
+            assertThat(user1Orders).hasSize(1);
+            assertThat(user1Orders.getFirst().getId()).isEqualTo(order1.getId());
+
+            assertThat(user2Orders).hasSize(1);
+            assertThat(user2Orders.getFirst().getId()).isEqualTo(order2.getId());
+        }
+
+        @DisplayName("특정 주문 ID와 사용자로 본인의 주문을 조회할 수 있다.")
+        @Test
+        void getOrderByIdAndUser_success() {
+            // act
+            Order foundOrder = orderService.getOrderByIdAndUser(order1.getId(), user1);
+
+            // assert
+            assertAll(
+                    () -> assertThat(foundOrder).isNotNull(),
+                    () -> {
+                        Assertions.assertNotNull(foundOrder);
+                        assertThat(foundOrder.getId()).isEqualTo(order1.getId());
+                    }
+            );
+        }
+
+        @DisplayName("존재하지 않는 주문 ID로 조회 시, NOT_FOUND 예외가 발생한다.")
+        @Test
+        void getOrderByIdAndUser_throwsNotFound_whenOrderNotExist() {
+            // act & assert
+            CoreException exception = assertThrows(CoreException.class, () -> {
+                orderService.getOrderByIdAndUser(-99L, user1);
+            });
+
+            assertThat(exception.getErrorType()).isEqualTo(ErrorType.NOT_FOUND);
+        }
+
+        @DisplayName("다른 사용자의 주문 ID로 조회 시, NOT_FOUND 예외가 발생한다.")
+        @Test
+        void getOrderByIdAndUser_throwsNotFound_whenUserIsDifferent() {
+            // act & assert
+            CoreException exception = assertThrows(CoreException.class, () -> {
+                orderService.getOrderByIdAndUser(order1.getId(), user2);
+            });
+
+            // assert
+            assertThat(exception.getErrorType()).isEqualTo(ErrorType.NOT_FOUND);
+        }
+    }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderTest.java
new file mode 100644
index 000000000..6ed7edeb5
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderTest.java
@@ -0,0 +1,146 @@
+package com.loopers.domain.order;
+
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.product.Product;
+import com.loopers.domain.user.Gender;
+import com.loopers.domain.user.User;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.time.ZonedDateTime;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class OrderTest {
+
+    private User dummyUser;
+    private Product dummyProduct1;
+    private Product dummyProduct2;
+
+    @BeforeEach
+    void setUp() {
+        // 테스트에 필요한 User, Product (및 Brand) 더미 객체 생성
+        dummyUser = User.create("testuser", "test@mail.com", "1990-01-01", Gender.MALE);
+        Brand dummyBrand = Brand.create("Dummy Brand");
+        dummyProduct1 = Product.create("Product A", 1000L, 10, dummyBrand);
+        dummyProduct2 = Product.create("Product B", 2000L, 10, dummyBrand);
+    }
+
+    @Nested
+    @DisplayName("주문 생성 (Order.create)")
+    class CreateOrder {
+
+        @DisplayName("사용자로 주문을 생성할 수 있다.")
+        @Test
+        void createOrder() {
+            // act
+            Order order = Order.create(dummyUser);
+
+            // assert
+            assertThat(order.getUser()).isEqualTo(dummyUser);
+            assertThat(order.getStatus()).isEqualTo(OrderStatus.PENDING);
+            assertThat(order.getTotalAmountValue()).isZero();
+            assertThat(order.getOrderItems()).isEmpty();
+            assertThat(order.getPaidAt()).isNull();
+        }
+
+        @DisplayName("사용자가 null이면 BAD_REQUEST 예외가 발생한다.")
+        @Test
+        void throwsException_whenUserIsNull() {
+            // act & assert
+            assertThatThrownBy(() -> Order.create(null))
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+        }
+    }
+
+    @Nested
+    @DisplayName("주문 항목 추가 (addOrderItem)")
+    class AddItem {
+
+        @DisplayName("주문에 항목을 추가하면 총 금액이 재계산된다.")
+        @Test
+        void addOrderItem() {
+            // arrange
+            Order order = Order.create(dummyUser);
+
+            // act
+            order.addOrderItem(dummyProduct1, 2); // 1000 * 2 = 2000
+            order.addOrderItem(dummyProduct2, 1); // 2000 * 1 = 2000
+
+            // assert
+            assertThat(order.getOrderItems()).hasSize(2);
+            assertThat(order.getTotalAmountValue()).isEqualTo(4000L);
+            assertThat(order.getOrderItems().get(0).getUnitPriceValue()).isEqualTo(1000L); // 가격 스냅샷 확인
+        }
+
+        @DisplayName("상품이 null이면 BAD_REQUEST 예외가 발생한다.")
+        @Test
+        void throwsException_whenProductIsNull() {
+            // arrange
+            Order order = Order.create(dummyUser);
+
+            // act & assert
+            assertThatThrownBy(() -> order.addOrderItem(null, 1))
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+        }
+
+        @DisplayName("수량이 0 이하면 BAD_REQUEST 예외가 발생한다.")
+        @ParameterizedTest
+        @ValueSource(ints = {0, -1})
+        void throwsException_whenQuantityIsInvalid(int invalidQuantity) {
+            // arrange
+            Order order = Order.create(dummyUser);
+
+            // act & assert
+            assertThatThrownBy(() -> order.addOrderItem(dummyProduct1, invalidQuantity))
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+        }
+    }
+
+    @Nested
+    @DisplayName("결제 완료 (completePayment)")
+    class CompletePayment {
+
+        @DisplayName("주문 항목이 존재하면 결제를 완료할 수 있다.")
+        @Test
+        void completePayment() {
+            // arrange
+            Order order = Order.create(dummyUser);
+            order.addOrderItem(dummyProduct1, 1);
+            ZonedDateTime beforePayment = ZonedDateTime.now().minusSeconds(1);
+
+            // act
+            order.completePayment();
+
+            // assert
+            assertThat(order.getStatus()).isEqualTo(OrderStatus.PAID);
+            assertThat(order.getPaidAt()).isAfter(beforePayment);
+        }
+
+        @DisplayName("주문 항목이 없으면 BAD_REQUEST 예외가 발생한다.")
+        @Test
+        void throwsException_whenItemsAreEmpty() {
+            // arrange
+            Order order = Order.create(dummyUser);
+
+            // act & assert
+            assertThatThrownBy(order::completePayment)
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+        }
+    }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/point/PointServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/point/PointServiceIntegrationTest.java
index c1e19462c..e1ec5813e 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/point/PointServiceIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/point/PointServiceIntegrationTest.java
@@ -55,20 +55,10 @@ void returnsPoint_whenUserExists() {
                     },
                     () -> {
                         Assertions.assertNotNull(point);
-                        assertThat(point.getAmount()).isZero();
+                        assertThat(point.getBalanceValue()).isZero();
                     }
             );
         }
-
-        @DisplayName("해당 ID의 회원이 존재하지 않을 경우, null이 반환된다.")
-        @Test
-        void returnsNull_whenUserDoesNotExist() {
-            // act
-            Point point = pointService.getPoint("nonexistentuser");
-
-            // assert
-            assertThat(point).isNull();
-        }
     }
 
     @DisplayName("포인트 생성을 할 때,")
@@ -89,7 +79,7 @@ void createsPointWithZeroAmount() {
                     () -> assertThat(point).isNotNull(),
                     () -> {
                         Assertions.assertNotNull(point);
-                        assertThat(point.getAmount()).isZero();
+                        assertThat(point.getBalanceValue()).isZero();
                     },
                     () -> verify(pointRepository, times(1)).save(any(Point.class))
             );
@@ -125,7 +115,7 @@ void canFindSavedPoint_afterSuccessfulCreation() {
 
             assertAll(
                     () -> assertThat(foundPoint.getUserId()).isEqualTo(userId),
-                    () -> assertThat(foundPoint.getAmount()).isZero()
+                    () -> assertThat(foundPoint.getBalanceValue()).isZero()
             );
         }
     }
@@ -146,7 +136,7 @@ void canChargePoint() {
 
             // assert
             Point point = pointService.getPoint(userId);
-            assertThat(point.getAmount()).isEqualTo(1000L);
+            assertThat(point.getBalanceValue()).isEqualTo(1000L);
         }
 
         @DisplayName("존재하지 않는 사용자에게 포인트 충전 시, NOT_FOUND 예외가 발생한다.")
@@ -160,4 +150,58 @@ void throwsNotFoundException_whenUserDoesNotExist() {
             assertThat(exception.getErrorType()).isEqualTo(ErrorType.NOT_FOUND);
         }
     }
+
+    @DisplayName("포인트 사용을 할 때,")
+    @Nested
+    class UsePoint {
+
+        @DisplayName("포인트를 사용할 수 있다.")
+        @Test
+        void canUsePoint() {
+            // arrange
+            String userId = "testuser01";
+            pointService.createPoint(userId);
+            pointService.chargePoint(userId, 10000L);
+
+            // act
+            pointService.usePoint(userId, 3000L);
+
+            // assert
+            Point point = pointService.getPoint(userId);
+            assertThat(point.getBalanceValue()).isEqualTo(7000L);
+        }
+
+        @DisplayName("잔액보다 많은 금액 사용 시 BAD_REQUEST 예외가 발생한다.")
+        @Test
+        void throwsException_whenInsufficientBalance() {
+            // arrange
+            String userId = "testuser01";
+            pointService.createPoint(userId);
+            pointService.chargePoint(userId, 5000L);
+
+            // act & assert
+            CoreException exception = assertThrows(CoreException.class, () -> {
+                pointService.usePoint(userId, 10000L);
+            });
+
+            assertThat(exception.getErrorType()).isEqualTo(ErrorType.BAD_REQUEST);
+        }
+
+        @DisplayName("비관적 락으로 포인트를 사용할 수 있다.")
+        @Test
+        void canUsePointWithLock() {
+            // arrange
+            String userId = "testuser01";
+            pointService.createPoint(userId);
+            pointService.chargePoint(userId, 10000L);
+
+            // act
+            pointService.usePointWithLock(userId, 3000L);
+
+            // assert
+            Point point = pointService.getPoint(userId);
+            assertThat(point.getBalanceValue()).isEqualTo(7000L);
+            verify(pointRepository, times(1)).findByUserIdWithPessimisticLock(userId);
+        }
+    }
 }
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/point/PointTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/point/PointTest.java
index 121bdcacf..28227792a 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/point/PointTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/point/PointTest.java
@@ -1,6 +1,7 @@
 package com.loopers.domain.point;
 
 import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 
@@ -9,17 +10,6 @@
 
 class PointTest {
 
-    @DisplayName("포인트를 생성할 때 초기 금액은 0이다.")
-    @Test
-    void createsPointWithZeroAmount() {
-        // act
-        Point point = Point.create("testuser01");
-
-        // assert
-        assertThat(point.getUserId()).isEqualTo("testuser01");
-        assertThat(point.getAmount()).isZero();
-    }
-
     @DisplayName("초기 금액을 지정하여 포인트를 생성할 수 있다.")
     @Test
     void createsPointWithInitialAmount() {
@@ -28,25 +18,27 @@ void createsPointWithInitialAmount() {
 
         // assert
         assertThat(point.getUserId()).isEqualTo("testuser01");
-        assertThat(point.getAmount()).isEqualTo(1000L);
+        assertThat(point.getBalanceValue()).isEqualTo(1000L);
     }
 
     @DisplayName("사용자 ID가 null이면 BAD_REQUEST 예외가 발생한다.")
     @Test
     void throwsException_whenUserIdIsNull() {
         // act & assert
-        assertThatThrownBy(() -> Point.create(null))
+        assertThatThrownBy(() -> Point.create(null, 0L))
                 .isInstanceOf(CoreException.class)
-                .hasMessage("사용자 ID는 비어있을 수 없습니다.");
+                .extracting(ex -> ((CoreException) ex).getErrorType())
+                .isEqualTo(ErrorType.BAD_REQUEST);
     }
 
     @DisplayName("사용자 ID가 빈 문자열이면 BAD_REQUEST 예외가 발생한다.")
     @Test
     void throwsException_whenUserIdIsEmpty() {
         // act & assert
-        assertThatThrownBy(() -> Point.create(""))
+        assertThatThrownBy(() -> Point.create("", 0L))
                 .isInstanceOf(CoreException.class)
-                .hasMessage("사용자 ID는 비어있을 수 없습니다.");
+                .extracting(ex -> ((CoreException) ex).getErrorType())
+                .isEqualTo(ErrorType.BAD_REQUEST);
     }
 
     @DisplayName("포인트 금액이 null이면 BAD_REQUEST 예외가 발생한다.")
@@ -55,7 +47,8 @@ void throwsException_whenAmountIsNull() {
         // act & assert
         assertThatThrownBy(() -> Point.create("testuser01", null))
                 .isInstanceOf(CoreException.class)
-                .hasMessage("포인트 금액은 필수입니다.");
+                .extracting(ex -> ((CoreException) ex).getErrorType())
+                .isEqualTo(ErrorType.BAD_REQUEST);
     }
 
     @DisplayName("포인트 금액이 음수이면 BAD_REQUEST 예외가 발생한다.")
@@ -64,7 +57,8 @@ void throwsException_whenAmountIsNegative() {
         // act & assert
         assertThatThrownBy(() -> Point.create("testuser01", -100L))
                 .isInstanceOf(CoreException.class)
-                .hasMessage("포인트 금액은 0 이상이어야 합니다.");
+                .extracting(ex -> ((CoreException) ex).getErrorType())
+                .isEqualTo(ErrorType.BAD_REQUEST);
     }
 
     @DisplayName("포인트를 충전할 수 있다.")
@@ -77,7 +71,7 @@ void canChargePoint() {
         point.charge(1000L);
 
         // assert
-        assertThat(point.getAmount()).isEqualTo(1000L);
+        assertThat(point.getBalanceValue()).isEqualTo(1000L);
     }
 
     @DisplayName("여러 번 충전할 수 있다.")
@@ -92,7 +86,7 @@ void canChargeMultipleTimes() {
         point.charge(300L);
 
         // assert
-        assertThat(point.getAmount()).isEqualTo(1800L);
+        assertThat(point.getBalanceValue()).isEqualTo(1800L);
     }
 
     @DisplayName("충전 금액이 0 이하이면 BAD_REQUEST 예외가 발생한다.")
@@ -104,11 +98,13 @@ void throwsException_whenChargeAmountIsZeroOrNegative() {
         // act & assert
         assertThatThrownBy(() -> point.charge(0L))
                 .isInstanceOf(CoreException.class)
-                .hasMessageContaining("충전 금액은 0보다 커야 합니다.");
+                .extracting(ex -> ((CoreException) ex).getErrorType())
+                .isEqualTo(ErrorType.BAD_REQUEST);
 
         assertThatThrownBy(() -> point.charge(-100L))
                 .isInstanceOf(CoreException.class)
-                .hasMessageContaining("충전 금액은 0보다 커야 합니다.");
+                .extracting(ex -> ((CoreException) ex).getErrorType())
+                .isEqualTo(ErrorType.BAD_REQUEST);
     }
 
     @DisplayName("충전 금액이 null이면 BAD_REQUEST 예외가 발생한다.")
@@ -120,6 +116,79 @@ void throwsException_whenChargeAmountIsNull() {
         // act & assert
         assertThatThrownBy(() -> point.charge(null))
                 .isInstanceOf(CoreException.class)
-                .hasMessage("포인트 금액은 필수입니다.");
+                .extracting(ex -> ((CoreException) ex).getErrorType())
+                .isEqualTo(ErrorType.BAD_REQUEST);
+    }
+
+    @DisplayName("포인트를 사용할 수 있다.")
+    @Test
+    void canUsePoint() {
+        // arrange
+        Point point = Point.create("testuser01", 10000L);
+
+        // act
+        point.use(3000L);
+
+        // assert
+        assertThat(point.getBalanceValue()).isEqualTo(7000L);
+    }
+
+    @DisplayName("여러 번 사용할 수 있다.")
+    @Test
+    void canUseMultipleTimes() {
+        // arrange
+        Point point = Point.create("testuser01", 10000L);
+
+        // act
+        point.use(2000L);
+        point.use(3000L);
+        point.use(1000L);
+
+        // assert
+        assertThat(point.getBalanceValue()).isEqualTo(4000L);
+    }
+
+    @DisplayName("잔액보다 많은 금액을 사용하면 BAD_REQUEST 예외가 발생한다.")
+    @Test
+    void throwsException_whenInsufficientBalance() {
+        // arrange
+        Point point = Point.create("testuser01", 5000L);
+
+        // act & assert
+        assertThatThrownBy(() -> point.use(10000L))
+                .isInstanceOf(CoreException.class)
+                .extracting(ex -> ((CoreException) ex).getErrorType())
+                .isEqualTo(ErrorType.BAD_REQUEST);
+    }
+
+    @DisplayName("사용 금액이 0 이하이면 BAD_REQUEST 예외가 발생한다.")
+    @Test
+    void throwsException_whenUseAmountIsZeroOrNegative() {
+        // arrange
+        Point point = Point.create("testuser01", 10000L);
+
+        // act & assert
+        assertThatThrownBy(() -> point.use(0L))
+                .isInstanceOf(CoreException.class)
+                .extracting(ex -> ((CoreException) ex).getErrorType())
+                .isEqualTo(ErrorType.BAD_REQUEST);
+
+        assertThatThrownBy(() -> point.use(-100L))
+                .isInstanceOf(CoreException.class)
+                .extracting(ex -> ((CoreException) ex).getErrorType())
+                .isEqualTo(ErrorType.BAD_REQUEST);
+    }
+
+    @DisplayName("사용 금액이 null이면 BAD_REQUEST 예외가 발생한다.")
+    @Test
+    void throwsException_whenUseAmountIsNull() {
+        // arrange
+        Point point = Point.create("testuser01", 10000L);
+
+        // act & assert
+        assertThatThrownBy(() -> point.use(null))
+                .isInstanceOf(CoreException.class)
+                .extracting(ex -> ((CoreException) ex).getErrorType())
+                .isEqualTo(ErrorType.BAD_REQUEST);
     }
 }
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductServiceIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductServiceIntegrationTest.java
new file mode 100644
index 000000000..3cf114b77
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductServiceIntegrationTest.java
@@ -0,0 +1,153 @@
+package com.loopers.domain.product;
+
+import com.loopers.domain.brand.Brand;
+import com.loopers.domain.brand.BrandRepository;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import com.loopers.utils.DatabaseCleanUp;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@SpringBootTest
+class ProductServiceIntegrationTest {
+
+    @Autowired
+    private ProductService productService;
+
+    @MockitoSpyBean
+    private ProductRepository productRepository;
+
+    @MockitoSpyBean
+    private BrandRepository brandRepository;
+
+    @Autowired
+    private DatabaseCleanUp databaseCleanUp;
+
+    private Brand savedBrand1;
+    private Brand savedBrand2;
+    private Product savedProduct1;
+    private Product savedProduct2;
+    private Product savedProduct3;
+
+    @BeforeEach
+    void setUp() {
+        savedBrand1 = brandRepository.save(Brand.create("Brand A"));
+        savedBrand2 = brandRepository.save(Brand.create("Brand B"));
+
+        // Brand A 소속 상품 2개
+        savedProduct1 = productRepository.save(Product.create("Product 1 (A)", 1000L, 10, savedBrand1));
+        savedProduct2 = productRepository.save(Product.create("Product 2 (A)", 2000L, 20, savedBrand1));
+
+        // Brand B 소속 상품 1개
+        savedProduct3 = productRepository.save(Product.create("Product 3 (B)", 3000L, 30, savedBrand2));
+    }
+
+    @AfterEach
+    void tearDown() {
+        databaseCleanUp.truncateAllTables();
+    }
+
+    @DisplayName("ID로 상품 단건 조회를 할 수 있다.")
+    @Test
+    void getProduct() {
+        // act
+        Product foundProduct = productService.getProduct(savedProduct1.getId());
+
+        // assert
+        assertAll(
+                () -> assertThat(foundProduct).isNotNull(),
+                () -> assertThat(foundProduct.getId()).isEqualTo(savedProduct1.getId()),
+                () -> assertThat(foundProduct.getName()).isEqualTo("Product 1 (A)")
+        );
+    }
+
+    @DisplayName("존재하지 않는 ID로 상품 조회 시, NOT_FOUND 예외가 발생한다.")
+    @Test
+    void getProduct_throwsNotFound() {
+        // act & assert
+        CoreException exception = assertThrows(CoreException.class, () -> {
+            productService.getProduct(-1L);
+        });
+
+        assertThat(exception.getErrorType()).isEqualTo(ErrorType.NOT_FOUND);
+    }
+
+    @DisplayName("ID 목록으로 여러 상품을 조회할 수 있다.")
+    @Test
+    void getProductsByIds() {
+        // act
+        List<Product> products = productService.getProductsByIds(
+                List.of(savedProduct1.getId(), savedProduct3.getId())
+        );
+
+        // assert
+        assertThat(products).hasSize(2);
+        assertThat(products).extracting(Product::getName)
+                .containsExactlyInAnyOrder("Product 1 (A)", "Product 3 (B)");
+    }
+
+    @DisplayName("ID 목록 중 존재하지 않는 ID가 포함되면, NOT_FOUND 예외가 발생한다.")
+    @Test
+    void getProductsByIds_throwsNotFound() {
+        // act & assert
+        CoreException exception = assertThrows(CoreException.class, () -> {
+            productService.getProductsByIds(List.of(savedProduct1.getId(), -1L));
+        });
+
+        assertThat(exception.getErrorType()).isEqualTo(ErrorType.NOT_FOUND);
+    }
+
+    @DisplayName("상품 목록을 페이징하여 조회할 수 있다 (브랜드 ID 필터링).")
+    @Test
+    void getProducts_withPagingAndBrandFilter() {
+        // arrange
+        Pageable pageable = PageRequest.of(0, 1, Sort.by("id").ascending());
+
+        // act
+        ProductSearchCondition productSearchCondition = new ProductSearchCondition(savedBrand1.getId(), pageable);
+        Page<Product> productPage = productService.getProducts(productSearchCondition);
+
+        // assert
+        assertAll(
+                () -> assertThat(productPage.getTotalElements()).isEqualTo(2),
+                () -> assertThat(productPage.getTotalPages()).isEqualTo(2),
+                () -> assertThat(productPage.getContent()).hasSize(1),
+                () -> assertThat(productPage.getContent().get(0).getName()).isEqualTo("Product 1 (A)")
+        );
+    }
+
+    @DisplayName("상품 목록을 페이징하여 조회할 수 있다 (필터링 없음).")
+    @Test
+    void getProducts_withPaging() {
+        // arrange
+        Pageable pageable = PageRequest.of(0, 2, Sort.by("id").ascending());
+
+        // act
+        ProductSearchCondition productSearchCondition = new ProductSearchCondition(null, pageable);
+        Page<Product> productPage = productService.getProducts(productSearchCondition);
+
+        // assert
+        assertAll(
+                () -> assertThat(productPage.getTotalElements()).isEqualTo(3),
+                () -> assertThat(productPage.getTotalPages()).isEqualTo(2),
+                () -> assertThat(productPage.getContent()).hasSize(2),
+                () -> assertThat(productPage.getContent().get(0).getName()).isEqualTo("Product 1 (A)"),
+                () -> assertThat(productPage.getContent().get(1).getName()).isEqualTo("Product 2 (A)")
+        );
+    }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductTest.java
new file mode 100644
index 000000000..c1ade7797
--- /dev/null
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductTest.java
@@ -0,0 +1,135 @@
+package com.loopers.domain.product;
+
+import com.loopers.domain.brand.Brand;
+import com.loopers.support.error.CoreException;
+import com.loopers.support.error.ErrorType;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class ProductTest {
+
+    private Brand dummyBrand;
+
+    @BeforeEach
+    void setUp() {
+        dummyBrand = Brand.create("Dummy Brand");
+    }
+
+    @Nested
+    @DisplayName("상품 생성 (Product.create)")
+    class CreateProduct {
+
+        @DisplayName("유효한 정보로 상품을 생성할 수 있다.")
+        @Test
+        void createProduct() {
+            // act
+            Product product = Product.create("Test Product", 15000L, 50, dummyBrand);
+
+            // assert
+            assertThat(product.getName()).isEqualTo("Test Product");
+            assertThat(product.getPriceValue()).isEqualTo(15000L);
+            assertThat(product.getStockValue()).isEqualTo(50);
+            assertThat(product.getBrand()).isEqualTo(dummyBrand);
+        }
+
+        @DisplayName("상품명이 null이거나 공백이면 BAD_REQUEST 예외가 발생한다.")
+        @ParameterizedTest
+        @ValueSource(strings = {"", "   "})
+        void throwsException_whenNameIsBlank(String invalidName) {
+            // act & assert
+            assertThatThrownBy(() -> Product.create(invalidName, 1000L, 10, dummyBrand))
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+        }
+
+        @DisplayName("가격이 null이면 BAD_REQUEST 예외가 발생한다.")
+        @Test
+        void throwsException_whenPriceIsNull() {
+            // act & assert
+            assertThatThrownBy(() -> Product.create("Test", null, 10, dummyBrand))
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+        }
+
+        @DisplayName("가격이 음수이면 BAD_REQUEST 예외가 발생한다.")
+        @Test
+        void throwsException_whenPriceIsNegative() {
+            // act & assert
+            assertThatThrownBy(() -> Product.create("Test", -10L, 10, dummyBrand))
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+        }
+
+        @DisplayName("재고가 null이면 BAD_REQUEST 예외가 발생한다.")
+        @Test
+        void throwsException_whenStockIsNull() {
+            // act & assert
+            assertThatThrownBy(() -> Product.create("Test", 1000L, null, dummyBrand))
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+        }
+
+        @DisplayName("재고가 음수이면 BAD_REQUEST 예외가 발생한다.")
+        @Test
+        void throwsException_whenStockIsNegative() {
+            // act & assert
+            assertThatThrownBy(() -> Product.create("Test", 1000L, -1, dummyBrand))
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+        }
+    }
+
+    @Nested
+    @DisplayName("재고 관리 (decreaseStock, isStockAvailable)")
+    class ManageStock {
+
+        private Product product;
+
+        @BeforeEach
+        void setUp() {
+            product = Product.create("Test Product", 1000L, 10, dummyBrand);
+        }
+
+        @DisplayName("재고를 차감할 수 있다.")
+        @Test
+        void decreaseStock() {
+            // act
+            product.decreaseStock(3);
+            // assert
+            assertThat(product.getStockValue()).isEqualTo(7);
+        }
+
+        @DisplayName("차감 수량이 0 이하면 BAD_REQUEST 예외가 발생한다.")
+        @ParameterizedTest
+        @ValueSource(ints = {0, -1})
+        void throwsException_whenDecreaseQuantityIsInvalid(int invalidQuantity) {
+            // act & assert
+            assertThatThrownBy(() -> product.decreaseStock(invalidQuantity))
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+        }
+
+        @DisplayName("재고보다 많은 수량을 차감하면 BAD_REQUEST 예외가 발생한다.")
+        @Test
+        void throwsException_whenDecreaseQuantityIsMoreThanStock() {
+            // act & assert
+            assertThatThrownBy(() -> product.decreaseStock(11))
+                    .isInstanceOf(CoreException.class)
+                    .extracting(ex -> ((CoreException) ex).getErrorType())
+                    .isEqualTo(ErrorType.BAD_REQUEST);
+        }
+    }
+}
diff --git a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/ExampleV1ApiE2ETest.java b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/ExampleV1ApiE2ETest.java
deleted file mode 100644
index 1bb3dba65..000000000
--- a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/ExampleV1ApiE2ETest.java
+++ /dev/null
@@ -1,114 +0,0 @@
-package com.loopers.interfaces.api;
-
-import com.loopers.domain.example.ExampleModel;
-import com.loopers.infrastructure.example.ExampleJpaRepository;
-import com.loopers.interfaces.api.example.ExampleV1Dto;
-import com.loopers.utils.DatabaseCleanUp;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.boot.test.web.client.TestRestTemplate;
-import org.springframework.core.ParameterizedTypeReference;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpMethod;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-
-import java.util.function.Function;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.assertAll;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
-class ExampleV1ApiE2ETest {
-
-    private static final Function<Long, String> ENDPOINT_GET = id -> "/api/v1/examples/" + id;
-
-    private final TestRestTemplate testRestTemplate;
-    private final ExampleJpaRepository exampleJpaRepository;
-    private final DatabaseCleanUp databaseCleanUp;
-
-    @Autowired
-    public ExampleV1ApiE2ETest(
-        TestRestTemplate testRestTemplate,
-        ExampleJpaRepository exampleJpaRepository,
-        DatabaseCleanUp databaseCleanUp
-    ) {
-        this.testRestTemplate = testRestTemplate;
-        this.exampleJpaRepository = exampleJpaRepository;
-        this.databaseCleanUp = databaseCleanUp;
-    }
-
-    @AfterEach
-    void tearDown() {
-        databaseCleanUp.truncateAllTables();
-    }
-
-    @DisplayName("GET /api/v1/examples/{id}")
-    @Nested
-    class Get {
-        @DisplayName("존재하는 예시 ID를 주면, 해당 예시 정보를 반환한다.")
-        @Test
-        void returnsExampleInfo_whenValidIdIsProvided() {
-            // arrange
-            ExampleModel exampleModel = exampleJpaRepository.save(
-                new ExampleModel("예시 제목", "예시 설명")
-            );
-            String requestUrl = ENDPOINT_GET.apply(exampleModel.getId());
-
-            // act
-            ParameterizedTypeReference<ApiResponse<ExampleV1Dto.ExampleResponse>> responseType = new ParameterizedTypeReference<>() {};
-            ResponseEntity<ApiResponse<ExampleV1Dto.ExampleResponse>> response =
-                testRestTemplate.exchange(requestUrl, HttpMethod.GET, new HttpEntity<>(null), responseType);
-
-            // assert
-            assertAll(
-                () -> assertTrue(response.getStatusCode().is2xxSuccessful()),
-                () -> assertThat(response.getBody().data().id()).isEqualTo(exampleModel.getId()),
-                () -> assertThat(response.getBody().data().name()).isEqualTo(exampleModel.getName()),
-                () -> assertThat(response.getBody().data().description()).isEqualTo(exampleModel.getDescription())
-            );
-        }
-
-        @DisplayName("숫자가 아닌 ID 로 요청하면, 400 BAD_REQUEST 응답을 받는다.")
-        @Test
-        void throwsBadRequest_whenIdIsNotProvided() {
-            // arrange
-            String requestUrl = "/api/v1/examples/나나";
-
-            // act
-            ParameterizedTypeReference<ApiResponse<ExampleV1Dto.ExampleResponse>> responseType = new ParameterizedTypeReference<>() {};
-            ResponseEntity<ApiResponse<ExampleV1Dto.ExampleResponse>> response =
-                testRestTemplate.exchange(requestUrl, HttpMethod.GET, new HttpEntity<>(null), responseType);
-
-            // assert
-            assertAll(
-                () -> assertTrue(response.getStatusCode().is4xxClientError()),
-                () -> assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST)
-            );
-        }
-
-        @DisplayName("존재하지 않는 예시 ID를 주면, 404 NOT_FOUND 응답을 받는다.")
-        @Test
-        void throwsException_whenInvalidIdIsProvided() {
-            // arrange
-            Long invalidId = -1L;
-            String requestUrl = ENDPOINT_GET.apply(invalidId);
-
-            // act
-            ParameterizedTypeReference<ApiResponse<ExampleV1Dto.ExampleResponse>> responseType = new ParameterizedTypeReference<>() {};
-            ResponseEntity<ApiResponse<ExampleV1Dto.ExampleResponse>> response =
-                testRestTemplate.exchange(requestUrl, HttpMethod.GET, new HttpEntity<>(null), responseType);
-
-            // assert
-            assertAll(
-                () -> assertTrue(response.getStatusCode().is4xxClientError()),
-                () -> assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND)
-            );
-        }
-    }
-}
diff --git a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/point/PointV1ApiE2ETest.java b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/point/PointV1ApiE2ETest.java
index 40285e4f7..a31bea744 100644
--- a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/point/PointV1ApiE2ETest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/point/PointV1ApiE2ETest.java
@@ -1,5 +1,6 @@
 package com.loopers.interfaces.api.point;
 
+import com.loopers.application.point.PointCommand;
 import com.loopers.application.point.PointFacade;
 import com.loopers.domain.user.Gender;
 import com.loopers.interfaces.api.ApiResponse;
@@ -63,7 +64,8 @@ void returnsPointAmount_whenPointExists() {
 			testRestTemplate.postForEntity(ENDPOINT_SIGN_UP, registerRequest, ApiResponse.class);
 
 			// 포인트 충전
-			pointFacade.chargePoint("testuser01", 5000L);
+            PointCommand command = new PointCommand("testuser01", 5000L);
+			pointFacade.chargePoint(command);
 
 			// HTTP 헤더 설정
 			HttpHeaders headers = new HttpHeaders();
diff --git a/docs/week2/01-requirements.md b/docs/week2/01-requirements.md
index 2e0287e37..5f55a11b3 100644
--- a/docs/week2/01-requirements.md
+++ b/docs/week2/01-requirements.md
@@ -39,7 +39,7 @@
     2-1. 쿼리 파라미터 검증 (brandId, sort, page, size)
     2-2. Pageable 객체 생성 및 정렬 조건 적용
         - latest: createdAt desc (기본값)
-        - price_asc: price asc
+        - price_asc: productPrice asc
         - likes_desc: likeCount desc
     2-3. brandId가 있는 경우 해당 브랜드의 상품만 필터링
     2-4. 상품 목록 조회 및 각 상품의 좋아요 수 포함
diff --git a/docs/week2/02-sequence-diagrams.md b/docs/week2/02-sequence-diagrams.md
index 890dfbc19..89efaf052 100644
--- a/docs/week2/02-sequence-diagrams.md
+++ b/docs/week2/02-sequence-diagrams.md
@@ -28,7 +28,7 @@ sequenceDiagram
     participant ProductService
     participant LikeService
 
-    %% /api/v1/products?brandId=1&sort=createdAt,desc&sort=price,asc&sort=likes,desc&page=0&size=20
+    %% /api/v1/products?brandId=1&sort=createdAt,desc&sort=productPrice,asc&sort=likes,desc&page=0&size=20
     User->>ProductController: GET /api/v1/products?brandId={}&sort={}&page={}&size={}
     note right of User: Header: X-USER-ID: {userId}
 
diff --git a/docs/week2/03-class-diagram.md b/docs/week2/03-class-diagram.md
index 58fa3de74..a4bc80942 100644
--- a/docs/week2/03-class-diagram.md
+++ b/docs/week2/03-class-diagram.md
@@ -27,7 +27,7 @@ classDiagram
         -Long id
         -Brand brand
         -String name
-        -Long price
+        -Long productPrice
         -Long stock
         +decreaseStock()
         +increaseLikes()
