From 28c84888d130f02f9d06d4cd9fb5e06758693b4f Mon Sep 17 00:00:00 2001 From: appleg Date: Sat, 19 Mar 2022 22:13:36 +0900 Subject: [PATCH] =?UTF-8?q?[230319]=20=EA=B2=B0=EC=A0=9C=EC=99=84=EB=A3=8C?= =?UTF-8?q?=20command=20=EA=B0=9C=EB=B0=9C=20-=20=EA=B0=81=20=ED=8F=B4?= =?UTF-8?q?=EB=8D=94=20=EA=B5=AC=EC=A1=B0=20=EC=83=9D=EC=84=B1=20-=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=20Event=20=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20-=20kafka=20message=20sender=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EA=B9=8C=EC=A7=80=20=ED=99=95=EC=9D=B8=20-?= =?UTF-8?q?=20=EA=B2=B0=EC=A0=9C=20=EC=99=84=EB=A3=8C=20=EC=98=88=EC=8B=9C?= =?UTF-8?q?=20application=20service?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yaml | 8 ++-- .../src/main/java/com/example/Listener.java | 14 ------ .../java/com/example/common/IdResponse.java | 20 ++++++++ .../java/com/example/config/JpaConfig.java | 9 ++++ .../java/com/example/event/DomainEvent.java | 6 +++ .../com/example/event/EventPublisher.java | 6 +++ .../example/event/KafkaEventPublisher.java | 19 ++++++++ .../payment/application/PaymentService.java | 8 ++++ .../application/PaymentServiceImpl.java | 40 ++++++++++++++++ .../com/example/payment/domain/Payment.java | 47 +++++++++++++++++++ .../payment/domain/PaymentRepository.java | 10 ++++ .../payment/domain/event/PaymentCreated.java | 46 ++++++++++++++++++ .../payment/infra/http/PaymentController.java | 23 +++++++++ .../http/request/PaymentCompleteRequest.java | 16 +++++++ .../persistence/PaymentJpaRepository.java | 7 +++ .../persistence/PaymentRepositoryImpl.java | 24 ++++++++++ .../com/example/purchase/domain/Purchase.java | 34 ++++++++++++++ .../purchase/domain/PurchaseRepository.java | 10 ++++ .../infra/http/PurchaseController.java | 18 +++++++ .../persistence/PurchaseJpaRepository.java | 7 +++ .../persistence/PurchaseRepositoryImpl.java | 24 ++++++++++ .../java/com/example/user/domain/Cash.java | 43 +++++++++++++++++ .../java/com/example/user/domain/User.java | 43 +++++++++++++++++ .../example/user/domain/UserRepository.java | 8 ++++ .../infra/persistence/UserJpaRepositroy.java | 7 +++ .../infra/persistence/UserRepositoryImpl.java | 24 ++++++++++ .../src/main/resources/application.yaml | 4 ++ .../payment/PaymentApplicationTests.java | 8 +++- .../com/example/PaymentCreatedListener.java | 14 ++++++ .../com/example/PaymentQueryApplication.java | 2 +- .../payment/domain/event/PaymentCreated.java | 27 +++++++++++ .../src/main/resources/application.yaml | 23 +++++++++ .../main/resources/resources/application.yaml | 14 ------ 33 files changed, 578 insertions(+), 35 deletions(-) delete mode 100644 payment-command/src/main/java/com/example/Listener.java create mode 100644 payment-command/src/main/java/com/example/common/IdResponse.java create mode 100644 payment-command/src/main/java/com/example/config/JpaConfig.java create mode 100644 payment-command/src/main/java/com/example/event/DomainEvent.java create mode 100644 payment-command/src/main/java/com/example/event/EventPublisher.java create mode 100644 payment-command/src/main/java/com/example/event/KafkaEventPublisher.java create mode 100644 payment-command/src/main/java/com/example/payment/application/PaymentService.java create mode 100644 payment-command/src/main/java/com/example/payment/application/PaymentServiceImpl.java create mode 100644 payment-command/src/main/java/com/example/payment/domain/Payment.java create mode 100644 payment-command/src/main/java/com/example/payment/domain/PaymentRepository.java create mode 100644 payment-command/src/main/java/com/example/payment/domain/event/PaymentCreated.java create mode 100644 payment-command/src/main/java/com/example/payment/infra/http/PaymentController.java create mode 100644 payment-command/src/main/java/com/example/payment/infra/http/request/PaymentCompleteRequest.java create mode 100644 payment-command/src/main/java/com/example/payment/infra/persistence/PaymentJpaRepository.java create mode 100644 payment-command/src/main/java/com/example/payment/infra/persistence/PaymentRepositoryImpl.java create mode 100644 payment-command/src/main/java/com/example/purchase/domain/Purchase.java create mode 100644 payment-command/src/main/java/com/example/purchase/domain/PurchaseRepository.java create mode 100644 payment-command/src/main/java/com/example/purchase/infra/http/PurchaseController.java create mode 100644 payment-command/src/main/java/com/example/purchase/infra/persistence/PurchaseJpaRepository.java create mode 100644 payment-command/src/main/java/com/example/purchase/infra/persistence/PurchaseRepositoryImpl.java create mode 100644 payment-command/src/main/java/com/example/user/domain/Cash.java create mode 100644 payment-command/src/main/java/com/example/user/domain/User.java create mode 100644 payment-command/src/main/java/com/example/user/domain/UserRepository.java create mode 100644 payment-command/src/main/java/com/example/user/infra/persistence/UserJpaRepositroy.java create mode 100644 payment-command/src/main/java/com/example/user/infra/persistence/UserRepositoryImpl.java create mode 100644 payment-query/src/main/java/com/example/PaymentCreatedListener.java rename payment-query/src/main/java/{java => }/com/example/PaymentQueryApplication.java (92%) create mode 100644 payment-query/src/main/java/com/example/payment/domain/event/PaymentCreated.java create mode 100644 payment-query/src/main/resources/application.yaml delete mode 100644 payment-query/src/main/resources/resources/application.yaml diff --git a/docker-compose.yaml b/docker-compose.yaml index 7b81233..2e1918b 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -12,7 +12,7 @@ services: - "3306:3306" volumes: - ./db:/var/lib/mysql -# platform: linux/amd64 # for m1 mac + # platform: linux/amd64 # for m1 mac mysql-query: container_name: query-mysql image: mysql:8.0.21 @@ -22,10 +22,10 @@ services: - MYSQL_ROOT_PASSWORD=payment - TZ=UTC ports: - - "3307:3307" + - "3307:3306" volumes: - - ./db:/var/lib/mysql -# platform: linux/amd64 # for m1 mac + - ./db2:/var/lib/mysql + # platform: linux/amd64 # for m1 mac zookeeper: container_name: payment-zookeeper image: wurstmeister/zookeeper diff --git a/payment-command/src/main/java/com/example/Listener.java b/payment-command/src/main/java/com/example/Listener.java deleted file mode 100644 index 1b1f0dc..0000000 --- a/payment-command/src/main/java/com/example/Listener.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.example; - -import org.springframework.kafka.annotation.KafkaListener; -import org.springframework.stereotype.Service; - -import java.io.IOException; - -@Service -public class Listener { - @KafkaListener(topics = "topic", groupId = "group1") - public void consume(String message) { - System.out.println("receive message : " + message); - } -} \ No newline at end of file diff --git a/payment-command/src/main/java/com/example/common/IdResponse.java b/payment-command/src/main/java/com/example/common/IdResponse.java new file mode 100644 index 0000000..e8e9698 --- /dev/null +++ b/payment-command/src/main/java/com/example/common/IdResponse.java @@ -0,0 +1,20 @@ +package com.example.common; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public class IdResponse { + + private T id; + + private IdResponse(T id) { + this.id = id; + } + + public static IdResponse of(T id) { + return new IdResponse<>(id); + } +} diff --git a/payment-command/src/main/java/com/example/config/JpaConfig.java b/payment-command/src/main/java/com/example/config/JpaConfig.java new file mode 100644 index 0000000..ec57dd9 --- /dev/null +++ b/payment-command/src/main/java/com/example/config/JpaConfig.java @@ -0,0 +1,9 @@ +package com.example.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +@Configuration +@EnableJpaAuditing +public class JpaConfig { +} diff --git a/payment-command/src/main/java/com/example/event/DomainEvent.java b/payment-command/src/main/java/com/example/event/DomainEvent.java new file mode 100644 index 0000000..8619646 --- /dev/null +++ b/payment-command/src/main/java/com/example/event/DomainEvent.java @@ -0,0 +1,6 @@ +package com.example.event; + +public interface DomainEvent { + + String topic(); +} diff --git a/payment-command/src/main/java/com/example/event/EventPublisher.java b/payment-command/src/main/java/com/example/event/EventPublisher.java new file mode 100644 index 0000000..63ba0cc --- /dev/null +++ b/payment-command/src/main/java/com/example/event/EventPublisher.java @@ -0,0 +1,6 @@ +package com.example.event; + +public interface EventPublisher { + + void publish(DomainEvent event); +} diff --git a/payment-command/src/main/java/com/example/event/KafkaEventPublisher.java b/payment-command/src/main/java/com/example/event/KafkaEventPublisher.java new file mode 100644 index 0000000..7946630 --- /dev/null +++ b/payment-command/src/main/java/com/example/event/KafkaEventPublisher.java @@ -0,0 +1,19 @@ +package com.example.event; + +import lombok.RequiredArgsConstructor; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class KafkaEventPublisher implements EventPublisher { + + private final KafkaTemplate kafkaProducer; + + @Override + public void publish(DomainEvent event) { + String topic = event.topic(); + kafkaProducer.send(new ProducerRecord<>(topic, event)); + } +} diff --git a/payment-command/src/main/java/com/example/payment/application/PaymentService.java b/payment-command/src/main/java/com/example/payment/application/PaymentService.java new file mode 100644 index 0000000..8790cbd --- /dev/null +++ b/payment-command/src/main/java/com/example/payment/application/PaymentService.java @@ -0,0 +1,8 @@ +package com.example.payment.application; + +import com.example.payment.infra.http.request.PaymentCompleteRequest; + +public interface PaymentService { + + String completePayment(PaymentCompleteRequest request); +} diff --git a/payment-command/src/main/java/com/example/payment/application/PaymentServiceImpl.java b/payment-command/src/main/java/com/example/payment/application/PaymentServiceImpl.java new file mode 100644 index 0000000..2129ecb --- /dev/null +++ b/payment-command/src/main/java/com/example/payment/application/PaymentServiceImpl.java @@ -0,0 +1,40 @@ +package com.example.payment.application; + +import com.example.user.domain.Cash; +import com.example.event.EventPublisher; +import com.example.payment.domain.Payment; +import com.example.payment.domain.PaymentRepository; +import com.example.payment.domain.event.PaymentCreated; +import com.example.payment.infra.http.request.PaymentCompleteRequest; +import com.example.user.domain.User; +import com.example.user.domain.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class PaymentServiceImpl implements PaymentService { + + private final EventPublisher eventPublisher; + private final UserRepository userRepository; + private final PaymentRepository paymentRepository; + + @Override + @Transactional + public String completePayment(PaymentCompleteRequest request) { + // 1. 유저의 잔액 추가 / 캐시 생성 + User user = userRepository.getByIdOrDefault(request.getUserId()); + user.increaseBalance(request.getAmount()); + String cashId = user.addNewCash(Cash.of(request.getAmount(), user)); + userRepository.save(user); + + // 2. 결제 데이터 생성 + String paymentId = paymentRepository.save(Payment.of(request.getPgId(), request.getPayBy(), user)); + + // 3. 이벤트 발행 + eventPublisher.publish(new PaymentCreated(request, user.getUserId(), paymentId, cashId)); + + return paymentId; + } +} diff --git a/payment-command/src/main/java/com/example/payment/domain/Payment.java b/payment-command/src/main/java/com/example/payment/domain/Payment.java new file mode 100644 index 0000000..4bbfe2b --- /dev/null +++ b/payment-command/src/main/java/com/example/payment/domain/Payment.java @@ -0,0 +1,47 @@ +package com.example.payment.domain; + +import com.example.user.domain.User; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import java.time.LocalDateTime; +import java.util.UUID; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class Payment { + + @Id + private String id; + + private String pgId; + + private String payBy; + + @ManyToOne + @JoinColumn + private User user; + + @CreatedDate + private LocalDateTime createdAt; + + @LastModifiedDate + private LocalDateTime updatedAt; + + public String getId() { + return id; + } + + public static Payment of(String pgId, String payBy, User user) { + return new Payment(UUID.fromString("payment").toString(), pgId, payBy, user, null, null); + } + +} diff --git a/payment-command/src/main/java/com/example/payment/domain/PaymentRepository.java b/payment-command/src/main/java/com/example/payment/domain/PaymentRepository.java new file mode 100644 index 0000000..aa62ef6 --- /dev/null +++ b/payment-command/src/main/java/com/example/payment/domain/PaymentRepository.java @@ -0,0 +1,10 @@ +package com.example.payment.domain; + +import java.util.Optional; + +public interface PaymentRepository { + + String save(Payment purchase); + + Optional getById(String id); +} diff --git a/payment-command/src/main/java/com/example/payment/domain/event/PaymentCreated.java b/payment-command/src/main/java/com/example/payment/domain/event/PaymentCreated.java new file mode 100644 index 0000000..2fcdb10 --- /dev/null +++ b/payment-command/src/main/java/com/example/payment/domain/event/PaymentCreated.java @@ -0,0 +1,46 @@ +package com.example.payment.domain.event; + +import com.example.event.DomainEvent; +import com.example.payment.infra.http.request.PaymentCompleteRequest; +import java.io.Serializable; +import java.math.BigDecimal; +import lombok.Getter; + +@Getter +public class PaymentCreated implements DomainEvent, Serializable { + + private final String userId; + + private final String paymentId; + + private final String cashId; + + private final BigDecimal amount; + + private final String payBy; + + private final String pgId; + + @Override + public String topic() { + return "PaymentCreated"; + } + + public PaymentCreated(PaymentCompleteRequest request, String userId, String paymentId, String cashId) { + this.userId = userId; + this.paymentId = paymentId; + this.cashId = cashId; + this.amount = request.getAmount(); + this.payBy = request.getPayBy(); + this.pgId = request.getPgId(); + } + + public PaymentCreated(String userId, String paymentId, String cashId, BigDecimal amount, String payBy, String pgId) { + this.userId = userId; + this.paymentId = paymentId; + this.cashId = cashId; + this.amount = amount; + this.payBy = payBy; + this.pgId = pgId; + } +} diff --git a/payment-command/src/main/java/com/example/payment/infra/http/PaymentController.java b/payment-command/src/main/java/com/example/payment/infra/http/PaymentController.java new file mode 100644 index 0000000..f5a04cf --- /dev/null +++ b/payment-command/src/main/java/com/example/payment/infra/http/PaymentController.java @@ -0,0 +1,23 @@ +package com.example.payment.infra.http; + +import com.example.common.IdResponse; +import com.example.payment.application.PaymentService; +import com.example.payment.infra.http.request.PaymentCompleteRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping(("/payments")) +@RequiredArgsConstructor +public class PaymentController { + + private final PaymentService paymentService; + + @PostMapping("/complete") + public IdResponse paymentComplete(@RequestBody PaymentCompleteRequest request) { + return IdResponse.of(paymentService.completePayment(request)); + } +} diff --git a/payment-command/src/main/java/com/example/payment/infra/http/request/PaymentCompleteRequest.java b/payment-command/src/main/java/com/example/payment/infra/http/request/PaymentCompleteRequest.java new file mode 100644 index 0000000..4835149 --- /dev/null +++ b/payment-command/src/main/java/com/example/payment/infra/http/request/PaymentCompleteRequest.java @@ -0,0 +1,16 @@ +package com.example.payment.infra.http.request; + +import java.math.BigDecimal; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public class PaymentCompleteRequest { + + private String userId; + private BigDecimal amount; + private String payBy; + private String pgId; +} diff --git a/payment-command/src/main/java/com/example/payment/infra/persistence/PaymentJpaRepository.java b/payment-command/src/main/java/com/example/payment/infra/persistence/PaymentJpaRepository.java new file mode 100644 index 0000000..fe4fb38 --- /dev/null +++ b/payment-command/src/main/java/com/example/payment/infra/persistence/PaymentJpaRepository.java @@ -0,0 +1,7 @@ +package com.example.payment.infra.persistence; + +import com.example.payment.domain.Payment; +import org.springframework.data.repository.CrudRepository; + +public interface PaymentJpaRepository extends CrudRepository { +} diff --git a/payment-command/src/main/java/com/example/payment/infra/persistence/PaymentRepositoryImpl.java b/payment-command/src/main/java/com/example/payment/infra/persistence/PaymentRepositoryImpl.java new file mode 100644 index 0000000..45f1f03 --- /dev/null +++ b/payment-command/src/main/java/com/example/payment/infra/persistence/PaymentRepositoryImpl.java @@ -0,0 +1,24 @@ +package com.example.payment.infra.persistence; + +import com.example.payment.domain.Payment; +import com.example.payment.domain.PaymentRepository; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class PaymentRepositoryImpl implements PaymentRepository { + + private final PaymentJpaRepository jpaRepository; + + @Override + public String save(Payment payment) { + return jpaRepository.save(payment).getId(); + } + + @Override + public Optional getById(String id) { + return jpaRepository.findById(id); + } +} diff --git a/payment-command/src/main/java/com/example/purchase/domain/Purchase.java b/payment-command/src/main/java/com/example/purchase/domain/Purchase.java new file mode 100644 index 0000000..4f75871 --- /dev/null +++ b/payment-command/src/main/java/com/example/purchase/domain/Purchase.java @@ -0,0 +1,34 @@ +package com.example.purchase.domain; + +import com.example.user.domain.User; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import java.time.LocalDateTime; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Purchase { + + @Id + private String id; + + @ManyToOne + @JoinColumn + private User user; + + @CreatedDate + private LocalDateTime createdAt; + + @LastModifiedDate + private LocalDateTime updatedAt; + + public String getId() { + return id; + } +} diff --git a/payment-command/src/main/java/com/example/purchase/domain/PurchaseRepository.java b/payment-command/src/main/java/com/example/purchase/domain/PurchaseRepository.java new file mode 100644 index 0000000..1750fa0 --- /dev/null +++ b/payment-command/src/main/java/com/example/purchase/domain/PurchaseRepository.java @@ -0,0 +1,10 @@ +package com.example.purchase.domain; + +import java.util.Optional; + +public interface PurchaseRepository { + + String save(Purchase purchase); + + Optional getById(String id); +} diff --git a/payment-command/src/main/java/com/example/purchase/infra/http/PurchaseController.java b/payment-command/src/main/java/com/example/purchase/infra/http/PurchaseController.java new file mode 100644 index 0000000..f77ce98 --- /dev/null +++ b/payment-command/src/main/java/com/example/purchase/infra/http/PurchaseController.java @@ -0,0 +1,18 @@ +package com.example.purchase.infra.http; + +import com.example.common.IdResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/purchases") +@RequiredArgsConstructor +public class PurchaseController { + + @PostMapping() + public IdResponse purchase() { + return IdResponse.of(1L); + } +} diff --git a/payment-command/src/main/java/com/example/purchase/infra/persistence/PurchaseJpaRepository.java b/payment-command/src/main/java/com/example/purchase/infra/persistence/PurchaseJpaRepository.java new file mode 100644 index 0000000..51b0fc9 --- /dev/null +++ b/payment-command/src/main/java/com/example/purchase/infra/persistence/PurchaseJpaRepository.java @@ -0,0 +1,7 @@ +package com.example.purchase.infra.persistence; + +import com.example.purchase.domain.Purchase; +import org.springframework.data.repository.CrudRepository; + +public interface PurchaseJpaRepository extends CrudRepository { +} diff --git a/payment-command/src/main/java/com/example/purchase/infra/persistence/PurchaseRepositoryImpl.java b/payment-command/src/main/java/com/example/purchase/infra/persistence/PurchaseRepositoryImpl.java new file mode 100644 index 0000000..a67874d --- /dev/null +++ b/payment-command/src/main/java/com/example/purchase/infra/persistence/PurchaseRepositoryImpl.java @@ -0,0 +1,24 @@ +package com.example.purchase.infra.persistence; + +import com.example.purchase.domain.Purchase; +import com.example.purchase.domain.PurchaseRepository; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class PurchaseRepositoryImpl implements PurchaseRepository { + + private final PurchaseJpaRepository jpaRepository; + + @Override + public String save(Purchase purchase) { + return jpaRepository.save(purchase).getId(); + } + + @Override + public Optional getById(String id) { + return jpaRepository.findById(id); + } +} diff --git a/payment-command/src/main/java/com/example/user/domain/Cash.java b/payment-command/src/main/java/com/example/user/domain/Cash.java new file mode 100644 index 0000000..34bb013 --- /dev/null +++ b/payment-command/src/main/java/com/example/user/domain/Cash.java @@ -0,0 +1,43 @@ +package com.example.user.domain; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.UUID; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class Cash { + + @Id + private String id; + + private BigDecimal amount; + + @ManyToOne + @JoinColumn + private User user; + + @CreatedDate + private LocalDateTime createdAt; + + @LastModifiedDate + private LocalDateTime updatedAt; + + public String getId() { + return id; + } + + public static Cash of(BigDecimal amount, User user) { + return new Cash(UUID.fromString("cash").toString(), amount, user, null, null); + } +} diff --git a/payment-command/src/main/java/com/example/user/domain/User.java b/payment-command/src/main/java/com/example/user/domain/User.java new file mode 100644 index 0000000..143b51f --- /dev/null +++ b/payment-command/src/main/java/com/example/user/domain/User.java @@ -0,0 +1,43 @@ +package com.example.user.domain; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; + +@Entity +public class User { + + @Id + private String userId; + + private BigDecimal balance; + + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) + private List cashes = new ArrayList<>(); + + @CreatedDate + private LocalDateTime createdAt; + + @LastModifiedDate + private LocalDateTime updatedAt; + + public String getUserId() { + return userId; + } + + public void increaseBalance(BigDecimal amount) { + balance = balance.add(amount); + } + + public String addNewCash(Cash cash) { + this.cashes.add(cash); + return cash.getId(); + } +} diff --git a/payment-command/src/main/java/com/example/user/domain/UserRepository.java b/payment-command/src/main/java/com/example/user/domain/UserRepository.java new file mode 100644 index 0000000..7e38f3f --- /dev/null +++ b/payment-command/src/main/java/com/example/user/domain/UserRepository.java @@ -0,0 +1,8 @@ +package com.example.user.domain; + +public interface UserRepository { + + User getByIdOrDefault(String id); + + String save(User user); +} diff --git a/payment-command/src/main/java/com/example/user/infra/persistence/UserJpaRepositroy.java b/payment-command/src/main/java/com/example/user/infra/persistence/UserJpaRepositroy.java new file mode 100644 index 0000000..0e70e53 --- /dev/null +++ b/payment-command/src/main/java/com/example/user/infra/persistence/UserJpaRepositroy.java @@ -0,0 +1,7 @@ +package com.example.user.infra.persistence; + +import com.example.user.domain.User; +import org.springframework.data.repository.CrudRepository; + +public interface UserJpaRepositroy extends CrudRepository { +} diff --git a/payment-command/src/main/java/com/example/user/infra/persistence/UserRepositoryImpl.java b/payment-command/src/main/java/com/example/user/infra/persistence/UserRepositoryImpl.java new file mode 100644 index 0000000..407596f --- /dev/null +++ b/payment-command/src/main/java/com/example/user/infra/persistence/UserRepositoryImpl.java @@ -0,0 +1,24 @@ +package com.example.user.infra.persistence; + +import com.example.user.domain.User; +import com.example.user.domain.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class UserRepositoryImpl implements UserRepository { + + private final UserJpaRepositroy jpaRepository; + + @Override + public String save(User user) { + return jpaRepository.save(user).getUserId(); + } + + @Override + public User getByIdOrDefault(String id) { + + return null; + } +} diff --git a/payment-command/src/main/resources/application.yaml b/payment-command/src/main/resources/application.yaml index a61cd03..806981e 100644 --- a/payment-command/src/main/resources/application.yaml +++ b/payment-command/src/main/resources/application.yaml @@ -12,3 +12,7 @@ spring: ddl-auto: update show-sql: true database-platform: org.hibernate.dialect.MySQL8Dialect + kafka: + producer: + key-serializer: org.apache.kafka.common.serialization.StringSerializer + value-serializer: org.springframework.kafka.support.serializer.JsonSerializer \ No newline at end of file diff --git a/payment-command/src/test/java/com/example/payment/PaymentApplicationTests.java b/payment-command/src/test/java/com/example/payment/PaymentApplicationTests.java index b330d9e..8951ec2 100644 --- a/payment-command/src/test/java/com/example/payment/PaymentApplicationTests.java +++ b/payment-command/src/test/java/com/example/payment/PaymentApplicationTests.java @@ -1,5 +1,7 @@ package com.example.payment; +import com.example.payment.domain.event.PaymentCreated; +import java.math.BigDecimal; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -7,12 +9,14 @@ import org.springframework.kafka.core.KafkaTemplate; @SpringBootTest class PaymentApplicationTests { + @Autowired - KafkaTemplate template; + KafkaTemplate template; @Test void contextLoads() { - template.send("topic", "hello this is new topic2!!!!!!"); + PaymentCreated paymentCreated = new PaymentCreated("user", "payment", "cash", BigDecimal.ONE, "payBy", "pgId"); + template.send("PaymentCreated", paymentCreated); } } diff --git a/payment-query/src/main/java/com/example/PaymentCreatedListener.java b/payment-query/src/main/java/com/example/PaymentCreatedListener.java new file mode 100644 index 0000000..7c71dfe --- /dev/null +++ b/payment-query/src/main/java/com/example/PaymentCreatedListener.java @@ -0,0 +1,14 @@ +package com.example; + +import com.example.payment.domain.event.PaymentCreated; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Service; + +@Service +public class PaymentCreatedListener { + + @KafkaListener(topics = "PaymentCreated", groupId = "group1") + public void consume(PaymentCreated message) { + System.out.println("receive message : " + message.toString()); + } +} \ No newline at end of file diff --git a/payment-query/src/main/java/java/com/example/PaymentQueryApplication.java b/payment-query/src/main/java/com/example/PaymentQueryApplication.java similarity index 92% rename from payment-query/src/main/java/java/com/example/PaymentQueryApplication.java rename to payment-query/src/main/java/com/example/PaymentQueryApplication.java index add4a2c..4d90a39 100644 --- a/payment-query/src/main/java/java/com/example/PaymentQueryApplication.java +++ b/payment-query/src/main/java/com/example/PaymentQueryApplication.java @@ -1,4 +1,4 @@ -package java.com.example; +package com.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/payment-query/src/main/java/com/example/payment/domain/event/PaymentCreated.java b/payment-query/src/main/java/com/example/payment/domain/event/PaymentCreated.java new file mode 100644 index 0000000..6a01225 --- /dev/null +++ b/payment-query/src/main/java/com/example/payment/domain/event/PaymentCreated.java @@ -0,0 +1,27 @@ +package com.example.payment.domain.event; + +import java.io.Serializable; +import java.math.BigDecimal; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@ToString +public class PaymentCreated implements Serializable { + + private String userId; + + private String paymentId; + + private String cashId; + + private BigDecimal amount; + + private String payBy; + + private String pgId; + +} diff --git a/payment-query/src/main/resources/application.yaml b/payment-query/src/main/resources/application.yaml new file mode 100644 index 0000000..462fc6c --- /dev/null +++ b/payment-query/src/main/resources/application.yaml @@ -0,0 +1,23 @@ +server: + port: 8082 + +spring: + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3307/payment?serverTimezone=Asia/Seoul&useSSL=false&allowPublicKeyRetrieval=true + username: root + password: payment + jpa: + hibernate: + ddl-auto: update + show-sql: true + database-platform: org.hibernate.dialect.MySQL8Dialect + kafka: + consumer: + key-deserializer: org.apache.kafka.common.serialization.StringDeserializer + value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer + properties: + spring: + json: + trusted: + packages: '*' \ No newline at end of file diff --git a/payment-query/src/main/resources/resources/application.yaml b/payment-query/src/main/resources/resources/application.yaml deleted file mode 100644 index 83654d7..0000000 --- a/payment-query/src/main/resources/resources/application.yaml +++ /dev/null @@ -1,14 +0,0 @@ -server: - port: 8082 - -spring: - datasource: - driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://localhost:3307/payment?serverTimezone=Asia/Seoul&useSSL=false&allowPublicKeyRetrieval=true - username: root - password: payment - jpa: - hibernate: - ddl-auto: update - show-sql: true - database-platform: org.hibernate.dialect.MySQL8Dialect \ No newline at end of file