[STK5-02] account 도메인 Entity·DomainService + 토스 계좌 Gateway 구현

작업 내용 (설계 의도)

변경 사항

account 도메인 패키지 전체를 신규 구성한다. STK5-03(order/holding 도메인)과 독립적인 패키지이므로 병렬 진행 가능하다.

구성 범위:

  • domain: Account Entity, AccountType·AccountDomainService + AccountGateway·AccountRepository interface
  • infrastructure: TossAccountGateway (계좌 목록·매수가능금액·매도가능수량·수수료 Toss API 호출) + AccountRepositoryImpl + AccountJpaRepository + AccountEntity
  • application: AccountResponse

OAuth Authorization Code Flow로 발급된 사용자 access_token은 TossUserTokenStore에서 조회한다. 계좌 API 호출 시 Authorization: Bearer {accessToken} 헤더 사용.

다이어그램

클래스 의존

flowchart LR
    subgraph Domain["domain"]
        AccountDS[AccountDomainService]
        AGW[AccountGateway]
        AR[AccountRepository]
        Account[Account Entity]
    end
    subgraph Infra["infrastructure"]
        TAG[TossAccountGateway]
        ARI[AccountRepositoryImpl]
        AJR[AccountJpaRepository]
        AE[AccountEntity]
    end
    AccountDS --> AGW
    AccountDS --> AR
    AccountDS --> Account
    TAG -.->|implements| AGW
    ARI -.->|implements| AR
    ARI --> AJR
    AJR --> AE

처리 흐름 (계좌 목록 조회·upsert)

sequenceDiagram
    participant DS as AccountDomainService
    participant G as AccountGateway
    participant R as AccountRepository
    DS->>G: fetchAccounts()
    G-->>DS: List<AccountData>
    DS->>R: upsertAll(accounts)
    R-->>DS: List<Account>
    Note over DS: refreshed_at 갱신 포함
    alt Toss API 오류
        G-->>DS: 예외 (TossApiException)
        DS-->>DS: 예외 전파 (upsert 미실행)
    end

처리 흐름 (활성 계좌 선택 전환)

sequenceDiagram
    participant UC as SelectAccountUseCase
    participant DS as AccountDomainService
    participant R as AccountRepository
    UC->>DS: selectAccount(accountNumber)
    DS->>R: findSelected()
    R-->>DS: Account(selected=true) or null
    alt 기존 선택 계좌 존재
        DS->>DS: previousAccount.deselect()
        DS->>R: save(previousAccount)
    end
    DS->>R: findBy(accountNumber)
    R-->>DS: Account
    DS->>DS: account.select()
    DS->>R: save(account)
    R-->>DS: Account(selected=true)
    DS-->>UC: Account

테스트 케이스

  • Account.select() 호출 시 selected = true로 전이된다.
  • 이미 선택된 계좌에 select()를 다시 호출해도 예외 없이 멱등하게 처리된다.
  • Account.deselect() 호출 시 selected = false로 전이된다.
  • TossAccountGateway가 Toss API 호출 시 Authorization: Bearer {accessToken} 헤더를 포함한다.
  • Toss API가 오류 응답을 반환할 때 AccountDomainService는 upsert를 실행하지 않고 예외를 전파한다.
  • 계좌 목록 upsert 후 동일 account_number로 재조회하면 refreshed_at이 갱신된 값으로 반환된다.