ホームページ >Java >&#&チュートリアル >インターネットクレジット購入システムの設計

インターネットクレジット購入システムの設計

Susan Sarandon
Susan Sarandonオリジナル
2025-01-15 18:05:45696ブラウズ

私が直面した技術面接の 1 つで、ユーザーがサードパーティ プロバイダーからインターネット クレジットを購入できる電子商取引システムを設計するように求められました。

私は自信を持って、利用可能なパッケージを表示し、ユーザーにパッケージを選択させ、外部ゲートウェイ経由で支払いを処理し、プロバイダーと対話してクレジットを提供するという単純な解決策を提案しました。しかし、ユーザーが支払いを完了した後にプロバイダーの在庫がなくなるなどの障害シナリオについて尋ねられたとき、私の設計にはそのような問題を効果的に処理するための回復力が欠けていることに気づきました。

数週間前、私はフラッシュセールシステムと在庫予約パターン、特に在庫予約戦略に焦点を当てた調査を実施しました。フラッシュセールでは需要が高く在庫が限られていることが多いため、システムの安定性を維持し、顧客の期待を管理するための高度なメカニズムが必要です。私が発見したコンセプトの 1 つは、ピーク時の過剰販売を防ぐための一時的な在庫予約です。

この調査を見て、私は面接での経験を思い出しました。これらの在庫予約戦略を適用すれば、最初の設計の欠点に対処できる可能性があることに気づきました。チェックアウトプロセス中に在庫の一時的な保留を組み込むことで、システムはプロバイダーの在庫が枯渇するシナリオを効果的に処理できます。

この研究文書では、私の研究から得られた洞察を共有し、インターネット クレジット購入システムを設計するための洗練されたアプローチを提案することを目的としています。在庫予約戦略を統合することで、シームレスなエクスペリエンスを提供しながら、さまざまな障害シナリオに対応できる堅牢かつユーザーフレンドリーなプラットフォームを構築できます。

1. 主要な設計上の考慮事項

インターネット クレジット購入システムを設計する場合、シームレスで安全で楽しいユーザー エクスペリエンスを確保するために考慮すべき重要な要素がいくつかあります。それらを詳しく見てみましょう:

1.1 クォータ管理

  • リアルタイムのクォータ検証: ユーザーが誤って利用できないオプションを選択しないように、システムはインターネット クレジット パッケージの在庫があるかどうかを即座に確認します。
  • 一時的な割り当て予約: 選択したパッケージを短期間保持するメカニズムを追加し、ユーザーが商品を紛失するリスクなしに購入を完了するのに十分な時間を与えます。
  • クォータの競合の処理: 複数のユーザーが同時に同じパッケージを購入しようとする状況を管理し、公平な割り当てを確保するための戦略を策定します。
  • パッケージ情報のキャッシュ管理: ユーザーが常に正確な詳細と可用性を確認できるように、キャッシュ データを正確かつ最新に保ちます。

1.2 支払い処理

  • 安全な支払い処理: 取引中にユーザーの支払い詳細を保護するための強力なセキュリティ対策を実装します。
  • 支払い保護のためのエスクロー システム: エスクロー サービスを使用してクレジットが配信されるまで資金を保持し、購入者とプロバイダーの両方を安全に保ちます。
  • 支払いゲートウェイの統合: システムが信頼できる支払いゲートウェイにスムーズに接続して、手間のかからない取引を確保します。
  • 返金メカニズム: 支払いが失敗した場合やキャンセルされた場合に返金を行うための、明確で使いやすいプロセスを作成します。

1.3 プロバイダーの統合

  • システムの可用性: 信頼できるシステムを備えたプロバイダーと提携して、中断なく購入が処理されるようにします。
  • API の信頼性: 安定した、十分に文書化された API を提供するプロバイダーと連携して、シームレスな統合を実現します。
  • サービスアクティベーションの検証: 購入したクレジットが適切かつ迅速にアクティベートされていることを確認するためのチェックが含まれます。
  • エラー処理と再試行: 失敗したプロセスに対する再試行メカニズムを備えた、エラーを迅速に捕捉して解決するためのプロトコルを実装します。

1.4 取引の安全性

  • マネー フロー制御: 取引が正常に完了した場合にのみ資金が解放されるようにします。
  • トランザクションの一貫性: エラーを防ぐために、すべてのトランザクションの正確かつ一貫した記録を保持します。
  • ロールバック メカニズム: 何か問題が発生した場合にトランザクションを元に戻し、ユーザーとシステムの両方を保護する計画を立ててください。
  • 監査証跡: 詳細なログを維持して、問題を効果的に監視し、トラブルシューティングするのに役立ちます。

1.5 ユーザーエクスペリエンス

  • 明確なエラー メッセージ: 発生した問題を解決するための、わかりやすく有益なエラー メッセージをユーザーに提供します。
  • 取引ステータスの可視性: ユーザーがリアルタイムで購入ステータスを簡単に追跡できるようにし、透明性を高めます。
  • クイックパッケージロード: 利用可能なパッケージを迅速にロードするようにシステムを最適化し、ユーザーの待ち時間を短縮します。
  • リアルタイム更新: トランザクションや利用可能なパッケージの変更や更新をユーザーに迅速に通知します。

