Skip to content

Commit

Permalink
Merge pull request #4 from this-is-spear/port
Browse files Browse the repository at this point in the history
�유스케이스를 작성한다.
  • Loading branch information
this-is-spear committed Feb 16, 2024
2 parents 09d715e + a42ee75 commit e80ff36
Show file tree
Hide file tree
Showing 55 changed files with 1,178 additions and 127 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ cmake-build-*/
# IntelliJ
out/

!src/main/kotlin/com/example/estdelivery/application/port/out

# mpeltonen/sbt-idea plugin
.idea_modules/

Expand Down
60 changes: 57 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,14 +183,18 @@ erDiagram
long coupon_id
}
coupon {
handout_coupon_book {
long shop_id
long coupon_id
}
coupon {
long id pk
long shop_id
string name
string description
string type
int amount
string type
}
members_coupon_book {
Expand All @@ -203,6 +207,56 @@ erDiagram
royal_member }o--|| shop : manage
members_coupon_book ||--o{ coupon : contains
members_coupon_book ||--o{ member : have
handout_coupon_book ||--o{ shop : have
handout_coupon_book ||--o{ coupon : contains
published_coupon_book ||--o{ shop : have
published_coupon_book ||--o{ coupon : contains
```
```

## 유스 케이스 정리

### 회원은 게시된 쿠폰을 발행한다.

1. 회원 정보를 조회한다.
2. 쿠폰정보를 조회한다.
3. 가게가 가진 게시된 쿠폰 북에서 쿠폰을 발행한다.
4. 발급된 쿠폰을 사용자의 쿠폰북에 추가한다.
5. 가게 단골 손님으로 등록한다.

검증 목록
- 회원은 가게에 게시된 쿠폰북에서 쿠폰을 꺼내 자신의 쿠폰 북에 담는다.
- 이미 가진 쿠폰이라면 발행 할 수 없다.(도메인과 중복 검증)
- 게시되지 않은 쿠폰은 발행 될 수 없다.(도메인과 중복 검증)

### 가게 주인은 쿠폰을 가게에 게시한다.

1. 가게 주인 정보를 조회한다.
2. 게시할 쿠폰을 생성한다.
3. 가게 주인은 가게에다 게시된 쿠폰북에 쿠폰을 게시한다.

검증 목록
- 가게 주인은 가게에 쿠폰을 게시할 수 있다.
- 게시된 쿠폰북에 동일한 쿠폰이 있을 수 없다.(도메인과 중복 검증)


### 가게 주인은 단골들에게 쿠폰을 뿌린다.

1. 가게 주인 정보를 조회한다.
2. 아직 나눠준적 없는 쿠폰이라면 쿠폰을 생성해 나눠준 쿠폰북에 쿠폰을 추가한다.
3. 단골들 중 동일한 쿠폰을 아직 받지 않은 사용자에게 쿠폰을 뿌린다.

검증 목록
- 가게 주인은 가게에 쿠폰을 뿌릴 수 있다.
- 이미 나눠줬던 쿠폰을 다시 나눠줄 때 새로운 단골들에게만 나눠준다.

### 회원은 쿠폰을 사용한다.

1. 회원 정보를 조회한다.
2. 쿠폰 정보를 조회한다.
3. 회원이 가진 쿠폰북에서 쿠폰을 사용한다.
4. 가게에서 사용한 쿠폰을 받는다.

검증 목록
- 회원은 가게에 쿠폰을 사용할 수 있다.
- 이미 사용한 쿠폰은 사용할 수 없다.(도메인과 중복 검증)
- 게시되지 않거나 나눠주지 않은 쿠폰은 사용할 수 없다.(도메인과 중복 검증)
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dependencies {
testImplementation("org.junit.jupiter", "junit-jupiter", "5.8.2")
testImplementation("org.assertj", "assertj-core", "3.22.0")
testImplementation("io.kotest", "kotest-runner-junit5", "5.4.0")
testImplementation("io.mockk:mockk:1.13.9")
}

tasks.withType<KotlinCompile> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.example.estdelivery.application

import com.example.estdelivery.application.port.`in`.HandoutCouponUseCase
import com.example.estdelivery.application.port.`in`.command.HandoutCouponCommand
import com.example.estdelivery.application.port.out.CreateCouponStatePort
import com.example.estdelivery.application.port.out.LoadCouponStatePort
import com.example.estdelivery.application.port.out.LoadShopOwnerStatePort
import com.example.estdelivery.application.port.out.UpdateShopOwnerStatePort
import com.example.estdelivery.application.port.out.state.CouponState
import com.example.estdelivery.application.port.out.state.ShopOwnerState
import com.example.estdelivery.domain.coupon.Coupon

class HandoutCouponService(
private val loadShopOwnerStatePort: LoadShopOwnerStatePort,
private val loadCouponStatePort: LoadCouponStatePort,
private val updateShopOwnerStatePort: UpdateShopOwnerStatePort,
private val createCouponStatePort: CreateCouponStatePort,
) : HandoutCouponUseCase {
/**
* 1. 가게 주인 정보를 조회한다.
* 2. 가게 정보를 조회한다.
* 3. 단골들 중 동일한 쿠폰을 아직 받지 않은 사용자에게 쿠폰을 뿌린다.
*
* @param handoutCouponCommand 나눠줄 쿠폰 정보와 가게 주인 정보
*/
override fun handoutCoupon(handoutCouponCommand: HandoutCouponCommand) {
val shopOwner = loadShopOwnerStatePort.findById(handoutCouponCommand.shopOwnerId).toShopOwner()
val handoutCoupon: Coupon = getHandoutCoupon(handoutCouponCommand)
shopOwner.handOutCouponToRoyalCustomersInShop(handoutCoupon)
updateShopOwnerStatePort.update(ShopOwnerState.from(shopOwner))
}

private fun getHandoutCoupon(handoutCouponCommand: HandoutCouponCommand): Coupon {
val id = handoutCouponCommand.coupon.id
if (id == null || loadCouponStatePort.exists(id).not()) {
val handoutCoupon = createCouponStatePort.create(CouponState.from(handoutCouponCommand.coupon)).toCoupon()
return handoutCoupon
}

val handoutCoupon: Coupon = loadCouponStatePort.findById(id).toCoupon()
return handoutCoupon
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.example.estdelivery.application

import com.example.estdelivery.application.port.`in`.IssuePublishedCouponUseCase
import com.example.estdelivery.application.port.`in`.command.IssuePublishedCouponCommand
import com.example.estdelivery.application.port.out.*
import com.example.estdelivery.application.port.out.state.MemberState
import com.example.estdelivery.application.port.out.state.ShopOwnerState
import com.example.estdelivery.domain.member.Member
import com.example.estdelivery.domain.shop.ShopOwner

class IssuePublishedCouponService(
private val loadMemberStatePort: LoadMemberStatePort,
private val loadCouponStatePort: LoadCouponStatePort,
private val loadShopOwnerStatePort: LoadShopOwnerStatePort,
private val updateMemberStatePort: UpdateMemberStatePort,
private val updateShopOwnerStatePort: UpdateShopOwnerStatePort,
) : IssuePublishedCouponUseCase {
/**
* 1. 회원 정보를 조회한다.
* 2. 쿠폰정보를 조회한다.
* 3. 가게가 가진 게시된 쿠폰 북에서 쿠폰을 발행한다.
* 4. 발급된 쿠폰을 사용자의 쿠폰북에 추가한다.
* 5. 가게 단골 손님으로 등록한다.
*
* @param issuePublishedCouponCommand 발행된 쿠폰을 사용자에게 발급하는 명령
*/
override fun issuePublishedCoupon(issuePublishedCouponCommand: IssuePublishedCouponCommand) {
val member = getMember(issuePublishedCouponCommand)
val shopOwner = getShopOwner(issuePublishedCouponCommand)
val coupon = getCoupon(issuePublishedCouponCommand)
member.receiveCoupon(shopOwner.issuePublishedCouponInShop(coupon))
updateMember(member)
addRoyalCustomers(shopOwner, member)
}

private fun addRoyalCustomers(
shopOwner: ShopOwner,
member: Member
) {
if (!shopOwner.showRoyalCustomersInShop().contains(member)) {
shopOwner.addRoyalCustomersInShop(member)
updateShopOwnerStatePort.update(ShopOwnerState.from(shopOwner))
}
}

private fun updateMember(member: Member) {
updateMemberStatePort.update(MemberState.from(member))
}

private fun getShopOwner(issuePublishedCouponCommand: IssuePublishedCouponCommand) =
loadShopOwnerStatePort.findByShopId(issuePublishedCouponCommand.shopId).toShopOwner()

private fun getCoupon(issuePublishedCouponCommand: IssuePublishedCouponCommand) =
loadCouponStatePort.findByCouponId(issuePublishedCouponCommand.couponId).toCoupon()

private fun getMember(issuePublishedCouponCommand: IssuePublishedCouponCommand) =
loadMemberStatePort.findById(issuePublishedCouponCommand.memberId).toMember()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.example.estdelivery.application

import com.example.estdelivery.application.port.`in`.PublishCouponUseCase
import com.example.estdelivery.application.port.`in`.command.PublishCouponCommand
import com.example.estdelivery.application.port.out.CreateCouponStatePort
import com.example.estdelivery.application.port.out.LoadShopOwnerStatePort
import com.example.estdelivery.application.port.out.UpdateShopOwnerStatePort
import com.example.estdelivery.application.port.out.state.CouponState
import com.example.estdelivery.application.port.out.state.ShopOwnerState

class PublishCouponService(
private val loadShopOwnerPort: LoadShopOwnerStatePort,
private val createCouponStatePort: CreateCouponStatePort,
private val updateShopOwnerStatePort: UpdateShopOwnerStatePort,
) : PublishCouponUseCase {
/**
* 1. 가게 주인 정보를 조회한다.
* 2. 가게 정보를 조회한다.
* 3. 쿠폰을 생성한다.
* 4. 가게에 게시된 쿠폰북에 게시한다.
*
* @param publishCouponCommand 게시할 쿠폰 정보와 가게 주인 정보
*/
override fun publishCoupon(publishCouponCommand: PublishCouponCommand) {
val shopOwner = loadShopOwnerPort.findById(publishCouponCommand.shopOwnerId).toShopOwner()
val publishedCoupon = createCouponStatePort.create(CouponState.from(publishCouponCommand.coupon)).toCoupon()
shopOwner.publishCouponInShop(publishedCoupon)
updateShopOwnerStatePort.update(ShopOwnerState.from(shopOwner))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.example.estdelivery.application

import com.example.estdelivery.application.port.`in`.UseCouponUseCase
import com.example.estdelivery.application.port.`in`.command.UseCouponCommand
import com.example.estdelivery.application.port.out.*
import com.example.estdelivery.application.port.out.state.MemberState
import com.example.estdelivery.application.port.out.state.ShopOwnerState

class UseCouponService(
private val loadMemberStatePort: LoadMemberStatePort,
private val loadCouponStatePort: LoadCouponStatePort,
private val loadShopOwnerStatePort: LoadShopOwnerStatePort,
private val updateMemberStatePort: UpdateMemberStatePort,
private val updateShopOwnerStatePort: UpdateShopOwnerStatePort,
) : UseCouponUseCase {
/**
* 1. 회원 정보를 조회한다.
* 2. 쿠폰 정보를 조회한다.
* 3. 회원이 가진 쿠폰북에서 쿠폰을 꺼낸다.
* 4. 가게주인은 사용할 쿠폰을 받는다.
*
* @param useCouponCommand 사용할 쿠폰 정보와 회원 정보
*/
override fun useCoupon(useCouponCommand: UseCouponCommand) {
val member = loadMemberStatePort.findById(useCouponCommand.memberId).toMember()
val coupon = loadCouponStatePort.findById(useCouponCommand.couponId).toCoupon()
val shopOwner = loadShopOwnerStatePort.findByShopId(useCouponCommand.shopId).toShopOwner()

member.useCoupon(coupon)
shopOwner.receiveCoupon(coupon)

updateMemberStatePort.update(MemberState.from(member))
updateShopOwnerStatePort.update(ShopOwnerState.from(shopOwner))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example.estdelivery.application.port.`in`

import com.example.estdelivery.application.port.`in`.command.HandoutCouponCommand

interface HandoutCouponUseCase {
/**
* 가게 사장은 단골들에게 쿠폰을 나눠줄 수 있다.
* @param handoutCouponCommand 나눠줄 쿠폰 정보와 가게 주인 정보
*/
fun handoutCoupon(handoutCouponCommand: HandoutCouponCommand)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example.estdelivery.application.port.`in`

import com.example.estdelivery.application.port.`in`.command.IssuePublishedCouponCommand

interface IssuePublishedCouponUseCase {
/**
* 회원은 가게에 게시된 쿠폰을 발생한다. 이미 발행된 쿠폰은 재발행 할 수 없고, 게시되지 않은 쿠폰은 발행할 수 없다.
* @param issuePublishedCouponCommand
*/
fun issuePublishedCoupon(issuePublishedCouponCommand: IssuePublishedCouponCommand)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example.estdelivery.application.port.`in`

import com.example.estdelivery.application.port.`in`.command.PublishCouponCommand

interface PublishCouponUseCase {
/**
* 가게 주인은 가게에 쿠폰을 게시한다.
* @param publishCouponCommand 게시할 쿠폰 정보와 가게 주인 정보
*/
fun publishCoupon(publishCouponCommand: PublishCouponCommand)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.example.estdelivery.application.port.`in`

import com.example.estdelivery.application.port.`in`.command.UseCouponCommand

interface UseCouponUseCase {
fun useCoupon(useCouponCommand: UseCouponCommand)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.example.estdelivery.application.port.`in`.command

import com.example.estdelivery.domain.coupon.Coupon

class HandoutCouponCommand(val shopOwnerId: Long, val shopId: Long, val coupon: Coupon)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.example.estdelivery.application.port.`in`.command

data class IssuePublishedCouponCommand(
val couponId: Long,
val memberId: Long,
val shopId: Long,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.example.estdelivery.application.port.`in`.command

import com.example.estdelivery.domain.coupon.Coupon

data class PublishCouponCommand(
val shopOwnerId: Long,
val shopId: Long,
val coupon: Coupon,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.example.estdelivery.application.port.`in`.command

class UseCouponCommand(
val memberId: Long,
val couponId: Long,
val shopId: Long
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.example.estdelivery.application.port.out

import com.example.estdelivery.application.port.out.state.CouponState

interface CreateCouponStatePort {
fun create(couponState: CouponState): CouponState
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.example.estdelivery.application.port.out

import com.example.estdelivery.application.port.out.state.CouponState

interface LoadCouponStatePort {
fun findByCouponId(couponId: Long): CouponState
fun exists(couponId: Long): Boolean
fun findById(couponId: Long): CouponState
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.example.estdelivery.application.port.out

import com.example.estdelivery.application.port.out.state.MemberState

interface LoadMemberStatePort {
fun findById(memberId: Long): MemberState
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.example.estdelivery.application.port.out

import com.example.estdelivery.application.port.out.state.ShopOwnerState

interface LoadShopOwnerStatePort {
fun findById(shopOwnerId: Long): ShopOwnerState
fun findByShopId(shopId: Long): ShopOwnerState
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.example.estdelivery.application.port.out

import com.example.estdelivery.application.port.out.state.ShopState

interface LoadShopStatePort {
fun findById(shopId: Long): ShopState
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.example.estdelivery.application.port.out

import com.example.estdelivery.application.port.out.state.MemberState

interface UpdateMemberStatePort {
fun update(memberState: MemberState)
}
Loading

0 comments on commit e80ff36

Please sign in to comment.