순수한 Swift 구조체/클래스, 비즈니스 규칙과 핵심 로직을 담음
외부 프레임워크나 Cocoa Touch에 대한 의존이 전혀 없음
Entity를 활용해 구체적인 비즈니스 동작을 수행
입력(요청) → 비즈니스 로직 → 출력(응답) 형태로 구성
인터페이스(프로토콜)를 통해 외부(Repository 등)와 통신
데이터베이스, 네트워크, UI 등 외부 시스템과 Use Case/Entity 사이의 변환 책임
예 : Repository 구현체, Presenter, ViewModel
Frameworks & Drivers (프레임워크와 드라이버)
실질적인 iOS SDK(UI, network, DB 라이브러리 등)
앱 진입점(AppDelegate, SceneDelegate), ViewController, Kingfisher, Alamofire 등
코드 의존성 방향은 바깥 레이어 → 안쪽 레이어로만 향해야 한다.
상위 레이어는 하위 레이어의 구체 구현체가 아니라, 프로토콜(추상화)에만 의존한다.
SRP (단일 책임 원칙)
OCP (개방-폐쇄 원칙)
LSP, ISP, DIP 등
// 1) Entity
struct Movie {
let id: Int
let title: String
}
// 2) Use Case Protocol
protocol FetchMoviesUseCase {
func execute(completion: @escaping (Result<[Movie], Error>) -> Void)
}
// 3) Use Case 구현체
final class FetchMoviesInteractor: FetchMoviesUseCase {
private let repository: MovieRepository
init(repository: MovieRepository) {
self.repository = repository
}
func execute(completion: @escaping (Result<[Movie], Error>) -> Void) {
repository.fetchAll { result in
switch result {
case .success(let data):
// 추가적인 비즈니스 로직 가능
completion(.success(data.map { Movie(id: $0.id, title: $0.title) }))
case .failure(let error):
completion(.failure(error))
}
}
}
}
// 4) Repository Protocol (Interface Adapter)
protocol MovieRepository {
func fetchAll(completion: @escaping (Result<[MovieDTO], Error>) -> Void)
}
// 5) Repository 구현체 (infrastructure)
final class MovieAPIRepository: MovieRepository {
func fetchAll(completion: @escaping (Result<[MovieDTO], Error>) -> Void) {
// Alamofire/URLSession을 통해 네트워크 호출
}
}
레거시 프로젝트에 도입할 땐 점진적으로 Use Case → Repository → UI 순으로 분리
Dependency Injection을 활용해 테스트 용이성 확보
작게 시작해서, 프로젝트 규모가 커질수록 레이어를 엄격히 분리