これらの考慮事項を考慮することで、効率的、安全、そしてユーザーフレンドリーなインターネット クレジット購入システムを設計でき、ユーザーの満足度と信頼が高まります。

2. システム設計とフロー

上で概説した基本的な考慮事項に基づいて、次のステップでは、これらの原則を堅牢で効果的なシステム設計に変換します。さまざまなコンポーネント間の相互作用を慎重にマッピングすることで、システムが機能要件を満たすだけでなく、信頼性と拡張性を維持しながらシームレスなユーザー エクスペリエンスを提供できるようになります。

このセクションでは、システムのアーキテクチャとフローを詳しく説明し、クォータ管理、支払い処理、サービスのアクティベーションなどのコア機能がどのように連携して実装されるかを示します。目的は、それぞれの設計上の選択が、潜在的な課題への対処と信頼できる e コマース クレジット購入プラットフォームの提供にどのように貢献するかを強調することです。

まず、システム フローの概要をフローチャートで視覚化し、ユーザーが最初から最後までシステムとどのように対話するかを説明します。

2.1 フローチャート図

Designing an Internet Credit Purchase System

わかりやすくするために、システムのフローは 6 つのフェーズに分割されています。

パッケージ選択フェーズ

  • まず、ユーザーはパッケージ選択ページにアクセスし、アプリがキャッシュからパッケージ データを取得します。このデータには、使用可能なパッケージとそのキャッシュされたクォータ情報が含まれており、ユーザーに表示されます。
  • ユーザーはパッケージを選択し、「購入」をクリックします。
  • そのパッケージの割り当てが利用できない場合、アプリは「利用できません」というメッセージを表示し、ユーザーを選択ページに戻します。それ以外の場合、システムはユーザーのクォータを一時的に予約します。

購入の開始

  • 次に、システムは選択したパッケージのクォータを予約しようとします。
  • 予約が失敗した場合、ユーザーにはエラー メッセージが表示され、選択ページにリダイレクトされます。
  • 予約が成功すると、ユーザーは支払いページに進みます。
支払い段階
  • この段階で、ユーザーは支払いプロセスを開始し、サードパーティの支払いゲートウェイにリダイレクトされます。
  • アプリは、支払いステータスを確認するために、支払いゲートウェイからの応答 (コールバック) を待ちます。
支払い処理
  • アプリは支払いゲートウェイのコールバックをチェックして支払いを検証します。
    • 無効なコールバックの場合、システムは問題をログに記録し、それ以降のステップを停止します。
    • 有効なコールバックの場合:
      • 支払いが失敗した場合: システムは予約されたクォータを解放し、問題についてユーザーに通知します。
      • 支払いが成功した場合: システムは支払いを確認し、資金をエスクローに保持して、新しい注文を作成します。
サービスのアクティベーション
  • 支払いが成功すると、システムはプロバイダーにサービスをアクティブ化するように要求します。
    • アクティベーションが失敗した場合: エスクロー資金は顧客に返金され、失敗について通知されます。
    • アクティベーションが成功した場合: システムはアクティベーションを検証します。
      • 検証が失敗した場合、顧客は返金を受けます。
      • 検証が成功すると、エスクロー資金がプロバイダーに解放され、顧客は通知を受け取ります。
バックグラウンドプロセス
  • 定期的なキャッシュ更新: パッケージ データ キャッシュは定期的に更新されます。
  • リアルタイムのクォータ更新: クォータの変更は WebSocket 接続経由で通信されます。

このフローは、ユーザーにスムーズで信頼性の高いエクスペリエンスを保証すると同時に、リソースと潜在的なエラーを効果的に管理します。

2.2 シーケンス図

以下のシーケンス図は、さまざまな役割とコンポーネント間の相互作用を説明するのに役立ちます。

Designing an Internet Credit Purchase System

わかりやすくするために、システムのフローは 6 つのフェーズに分割されています。

パッケージ選択フェーズ

  • 顧客はまず、パッケージ選択ページにアクセスします。
  • フロントエンドはキャッシュからパッケージ データを取得し、キャッシュされたクォータ情報とともに使用可能なすべてのパッケージを顧客に表示します。

購入の開始

  • 顧客がパッケージを選択して「購入」をクリックすると、フロントエンドは購入リクエストをバックエンドに送信します。
  • バックエンドはプロバイダーに問い合わせて、選択したパッケージのクォータがまだ利用可能かどうかをリアルタイムで確認します。
  • クォータが利用可能な場合、バックエンドはプロバイダーを使用してクォータを 15 分間一時的に予約します。
  • バックエンドは予約確認をフロントエンドに送信し、顧客は支払いページにリダイレクトされます。

支払い段階

  • 顧客は支払いページに進み、支払いの詳細を送信します。
  • フロントエンドはこの情報をバックエンドに送信し、バックエンドは支払いゲートウェイとの支払いセッションを初期化します。
  • 支払いセッションの準備が完了すると、バックエンドはセッションの詳細をフロントエンドと共有します。
  • フロントエンドは、支払いを完了するために顧客を支払いゲートウェイにリダイレクトします。

