✍️

GraphQLスキーマ図模写 オークション編

2023/11/25に公開

twitter.comで@kawasimaさんがオークションサイトのスキーマってどんなだろうとつぶやいていたのに対して作ってみたスキーマ図が思いのほか反響があったので、改めて作ってみました。

色んなアプリケーションのスキーマ図を模写して公開してみることで誰かのGraphQLスキーマ作成の参考になればと思ったので公開してみます。

今回のアプリ オークションサイト

某オークションサイトを参考に、妄想でスキーマ図を作成しました。

スキーマ図

API対象利用者

  • ゲストユーザー
  • 落札者
  • 出品者

設計意図

  • 掲載情報(Listing)は誰でも見れる
  • 取引(Transaction)は出品者か落札者だけが見れる
  • 取引から掲載情報は一方通行
  • レビューはユーザーから絞り込みして取得する
  • 画面毎にログインユーザーを絞り込み条件として情報取得するため、ユーザーから関連する情報はQueryで実現する。
    • User Typeは作らない

SDL

SDL
scalar Datetime 

type Query {
  listings: [Listing!]
  ownListings: [Listing!]
  watchingListings: [Listing!]
  myTransactions: [Transaction!]
  transaction(transactionId: Int!): Transaction
  reviews(userId: Int!): [Review!]
  viewer: Viewer
}

"""
プロフィール
"""
type Profile {
  userId:Int!
  displayName:String!
  iconUrl:String
  reviewCount:Int
  reviewRate:Float
}

"""
配送住所
"""
type ShippingAddress {
  name:String!
  postCode:String!
  prefecture:Int!
  address1:String!
  address2:String
  phoneNumber:String
}

"""
掲載
"""
type Listing {
  ownerId:Int!
  listingId:Int!
  title:String!
  description:String
  images:[ListingImage!]!
  initialPrice:Int!
  currentPrice:Int!
  currentTax:Int!
  buyoutPrice:Int
  startAt:Datetime!
  endAt:Datetime!
  status:ListingStatus!
  shippingMethod:ShippingMethod!
  paymentMethod:PaymentMethod!

  """
  返品可能
  """
  canReturn:Boolean!

  """
  自動延長
  """
  isAutoExtention:Boolean!

  """
  トップカテゴリーからのカテゴリーリスト
  """
  categoriesFromTop:[Category!]!
  ownerProfile:Profile!
  currentBidding:Bidding
  closingBidding:Bidding
  biddings:[Bidding!]!
  biddingCount:Int!
  watchCount:Int

  """
  ウォッチしているか
  """
  isWatching:Boolean!
}
"""
入札
"""
type Bidding {
  bidderId:Int!
  price:Int!
  biddingAt:Datetime!
  status:BiddingStatus!
  bidderProfile:Profile!
}
"""
取引
"""
type Transaction {
  transactionId:Int!
  status:TransactionStatus!
  myRole:TransactionRole!
  listing:Listing!
  price:Int!
  shippingMethod:PaymentMethod!
  shippingAddress:ShippingAddress!
  shippingAmount:Int!
  totalAmount:Int!
  ownerProfile:Profile!
  bidderProfile:Profile!
  messages:[Message!]
  ownerReview:Review
  bidderReview:Review
}

"""
メッセージ
"""
type Message {
  messageId:Int!
  content:String
  sentAt:Datetime!
  sentBy:TransactionRole!
}

"""
カテゴリー
"""
type Category {
  categoryId:Int!
  name:String!
  childrenCategories:[Category]
}

"""
商品画像
"""
type ListingImage {
  url:String!
  alt:String
}

"""
レビュー
"""
type Review {
  reviewId:Int!
  rate:Int!
  comment:String
  transactionRole:TransactionRole!
  createdAt:Datetime!
  listing:Listing!
}

"""
閲覧者
"""
type Viewer {
  userId:Int!
  displayName:String!
  iconUrl:String
}

