Use Case and Repository Patterns
Defining use cases as Swift protocols and implementing repository interfaces.
Use Case and Repository Patterns is a free Swift Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Swift Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
What is a Use Case?
A use case (interactor) encapsulates a single business action, orchestrating domain entities and repositories.
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)
}
}Single Responsibility
Each use case does exactly one thing. Multiple responsibilities mean multiple use cases.
// One use case per business action:
struct FetchUserUseCase { ... }
struct UpdateUserUseCase { ... }
struct DeleteUserUseCase { ... }Use Case as a Protocol
Define a protocol for the use case so it can be mocked in presentation layer tests.
protocol PlacingOrder {
func execute(userId: Int) async throws -> Order
}
struct PlaceOrderUseCase: PlacingOrder { ... }What is a Repository?
A repository abstracts the data source. Callers interact with a Swift protocol; the implementation may be network, Core Data, or in-memory.
protocol OrderRepository {
func create(receipt: PaymentReceipt) async throws -> Order
func list(userId: Int) async throws -> [Order]
func cancel(orderId: Int) async throws
}Repository Implementation
The concrete repository translates domain calls into network or database operations.
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 { ... }
}In-Memory Repository for Tests
Provide an in-memory repository for fast, deterministic tests without 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 } }
}Composing Use Cases
Use cases can call other use cases for shared sub-operations, keeping each focused.
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
}
}Repository vs Service
Repository: CRUD over a single entity type. Service: orchestrates multiple entities or calls external APIs without direct storage.
// 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 }Caching in Repositories
Repositories can transparently cache results without the use case knowing the implementation detail.
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) }
}Testability
Because use cases depend on protocol repositories, you can inject stubs to test business logic in isolation.
let repo = InMemoryOrderRepo()
let sut = PlaceOrderUseCase(orders: repo, ...)
let order = try await sut.execute(userId: 1)
XCTAssertEqual(repo.orders.count, 1)Error Translation
Repositories should translate data-layer errors (network, DB) into domain errors before returning.
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
}
}Quick Check
What is the key difference between a Repository and a Service in Clean Architecture?
Lesson Recap
Use cases encapsulate single business actions and depend on repository protocols. Repositories abstract data sources behind a protocol. Use in-memory repositories for fast tests. Repositories translate infrastructure errors into domain errors. Services orchestrate multi-entity operations.
Frequently asked questions
Is the “Use Case and Repository Patterns” lesson free?
Yes — the full text of “Use Case and Repository Patterns” is free to read here on the web, and the Swift Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Swift Academy course, upgrade to CoddyKit PRO.
What will I learn in “Use Case and Repository Patterns”?
Defining use cases as Swift protocols and implementing repository interfaces. You practise Swift Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Swift Academy?
No prior experience is required. Swift Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Use Case and Repository Patterns” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Swift Academy lesson?
Yes. Every Swift Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.