支払い処理

  • 顧客は支払いゲートウェイで支払い情報を入力し、支払いプロセスを完了します。
  • 支払いゲートウェイは、コールバックを通じて支払いステータスをバックエンドに通知します。
    • 支払いが成功した場合:
      • バックエンドは、支払いゲートウェイを使用して支払いステータスを確認します。
      • 支払いはエスクローで保持され、バックエンドはエスクローの保持を確認します。
    • 支払いが失敗した場合:
      • バックエンドは、プロバイダーの予約済みクォータを解放し、支払いステータスを更新して失敗を反映します。
    • 予想時間内にコールバックが受信されない場合:
      • バックエンドは定期的に支払いゲートウェイをポーリングして支払いステータスを確認します。支払いが失敗するかタイムアウトになると、バックエンドは予約された割り当てを解放します。

サービスのアクティベーション

  • 支払いが成功すると、バックエンドはプロバイダーにサービスをアクティブ化するよう要求します。
  • プロバイダーはアクティベーション ステータスで応答し、バックエンドはアクティベーションを検証します。
    • アクティベーションが成功した場合:
      • バックエンドはエスクローからプロバイダーへの支払いを解放します。
      • 成功通知が顧客に送信され、サービスの準備ができたことを知らせます。
    • アクティベーションが失敗した場合:
      • バックエンドはエスクローされた資金を顧客に返金します。
      • お客様に問題について通知するために、障害通知が送信されます。

バックグラウンドプロセス

  • プロバイダーは、WebSocket 接続を介して、パッケージ クォータに関するリアルタイムの更新をバックエンドに送信します。
  • これらの更新により、キャッシュが常に最新であることが保証されるため、顧客は閲覧時に最も正確なパッケージの在庫状況を確認できます。

3. 技術的な実装

システムのフローと相互作用の概要を説明したので、次はそれらすべてがコードでどのように組み合わされるかを詳しく見ていきます。このセクションでは、実装を段階的に説明し、注文の管理からプロバイダーや支払いシステムとのやり取りまで、すべてを処理する機能部分に設計がどのように変換されるかを示します。

// Domain Models
@Getter @Setter
@Entity
public class Package {
    @Id
    private String id;
    private String name;
    private BigDecimal price;
    private BigDecimal providerCost;
    private String description;
    private boolean active;
}

@Getter @Setter
@Entity
public class Order {
    @Id
    private String id;
    private String customerId;
    private String packageId;
    private String reservationId;
    private String paymentId;
    private String escrowId;
    private OrderStatus status;
    private BigDecimal amount;
    private BigDecimal providerCost;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}

@Getter @Setter
@Entity
public class QuotaReservation {
    @Id
    private String id;
    private String packageId;
    private LocalDateTime expiresAt;
    private ReservationStatus status;
}

// Enums
public enum OrderStatus {
    CREATED, RESERVED, PAYMENT_PENDING, PAYMENT_COMPLETED, 
    IN_ESCROW, ACTIVATING, ACTIVATION_FAILED, COMPLETED, REFUNDED
}

public enum ReservationStatus {
    ACTIVE, EXPIRED, USED, CANCELLED
}

これらのクラスの機能は次のとおりです:

  • パッケージ: ここで、ユーザーが購入できるインターネット クレジット パッケージを定義します。パッケージ ID、名前、価格、プロバイダーのコスト、説明、パッケージがアクティブかどうかなどの詳細を追跡します。

  • 注文: これはユーザーの購入の記録と考えてください。これには、注文 ID、顧客 ID、選択したパッケージ ID などの情報と、予約 ID、支払い ID、エスクロー ID、注文ステータス、支払い金額、プロバイダー費用、タイムスタンプなどの関連詳細が含まれます。

  • QuotaRegistration: パッケージ クォータの一時的な予約を処理します。予約 ID、関連付けられているパッケージ、有効期限が切れたとき、および現在のステータス (アクティブまたは期限切れなど) を記録します。

  • OrderStatus Enum: この列挙型は、CREATED、RESERVED から PAYMENT_PENDING、COMPLETED、さらには REFUNDED まで、注文が通過できるすべての段階をマップします。

  • RegistrationStatus Enum: 同様に、この列挙型はクォータ予約の状態 (アクティブ、期限切れ、使用中、またはキャンセル済み) を追跡します。

これらのクラスと列挙型は一緒になって、システム内のパッケージ、注文、クォータ予約を管理するためのバックボーンを構築します。これは、電子商取引機能を効果的に処理するための、シンプルでありながら構造化されたアプローチです。

// Request/Response DTOs
@Getter @Setter
public class OrderRequest {
    private String customerId;
    private String packageId;
    private BigDecimal amount;
}

@Getter @Setter
public class PaymentCallback {
    private String orderId;
    private String paymentId;
    private String status;
    private BigDecimal amount;
    private LocalDateTime timestamp;
}

@Getter @Setter
public class QuotaResponse {
    private String packageId;
    private boolean available;
    private Integer remainingQuota;
    private LocalDateTime timestamp;
}

@Getter @Setter
public class ReservationResponse {
    private String id;
    private String packageId;
    private LocalDateTime expiresAt;
    private ReservationStatus status;
}

@Getter @Setter
public class ActivationResponse {
    private String orderId;
    private boolean success;
    private String activationId;
    private String errorCode;
    private String errorMessage;
}

@Getter @Setter
public class VerificationResponse {
    private String orderId;
    private String activationId;
    private boolean success;
    private String status;
    private LocalDateTime activatedAt;
}