enum ListingStatus {
  OPEN
  CLOSED
  CANCELED
}

enum TransactionRole {
  BIDDER
  OWNER
}

enum TransactionStatus {
  ONGOING
  FINISHED
  CANCELED
}

enum ListingCondition {
  NEW
  OLD
}

enum ShippingMethod {
  POST
  SELF
}

enum PaymentMethod {
  CREDIT_CARD
  BANK
}


enum BiddingStatus {
  ELECTED
  UNELECTED
  CANCELED
}

graphql-voyagerにSDLを貼ると閲覧しやすいです。

オペレーション

商品一覧
query ListingList {
  listings {
    listingId
    ownerId
    title
    description
    currentPrice
    biddingCount
    watchiCount
    isWatching
    startAt
    endAt
  }
}
商品詳細
query ListingDetail {
  listings {
    ownerId
    listingId
    title
    description
    images
    initialPrice
    currentPrice
    currentTax
    buyoutPrice
    startAt
    endAt
    status
    shippingMethod
    paymentMethod
    canReturn
    isAutoExtention
    categoriesFromTop {
      categoryId
      name
    }
    ownerProfile {
      userId
      displayName
      iconUrl
      reviewCount
      reviewRate
    }
    currentBidding {
      bidderId
      price
      biddingAt
      bidderProfile {
        userId
        displayName
        iconUrl
        reviewCount
        reviewRate
      }
    }
    biddingCount
    watchCount
    isWatching
  }
}
取引一覧
query MyTransactions {
  myTransactions {
    transactionId
    status
    myRole
    listing {
      listingId
      title
      images {
        url
      }
    }
    closingBidding {
      bidderId
      price
      biddingAt
      status
    }
    price
    ownerProfile {
      userId
      displayName
      iconUrl
    }
    bidderProfile {
      userId
      displayName
      iconUrl
    }
    ownerReview {
      reviewId
      rate
      comment
      createdAt
    }
    bidderReview {
      reviewId
      rate
      comment
      createdAt
    }
  }
}
取引詳細
query Transaction($transactionId: Int!) {
  transaction(transactionId: $transactionId) {
    transactionId
    status
    myRole
    listing {
      listingId
      title
      images {
        url
      }
    }
    closingBidding {
      bidderId
      price
      biddingAt
      status
    }
    price
    ownerProfile {
      userId
      displayName
      iconUrl
    }
    bidderProfile {
      userId
      displayName
      iconUrl
    }
    messages {
      messageId
      content
      sentAt
      sentBy
    }
    ownerReview {
      reviewId
      rate
      comment
      createdAt
    }
    bidderReview {
      reviewId
      rate
      comment
      createdAt
    }
  }
}
レビュー一覧
query Reviews($userId: Int!) {
  reviews(userId: $userId) {
    reviewId
    rate
    comment
    transactionRole
    createdAt
    listing {
      listingId
      title
      images {
        url
      }
    }
  }
}
ログインユーザー情報
query Viewer {
  userId
  displayName
  iconUrl
}

雑感

オークションサイトの特性

  • 特徴: Read Heavy
  • ユーザーの滞在時間: 短い
  • ユーザーによる操作: 少ない
  • クライアントの状態の変化: 少ない
  • データの種類: 少ない
  • データの関連性: 少ない
  • 未来のAPIの拡張必要性: 低い
  • 未来のAPIの変化可能性: 低い

オークションサイトの特性上、どれもGraphQLを必要とする特性がないためREST APIで十分問題なさそう。GraphQL APIで提供する場合でも Persisted Queryを利用してGETリクエストでHTTPのキャッシュが効くように運用する。

おわりに

フィードバックや質問があればディスカッションしましょう。みなさまのGraphQLの理解に役立てれば幸いです。需要があればまた別のアプリケーションでやります。

合同会社春秋テックブログ

Discussion