Swift Academy · レッスン

ユースケースとリポジトリパターン

ユースケースをSwiftプロトコルとして定義し、リポジトリインターフェースを実装します。

レッスン 2/413 ステップ

「ユースケースとリポジトリパターン」はCoddyKit上の無料Swift Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはSwift Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Swift Academyコースには全4レッスンが含まれています。

ユースケースとは

ユースケース(インタラクター)は、単一のビジネスアクションをカプセル化し、ドメインエンティティとリポジトリを調整します。

struct PlaceOrderUseCase {
  let cart: CartRepository
  let payment: PaymentService
  let orders: OrderRepository
  func execute(userId: Int) async throws -> Order {
    let items = try await cart.items(for: userId)
    let receipt = try await payment.charge(items: items)
    return try await orders.create(receipt: receipt)
  }
}

単一責任

各ユースケースは、まさに1つのことだけを行います。複数の責任がある場合は、複数のユースケースに分割します。

// One use case per business action:
struct FetchUserUseCase { ... }
struct UpdateUserUseCase { ... }
struct DeleteUserUseCase { ... }

プロトコルとしてのユースケース

ユースケース用のプロトコルを定義すると、プレゼンテーション層のテストでモックに差し替えられます。

protocol PlacingOrder {
  func execute(userId: Int) async throws -> Order
}
struct PlaceOrderUseCase: PlacingOrder { ... }

リポジトリとは

リポジトリはデータソースを抽象化します。呼び出し側はSwiftのプロトコルを通じて操作し、実装にはネットワーク、Core Data、メモリ内のデータなどを利用できます。

protocol OrderRepository {
  func create(receipt: PaymentReceipt) async throws -> Order
  func list(userId: Int) async throws -> [Order]
  func cancel(orderId: Int) async throws
}

リポジトリの実装

具体的なリポジトリは、ドメインからの呼び出しをネットワークやデータベースの操作に変換します。

struct RemoteOrderRepository: OrderRepository {
  func create(receipt: PaymentReceipt) async throws -> Order {
    let dto = receipt.toDTO()
    let data = try await api.post("/orders", body: dto)
    return try JSONDecoder().decode(OrderDTO.self, from: data).toDomain()
  }
  func list(userId: Int) async throws -> [Order] { ... }
  func cancel(orderId: Int) async throws { ... }
}

テスト用のメモリ内リポジトリ

I/Oを使わずに高速で決定論的なテストを行えるよう、メモリ内リポジトリを用意します。

final class InMemoryOrderRepo: OrderRepository {
  var orders: [Order] = []
  func create(receipt: PaymentReceipt) async throws -> Order {
    let order = Order(id: orders.count + 1, items: receipt.items)
    orders.append(order)
    return order
  }
  func list(userId: Int) async throws -> [Order] { orders }
  func cancel(orderId: Int) async throws { orders.removeAll { $0.id == orderId } }
}

ユースケースの組み合わせ

ユースケースは、共通するサブ処理のために他のユースケースを呼び出せます。これにより、それぞれの役割を明確に保てます。

struct CheckoutUseCase {
  let validateCart: ValidateCartUseCase
  let placeOrder: PlaceOrderUseCase
  let notifyUser: NotifyUserUseCase
  func execute(userId: Int) async throws -> Order {
    try await validateCart.execute(userId: userId)
    let order = try await placeOrder.execute(userId: userId)
    try await notifyUser.execute(order: order)
    return order
  }
}

リポジトリとサービスの違い

リポジトリ:単一のエンティティ型に対するCRUDを行います。サービス:複数のエンティティを調整したり、直接ストレージを扱わずに外部APIを呼び出したりします。

// Repository: single entity
protocol UserRepository { func getUser(id: Int) async throws -> User }
// Service: cross-entity operation
protocol AuthService { func login(email: String, password: String) async throws -> Session }

リポジトリでのキャッシュ

リポジトリは、実装の詳細をユースケースに意識させることなく、透過的に結果をキャッシュできます。

struct CachedUserRepository: UserRepository {
  private var cache: [Int: User] = [:]
  private let remote: UserRepository
  func getUser(id: Int) async throws -> User {
    if let cached = cache[id] { return cached }
    let user = try await remote.getUser(id: id)
    cache[id] = user
    return user
  }
  func saveUser(_ user: User) async throws { try await remote.saveUser(user) }
}

テスト容易性

ユースケースはプロトコルで定義されたリポジトリに依存するため、スタブを注入してビジネスロジックを単独でテストできます。

let repo = InMemoryOrderRepo()
let sut = PlaceOrderUseCase(orders: repo, ...)
let order = try await sut.execute(userId: 1)
XCTAssertEqual(repo.orders.count, 1)

エラーの変換

リポジトリは、データ層のエラー(ネットワークやDBのエラー)を返す前にドメインエラーへ変換する必要があります。

func getUser(id: Int) async throws -> User {
  do {
    let data = try await api.get("/users/\(id)")
    return try decode(data)
  } catch let e as URLError where e.code == .notConnectedToInternet {
    throw DomainError.offline
  }
}

確認問題

Clean Architectureにおけるリポジトリとサービスの主な違いは何ですか。

学習内容のまとめ

ユースケースは単一のビジネスアクションをカプセル化し、リポジトリプロトコルに依存します。リポジトリはプロトコルの背後でデータソースを抽象化します。高速なテストにはメモリ内リポジトリを使用します。リポジトリはインフラストラクチャのエラーをドメインエラーに変換します。サービスは複数のエンティティにまたがる処理を調整します。

無料で開始

AI チューターと学ぶ Swift — 無料

ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。

コース
122
レッスン
409

よくある質問

「ユースケースとリポジトリパターン」レッスンは無料ですか?

はい。「ユースケースとリポジトリパターン」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Swift Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Swift Academyコースには全4レッスンが含まれています。

「ユースケースとリポジトリパターン」で何を学びますか?

ユースケースをSwiftプロトコルとして定義し、リポジトリインターフェースを実装します。 ブラウザで直接実行するハンズオンコードでSwift Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Swift Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのSwift Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「ユースケースとリポジトリパターン」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このSwift Academyレッスンでコードを書いて実行できますか?

はい。すべてのSwift Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. レイヤー:ドメイン、データ、プレゼンテーション
  2. ユースケースとリポジトリパターン
  3. プロトコル抽象化による依存性逆転
  4. DIフレームワークを使わずにレイヤーを接続する
← Swift Academyに戻る