@Getter @Setter
public class PaymentRequest {
    private String orderId;
    private BigDecimal amount;
    private String currency;
    private String customerId;
    private String returnUrl;
    private String callbackUrl;
}

@Getter @Setter
public class PaymentSession {
    private String sessionId;
    private String paymentUrl;
    private LocalDateTime expiresAt;
    private String status;
}

@Getter @Setter
public class EscrowResponse {
    private String id;
    private String paymentId;
    private BigDecimal amount;
    private String status;
    private LocalDateTime createdAt;
}

詳しく見てみましょう:

  • OrderRequest: これはすべて、新しい注文を作成するために必要なデータに関するものです。これには、顧客 ID、購入したいパッケージ、支払う合計金額が含まれます。

  • PaymentCallback: これは、支払いゲートウェイからの通知と考えてください。支払いを試行すると、注文 ID、支払い ID、ステータス (成功または失敗)、支払金額、支払いがいつ行われたかなどの詳細が表示されます。

  • QuotaResponse: これは、可用性の確認に関するものです。パッケージが利用可能かどうか、割り当てがどれだけ残っているか、情報が最後に更新されたのはいつかを示します。

  • RegistrationResponse: パッケージが予約されると、予約 ID、関連するパッケージ、予約の有効期限が切れる時期、および現在のステータス (アクティブまたは期限切れなど) のすべての詳細が表示されます。 .

  • ActivationResponse: これは、サービスのアクティブ化がどのように行われたかを示します。成功または失敗すると、アクティベーション ID と、問題が発生した場合のエラーの詳細が表示されます。

  • VerificationResponse: アクティベーション後、すべてがスムーズに進んだかどうかを検証します。これには、注文 ID、アクティベーション ID、成功ステータス、アクティベートされた時間が含まれます。

  • PaymentRequest: 支払いプロセスを開始する前に、この DTO は注文 ID、支払われる金額、通貨、顧客 ID、コールバック URL などの必要な詳細を収集します。

  • PaymentSession: これは、支払いプロセスの開始時に作成されるものです。これには、セッション ID、支払い URL (ユーザーが支払いに行く場所)、有効期限が切れる時期、およびセッション ステータスが含まれます。

  • EscrowResponse: 資金がエスクローに保持されている場合、エスクロー ID、支払い ID、保持金額、ステータス、作成日など、資金に関するすべてがわかります。

これらのクラスはすべて、送信されるリクエストや返される応答など、システムのさまざまな部分間の通信の構成要素を定義します。全員 (そしてすべて) が同じ認識を持っていることを保証します。

// Domain Models
@Getter @Setter
@Entity
public class Package {
    @Id
    private String id;
    private String name;
    private BigDecimal price;
    private BigDecimal providerCost;
    private String description;
    private boolean active;
}

@Getter @Setter
@Entity
public class Order {
    @Id
    private String id;
    private String customerId;
    private String packageId;
    private String reservationId;
    private String paymentId;
    private String escrowId;
    private OrderStatus status;
    private BigDecimal amount;
    private BigDecimal providerCost;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}

@Getter @Setter
@Entity
public class QuotaReservation {
    @Id
    private String id;
    private String packageId;
    private LocalDateTime expiresAt;
    private ReservationStatus status;
}

// Enums
public enum OrderStatus {
    CREATED, RESERVED, PAYMENT_PENDING, PAYMENT_COMPLETED, 
    IN_ESCROW, ACTIVATING, ACTIVATION_FAILED, COMPLETED, REFUNDED
}

public enum ReservationStatus {
    ACTIVE, EXPIRED, USED, CANCELLED
}
キャッシュサービス
1. 目的:

このサービスは、パッケージ データを保存するローカル キャッシュを処理します。目標は、システムを高速化し、プロバイダーの API への不必要な呼び出しを減らすことです。

2. 主な特徴:
  • updateCache(): このメソッドは、プロバイダーからすべてのパッケージ データをフェッチすることにより、5 分ごとにローカル キャッシュを更新します。これにより、キャッシュが最新の状態に保たれます。
  • getPackage(): このメソッドは、ID を使用してキャッシュからパッケージ情報を取得します。
  • updatePackageQuota(): クォータの詳細が変更されると、このメソッドは特定のパッケージの新しい情報でキャッシュを更新します。
プロバイダーの統合
1. 目的:

このサービスは、プロバイダーの API との通信を処理します。クォータの確認、パッケージの予約、サービスのアクティブ化、それらのアクティブ化の検証などのタスクを管理します。

2. 主な特徴:
  • checkQuota(): このメソッドは、プロバイダーの API を呼び出して、パッケージに使用可能な十分なクォータがあるかどうかを確認します。
  • reserveQuota(): プロバイダーにリクエストを送信することで、顧客用のパッケージのクォータを予約します。
  • activateService(): 注文のサービスをアクティブ化するとき、このメソッドはプロバイダーへのリクエストを処理します。
  • verifyActivation(): アクティブ化後、このメソッドはすべてが成功したかどうかを確認します。
  • getAllPackages(): このメソッドは、プロバイダーから利用可能なすべてのパッケージを取得します。これは、キャッシュを更新したり、ユーザーにパッケージ オプションを表示したりするのに役立ちます。
