Skip to Content
iOS🏗️ ArchitectureClean Architecture

Clean Architecture

1. Layers

┌─────────────────────────────────────┐ │ Presentation │ Views, ViewModels ├─────────────────────────────────────┤ │ Domain │ Use Cases, Entities ├─────────────────────────────────────┤ │ Data │ Repositories, API └─────────────────────────────────────┘

2. Domain Layer

// Entity struct User: Identifiable { let id: Int let name: String let email: String } // Repository Protocol protocol UserRepository { func getUsers() async throws -> [User] func getUser(id: Int) async throws -> User } // Use Case class GetUsersUseCase { private let repository: UserRepository init(repository: UserRepository) { self.repository = repository } func execute() async throws -> [User] { try await repository.getUsers() } }

3. Data Layer

class UserRepositoryImpl: UserRepository { private let apiClient: APIClient func getUsers() async throws -> [User] { let response: [UserDTO] = try await apiClient.get("/users") return response.map { $0.toDomain() } } } struct UserDTO: Codable { let id: Int let name: String let email: String func toDomain() -> User { User(id: id, name: name, email: email) } }

4. Presentation Layer

@MainActor class UserListViewModel: ObservableObject { private let getUsersUseCase: GetUsersUseCase @Published var users: [User] = [] init(getUsersUseCase: GetUsersUseCase) { self.getUsersUseCase = getUsersUseCase } func loadUsers() async { users = try await getUsersUseCase.execute() } }

📝 Benefits

  • Clear boundaries between layers
  • Easy to test each layer
  • Scalable for large apps
  • Independent of frameworks
Last updated on