Код-ревью Senior Clean Architecture и слои обновлено

Код-ревью: use case оформления заказа (Swift)

Вопрос

Найдите нарушения границ слоёв в use case.

import UIKit
import Alamofire

final class PlaceOrderUseCase {
    let vc: UIViewController

    func execute(cart: CartDTO) async throws -> UIColor {
        guard cart.items.count > 0 else {
            await vc.present(UIAlertController(title: "Пусто", message: nil, preferredStyle: .alert), animated: true)
            return .red
        }
        let response = try await AF.request("https://api.shop.ru/orders", method: .post)
            .serializingDecodable(OrderDTO.self).value
        UserDefaults.standard.set(response.id, forKey: "lastOrder")
        return response.status == "ok" ? .green : .red
    }
}

Коротко

Чистая версия: final class PlaceOrderUseCase { init(orders: OrderRepository, history: OrderHistoryStore) }, метод execute(cart: Cart) async throws(OrderError) - PlacedOrder. Пустая корзина это доменная ошибка, которую view model покажет алертом.

Что должен найти ревьюер

  1. Стильстрока 1
    Домен импортирует UIKit: use case нельзя протестировать и переиспользовать без UI.
  2. Стильстрока 2
    Сетевая библиотека в домене: запрос должен уходить через интерфейс репозитория, реализованный в data.
  3. Ошибкастрока 5
    Use case держит контроллер и показывает алерт: это ответственность presentation.
  4. Стильстрока 7
    Use case принимает DTO и возвращает цвет: на входе и выходе должны быть доменные типы (корзина, результат заказа).
  5. Стильстрока 14
    Прямая запись в UserDefaults: хранение тоже скрывается за абстракцией.
  6. Ошибкастрока 15
    Статус сравнивается со строкой из ответа: формат API протекает в бизнес-логику, опечатка на сервере сломает заказ.

Разбор

Чистая версия: final class PlaceOrderUseCase { init(orders: OrderRepository, history: OrderHistoryStore) }, метод execute(cart: Cart) async throws(OrderError) -> PlacedOrder. Пустая корзина это доменная ошибка, которую view model покажет алертом. Репозиторий в data делает запрос и маппит DTO со строковым статусом в enum.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.