3. 再試行メカニズム:

このサービスは、一時的な問題が発生した場合に、RetryTemplate を使用してプロバイダーの API へのリクエストを自動的に再試行します。これにより、軽微な問題が発生した場合でも、システムの信頼性と復元力が確保されます。

これらの機能を組み合わせることで、このコードは、システムがプロバイダーの API とのスムーズで信頼性の高い通信を維持しながら、パッケージ データを効率的に管理できるようにします。

// Domain Models
@Getter @Setter
@Entity
public class Package {
    @Id
    private String id;
    private String name;
    private BigDecimal price;
    private BigDecimal providerCost;
    private String description;
    private boolean active;
}

@Getter @Setter
@Entity
public class Order {
    @Id
    private String id;
    private String customerId;
    private String packageId;
    private String reservationId;
    private String paymentId;
    private String escrowId;
    private OrderStatus status;
    private BigDecimal amount;
    private BigDecimal providerCost;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}

@Getter @Setter
@Entity
public class QuotaReservation {
    @Id
    private String id;
    private String packageId;
    private LocalDateTime expiresAt;
    private ReservationStatus status;
}

// Enums
public enum OrderStatus {
    CREATED, RESERVED, PAYMENT_PENDING, PAYMENT_COMPLETED, 
    IN_ESCROW, ACTIVATING, ACTIVATION_FAILED, COMPLETED, REFUNDED
}

public enum ReservationStatus {
    ACTIVE, EXPIRED, USED, CANCELLED
}
ペイメントゲートウェイの統合

このクラスは、システムが支払いゲートウェイとやり取りして金融取引をスムーズかつ安全に処理する方法を管理する上で重要な役割を果たします。

1.initializePayment(PaymentRequestリクエスト):
  • これは支払いプロセスの開始であると考えてください。すべての支払い詳細を含むリクエストを支払いゲートウェイに送信します。
  • 支払い URL やセッション ステータスなどの情報を含む PaymentSession オブジェクトを返します。
2.holdInEscrow(文字列支払いID):
  • この方法では、指定された支払い ID を使用してエスクロー アカウントでの支払いが保護されます。
  • エスクローされた資金に関するすべての詳細を含む EscrowResponse オブジェクトを提供します。
3. releaseToProvider(String escrowId):
  • サービスが正常にアクティブ化された後、このメソッドは資金をエスクローからサービスプロバイダーに解放します。
  • エスクロー ID は、正しい資金を識別して解放するために使用されます。
4.返金ToCustomer(文字列エスクローID):
  • サービスのアクティブ化が失敗するなど、何か問題が発生した場合、この方法ではエスクローされた資金が顧客に返金されます。
  • エスクロー ID を使用して返金を処理します。
主な特徴:
  • このクラスは WebClient を使用して HTTP リクエストを支払いゲートウェイの REST API に送信し、シームレスな統合を保証します。
  • 支払いの開始、エスクローの管理、返金の処理などの重要な操作を処理します。
  • すべてのメソッドは同期呼び出し (.block() 経由) を使用して、次に進む前に操作が完了していることを確認し、信頼性を確保します。

このクラスは、システム内で安全かつ効率的な金融取引を管理する上で、パズルの重要なピースです。

// Domain Models
@Getter @Setter
@Entity
public class Package {
    @Id
    private String id;
    private String name;
    private BigDecimal price;
    private BigDecimal providerCost;
    private String description;
    private boolean active;
}

@Getter @Setter
@Entity
public class Order {
    @Id
    private String id;
    private String customerId;
    private String packageId;
    private String reservationId;
    private String paymentId;
    private String escrowId;
    private OrderStatus status;
    private BigDecimal amount;
    private BigDecimal providerCost;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}

@Getter @Setter
@Entity
public class QuotaReservation {
    @Id
    private String id;
    private String packageId;
    private LocalDateTime expiresAt;
    private ReservationStatus status;
}

// Enums
public enum OrderStatus {
    CREATED, RESERVED, PAYMENT_PENDING, PAYMENT_COMPLETED, 
    IN_ESCROW, ACTIVATING, ACTIVATION_FAILED, COMPLETED, REFUNDED
}

public enum ReservationStatus {
    ACTIVE, EXPIRED, USED, CANCELLED
}
通知 DTO
1.電子メール通知:
  • これは、電子メール通知を送信するための青写真と考えてください。これには次のものが含まれます。
    • 受信者の電子メール (宛先)。
    • メールの件名。
    • 形式を決定するテンプレート ID。
    • コンテンツをパーソナライズするための動的データ (templateData)。
2.SMS通知:
  • 電子メール通知に似ていますが、SMS 用に調整されています。これには次のものが含まれます。
    • 受信者の電話番号 (phoneNumber)。
    • メッセージ形式のテンプレート ID。
    • パーソナライズ用の動的データ (templateData)。
通知サービス

このサービスは、注文ステータスに関してユーザーに送信されるすべての通知を処理します。仕組みは次のとおりです:

1.sendSuccessNotification(注文注文):
  • このメソッドは成功通知の送信を処理します。それは以下を使用します:
    • buildSuccessEmail を使用して電子メール通知を作成します。
    • buildSuccessSms を使用して SMS 通知を作成します。
    • QuotaWebSocketHandler を使用して、WebSocket 経由でリアルタイム更新も送信します。
2.sendFailureNotification(注文順序):
  • これは失敗通知を処理します。それは以下を使用します:
    • 電子メール メッセージの場合は buildFailureEmail。
    • SMS メッセージの buildFailureSms。
    • 成功通知と同様に、WebSocket の更新も送信します。
3. ヘルパーメソッド:
  • buildSuccessEmail および buildFailureEmail: これらのメソッドは、注文が成功したか失敗したかに基づいて電子メール通知を作成します。テンプレートと注文の詳細を使用します。
  • buildSuccessSms と buildFailureSms: これらは同じことを行いますが、SMS 通知が対象です。
追加機能:
  • WebSocket 更新: QuotaWebSocketHandler を使用して、フロントエンドをリアルタイムで更新し続けます。
  • エラー ログ: 何か問題が発生した場合、デバッグのためにエラーをログに記録します。

このサービスにより、ユーザーは電子メール、SMS、またはリアルタイム更新のいずれを通じてでも、注文に関する情報を常に把握できるようになります。

// Domain Models
@Getter @Setter
@Entity
public class Package {
    @Id
    private String id;
    private String name;
    private BigDecimal price;
    private BigDecimal providerCost;
    private String description;
    private boolean active;
}

@Getter @Setter
@Entity
public class Order {
    @Id
    private String id;
    private String customerId;
    private String packageId;
    private String reservationId;
    private String paymentId;
    private String escrowId;
    private OrderStatus status;
    private BigDecimal amount;
    private BigDecimal providerCost;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}

@Getter @Setter
@Entity
public class QuotaReservation {
    @Id
    private String id;
    private String packageId;
    private LocalDateTime expiresAt;
    private ReservationStatus status;
}

// Enums
public enum OrderStatus {
    CREATED, RESERVED, PAYMENT_PENDING, PAYMENT_COMPLETED, 
    IN_ESCROW, ACTIVATING, ACTIVATION_FAILED, COMPLETED, REFUNDED
}

public enum ReservationStatus {
    ACTIVE, EXPIRED, USED, CANCELLED
}
QuotaUpdate クラス
  • このクラスは、クォータ更新のための単純なメッセンジャーと考えてください。これには 3 つの重要な情報が含まれています。
    • packageId: 更新されるパッケージの ID。
    • availableQuota: このパッケージに残っている割り当て量。
    • タイムスタンプ: 更新が行われたとき。
WebSocketの設定
1.WebSocketConfig:
  • WebSocket通信を可能にする設定です。
  • /ws/quota で WebSocket 接続をリッスンするハンドラー (quotaWebSocketHandler) を登録します。
  • また、allowedOrigins("*") を設定することで、任意のオリジンからの接続を許可します。
2.quotaWebSocketHandler():
  • これは、受信メッセージと接続を管理する WebSocket ハンドラー Bean を定義します。
クォータWebSocketHandler

ここで WebSocket の魔法がすべて起こります。サーバーとクライアント間のリアルタイムの更新を管理します。

1. フィールド:
  • PackageCacheService: クォータの更新が入るたびにローカル キャッシュを更新するのに役立ちます。
  • ObjectMapper: JSON ペイロードから Java オブジェクトへの変換、またはその逆の変換を処理します。
  • セッション: すべてのアクティブな WebSocket セッション (現在接続しているクライアント) を追跡します。
2. 方法:
  • 接続確立後(WebSocketSessionセッション):
    • 接続するとすぐに新しいクライアント セッションをアクティブ リストに追加します。
  • afterConnectionClosed(WebSocketSession セッション、CloseStatus ステータス):
    • 切断時にクライアント セッションを削除します。
  • handleTextMessage(WebSocketSession セッション、TextMessage メッセージ):
    • 受信メッセージを処理します。
    • 受信した JSON を QuotaUpdate オブジェクトに変換し、ローカル キャッシュを更新します。
3. sendOrderUpdate(注文注文):
  • 接続されているすべてのクライアントに注文変更に関するリアルタイムの更新情報を送信します。
  • Order オブジェクトを JSON に変換し、アクティブな WebSocket セッションにメッセージとして送信します。
  • 開いている接続のみが更新を受信するようにします。
コードの主な特徴:
  • リアルタイム更新:
    • クォータの変更や注文の更新についてクライアントに即座に通知します。
  • スレッドセーフな管理:
    • ConcurrentHashSet を使用して接続されたクライアントを処理し、複数のクライアントがアクティブな場合でも競合が発生しないようにします。
  • エラー処理:
    • メッセージの送信時に問題が発生した場合にエラーをログに記録するため、トラブルシューティングが容易になります。

この設定により、バックエンドとフロントエンド間のスムーズかつ即時の通信が保証されるため、ユーザーはクォータの利用可能性と注文ステータスに関する最新の情報を常に入手できます。

// Domain Models
@Getter @Setter
@Entity
public class Package {
    @Id
    private String id;
    private String name;
    private BigDecimal price;
    private BigDecimal providerCost;
    private String description;
    private boolean active;
}

@Getter @Setter
@Entity
public class Order {
    @Id
    private String id;
    private String customerId;
    private String packageId;
    private String reservationId;
    private String paymentId;
    private String escrowId;
    private OrderStatus status;
    private BigDecimal amount;
    private BigDecimal providerCost;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}

@Getter @Setter
@Entity
public class QuotaReservation {
    @Id
    private String id;
    private String packageId;
    private LocalDateTime expiresAt;
    private ReservationStatus status;
}

// Enums
public enum OrderStatus {
    CREATED, RESERVED, PAYMENT_PENDING, PAYMENT_COMPLETED, 
    IN_ESCROW, ACTIVATING, ACTIVATION_FAILED, COMPLETED, REFUNDED
}

public enum ReservationStatus {
    ACTIVE, EXPIRED, USED, CANCELLED
}

これらのカスタム例外クラスの内訳と、システム内の特定のエラー シナリオを処理するためにそれらがどのように使用されるかを次に示します。

QuotaNotAvailableException:

  • この例外は、ユーザーがパッケージを購入しようとしたが、そのパッケージの割り当てがすでになくなっている場合にトリガーされます。
  • 「パッケージ クォータが利用できません」という単純なデフォルト メッセージが表示されるため、開発者とユーザーの両方が問題を明確に理解できます。

OrderNotFoundException:

  • これは、提供された orderId に基づいてシステムが注文を見つけられない場合に開始されます。
  • 「注文が見つかりません: [orderId]」などの詳細なエラー メッセージが含まれているため、どの注文が欠落しているかを正確に特定するのが簡単になります。

PaymentVerificationException:

  • 金額が一致しない、または支払いステータスが不明瞭であるなど、支払いの確認に問題がある場合、この例外がスローされます。
  • カスタム メッセージを渡すことができ、支払いの問題を診断するための柔軟性とコンテキストが追加されます。

これらの例外を使用することにより、システムはクリーンかつ予測可能な方法でエラーを処理します。これらは開発者にとってデバッグの効率を高めるだけでなく、何か問題が発生したときにユーザーが明確で実用的なフィードバックを確実に受け取ることができます。

// Request/Response DTOs
@Getter @Setter
public class OrderRequest {
    private String customerId;
    private String packageId;
    private BigDecimal amount;
}

@Getter @Setter
public class PaymentCallback {
    private String orderId;
    private String paymentId;
    private String status;
    private BigDecimal amount;
    private LocalDateTime timestamp;
}

@Getter @Setter
public class QuotaResponse {
    private String packageId;
    private boolean available;
    private Integer remainingQuota;
    private LocalDateTime timestamp;
}

@Getter @Setter
public class ReservationResponse {
    private String id;
    private String packageId;
    private LocalDateTime expiresAt;
    private ReservationStatus status;
}

@Getter @Setter
public class ActivationResponse {
    private String orderId;
    private boolean success;
    private String activationId;
    private String errorCode;
    private String errorMessage;
}

@Getter @Setter
public class VerificationResponse {
    private String orderId;
    private String activationId;
    private boolean success;
    private String status;
    private LocalDateTime activatedAt;
}

@Getter @Setter
public class PaymentRequest {
    private String orderId;
    private BigDecimal amount;
    private String currency;
    private String customerId;
    private String returnUrl;
    private String callbackUrl;
}

@Getter @Setter
public class PaymentSession {
    private String sessionId;
    private String paymentUrl;
    private LocalDateTime expiresAt;
    private String status;
}

@Getter @Setter
public class EscrowResponse {
    private String id;
    private String paymentId;
    private BigDecimal amount;
    private String status;
    private LocalDateTime createdAt;
}

OrderService クラスは、注文の管理に関する面倒な作業を処理します。その仕組みを詳しく見てみましょう:

主な責任
  1. createOrder(OrderRequest リクエスト):

    • この方法は、新しい注文を作成することだけです。パッケージが利用可能かどうかを確認し、詳細を取得してクォータを予約し、注文を初期ステータス RESERVED でデータベースに保存します。
  2. processPayment(String orderId, PaymentCallback コールバック):

    • ここで支払いが処理されます。システムは支払いの詳細を確認し、注文を更新し、支払いをエスクローに預け、サービスのアクティブ化プロセスを開始します。何か問題が発生した場合、失敗を適切に管理します。
  3. verifyActivation(注文順序):

    • このメソッドは、サービスのアクティベーションがスムーズに行われたかどうかを再確認します。最大 3 回試行し、それでも失敗した場合、システムはフォールバックして失敗を処理します。
  4. completeOrder(注文注文):

    • すべてがチェックアウトされると、このメソッドで注文が確定します。エスクロー資金をプロバイダーに解放し、ステータスを更新し、成功についてユーザーに通知します。
  5. handleActivationFailure(注文順序):

    • アクティベーションが失敗した場合、この方法により顧客は返金と問題の原因に関する通知を確実に受け取ります。
  6. getOrder(String orderId):

    • この簡単なメソッドは、ID によって注文を取得します。注文が存在しない場合は、特定の例外がスローされます。
なぜ機能するのか
  • トランザクションの性質により、トランザクションが完了するかロールバックされることが保証されます。
  • 明確なエラー処理と再試行により、現実世界の問題を処理するのに十分な堅牢性を備えています。
  • 通知により、ユーザーはあらゆる段階で最新情報を把握できます。

このサービスは注文管理プロセスのバックボーンであり、すべてを結び付けてシームレスなユーザー エクスペリエンスを実現します。

// Domain Models
@Getter @Setter
@Entity
public class Package {
    @Id
    private String id;
    private String name;
    private BigDecimal price;
    private BigDecimal providerCost;
    private String description;
    private boolean active;
}

@Getter @Setter
@Entity
public class Order {
    @Id
    private String id;
    private String customerId;
    private String packageId;
    private String reservationId;
    private String paymentId;
    private String escrowId;
    private OrderStatus status;
    private BigDecimal amount;
    private BigDecimal providerCost;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}

@Getter @Setter
@Entity
public class QuotaReservation {
    @Id
    private String id;
    private String packageId;
    private LocalDateTime expiresAt;
    private ReservationStatus status;
}

// Enums
public enum OrderStatus {
    CREATED, RESERVED, PAYMENT_PENDING, PAYMENT_COMPLETED, 
    IN_ESCROW, ACTIVATING, ACTIVATION_FAILED, COMPLETED, REFUNDED
}

public enum ReservationStatus {
    ACTIVE, EXPIRED, USED, CANCELLED
}

OrderController クラスは、システム内の注文を管理する REST API エンドポイントを処理します。 Think は、リクエストを行うクライアントと、面倒な作業を行うバックエンド サービスとの間の橋渡しとなります。

主要なエンドポイント
  1. POST /api/orders (createOrder):

    • このエンドポイントは、新しい注文の作成を処理します。
    • 何が起こるか:
      • クライアントから OrderRequest を受け取ります。
      • OrderService.createOrder を呼び出してリクエストを処理し、注文を作成します。
      • 返信:
        • すべてがうまくいけば、新しく作成された注文に対して 200 OK の応答が返されます。
        • パッケージ クォータが使用できない場合の 409 競合。
        • 予期しない問題に対する 500 内部サーバー エラー。
  2. POST /api/orders/callback (handlePaymentCallback):

    • これは、支払いゲートウェイによって送信された支払い更新を処理します。
    • 流れは次のとおりです。
      • すべての支払い詳細を含む PaymentCallback を受け取ります。
      • OrderService.processPayment を呼び出して支払いを処理し、注文ステータスを更新します。
      • 可能な応答は次のとおりです:
        • 支払いが正常に処理されれば 200 OK。
        • 指定された注文 ID が存在しない場合は、404 Not Found。
        • 422 支払い検証に不一致がある場合は処理できないエンティティ。
        • 予期せぬ事態に対する 500 内部サーバー エラー。
  3. GET /api/orders/{orderId} (getOrder):

    • このエンドポイントは、ID によって特定の注文の詳細を取得します。
    • 仕組みは次のとおりです。
      • OrderService.getOrder を呼び出して注文を取得します。
      • 戻り値:
        • 見つかった場合は、注文の詳細を含む 200 OK 応答。
        • 注文 ID がどのレコードにも一致しない場合は、404 Not Found が返されます。
特徴
  • 関心事の分離: OrderController はすべてのビジネス ロジックを OrderService に委任し、物事をクリーンかつ集中的に保ちます。
  • 検証: リクエストのペイロードは @Valid アノテーションを使用して検証され、受信したデータが期待を満たしていることを確認します。
  • エラー処理:
    • 利用できない割り当てや注文の欠落など、一般的な問題に対して具体的で役立つ回答を提供します。
    • デバッグを容易にするために問題をログに記録します。
  • ロギング: 受信リクエスト、エラー、注文の詳細などの主要なイベントを追跡して、可視性を高めます。

このコントローラーにより、クライアントとバックエンドがシームレスに通信できるようになり、注文管理が可能な限りスムーズになります。

結論

この調査文書は、クォータ管理、支払い処理、サービスのアクティベーションなどの重要な課題に取り組む、電子商取引信用販売システムを設計するための基礎を示します。このデザインは基本をカバーしていますが、改善の余地は常にあります!

このデザインを改善するためのアイデアをいくつか紹介します:

  • イベント駆動型アーキテクチャを使用して、システムをより柔軟でスケーラブルにします。
  • 大量のトランザクションをスムーズに処理するために、メッセージ キュー ベースの処理を追加します。
  • 高度なキャッシュ戦略を検討して、処理を高速化し、外部 API への依存を軽減します。
  • スケーリングを容易にし、信頼性を向上させるために、分散システム パターンを検討してください。
  • サードパーティサービスの中断を適切に処理するために、サーキットブレーカーを実装します。
  • モニタリングとアラートを設定して、問題を早期に発見し、迅速に修正します。
  • ユーザーとそのデータを保護するために、セキュリティ対策を強化します。

読んでいただきありがとうございます!このドキュメントが役に立ち、同様の課題を検討している人にとって明確な情報を提供できれば幸いです。もちろん、この設計は完璧ではありません。常に改善の余地があります。ご意見やご提案がございましたら、ぜひお聞かせください。

リソース:

  • クリーンなアーキテクチャと堅実な原則についての詳細
  • マイクロサービス アーキテクチャを使用した電子商取引アプリケーションの設計
  • フラッシュ セールス用のスケーラブルなバックエンドの設計
  • ウィキペディアのエスクロー

以上がインターネットクレジット購入システムの設計の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。