Connect with us

Разработка

Приложение без MVC или MVVM — опыт разработки

Отказ от архитектурного проектирования сэкономил время на начальном этапе, но создал проблемы позже.

Опубликовано

/

     
     

Однажды ко мне? как фрилансеру, обратился клиент с очень чётким запросом. Ему нужно было быстро разработать мобильное приложение с ограниченным бюджетом. Во время обсуждения он сказал то, что большинство разработчиков обычно не слышат от клиентов:

«Можем ли мы обойтись без всей этой архитектурной работы? Нам просто нужно, чтобы приложение работало».

Его аргументация была проста. Приложение было ранней идеей стартапа. Он не думал о масштабировании до миллионов пользователей или создании большой команды разработчиков. Он просто хотел быстро запустить что-то и протестировать идею на рынке.

С его точки зрения, тратить время на проектирование архитектуры означало тратить больше денег.

Обычно, когда я начинаю iOS-проект, архитектура естественным образом становится частью процесса. Я создаю отдельные слои для сети, моделей данных, бизнес-логики и пользовательского интерфейса. После нескольких лет разработки это делается автоматически.

Но на этот раз я решил попробовать именно то, что предложил клиент.

Никакой архитектуры.

Никакого MVC.

Никакого MVVM.

Никакой чистой архитектуры.

Просто создаем приложение и заставляем его работать.

Поначалу это оказалось на удивление эффективным.

Разработка шла быстрее обычного

Поскольку архитектурного планирования не было, я сразу же приступил к написанию функционала.

Первым экраном был экран входа в систему. За ним следовал список элементов, полученных из API, а затем экран с подробной информацией, где пользователи могли увидеть более подробные сведения о каждом элементе.

Вместо создания нескольких уровней, всё находилось в одном контроллере представления.

Если экрану нужно было получить данные с сервера, сетевой код писался непосредственно в этом контроллере. Парсинг JSON происходил там же. Обновления пользовательского интерфейса запускались сразу после получения ответа от сети.

Код выглядел простым и понятным.

class ProductsViewController: UIViewController {

var products: [Product] = []
    override func viewDidLoad() {
        super.viewDidLoad()
        fetchProducts()
    }
    func fetchProducts() {
        let url = URL(string: "https://api.example.com/products")!
        URLSession.shared.dataTask(with: url) { data, response, error in
            guard let data = data else { return }
            do {
                let decoded = try JSONDecoder().decode([Product].self, from: data)
                self.products = decoded
                DispatchQueue.main.async {
                    self.tableView.reloadData()
                }
            } catch {
                print(error)
            }
        }.resume()
    }
}

Всё, что касалось экрана, оставалось в одном файле. Не было необходимости переходить между множеством папок или классов, чтобы понять, что происходит.

В первые несколько дней разработка шла очень быстро.

Клиент был доволен, потому что прогресс был виден каждый день. Экраны дорабатывались, функции добавлялись, и приложение начинало выглядеть как настоящий продукт.

На этом этапе отказ от архитектурного проектирования казался действительно разумным решением.

Первый тревожный сигнал

Проблемы возникли не сразу.

Они появились постепенно.

Через несколько недель клиент попросил добавить новую функцию. В списке товаров требовалась функция поиска. Пользователи должны были иметь возможность фильтровать товары по категории и цене.

Обычно эта логика размещалась бы в ViewModel или сервисном слое. Но поскольку у проекта не было структуры, логика фильтрации переместилась непосредственно в контроллер представления.

Файл начал разрастаться.

То, что изначально выглядело как чистый контроллер с примерно 100 строками кода, постепенно превратилось в файл с сотнями строк.

Сетевой код, логика фильтрации, конфигурация пользовательского интерфейса, методы отображения таблиц и логика взаимодействия с пользователем — всё это существовало вместе.

Чтобы визуализировать происходящее, структура экрана постепенно начала выглядеть примерно так.

ProductsViewController


├── UI Setup
├── API Request
├── JSON Parsing
├── TableView Data Source
├── Search Logic
├── Filtering Logic
├── Navigation Logic
└── Error Handling

Всё было перемешано.

На этом этапе код ещё работал, но чтение файла стало занимать больше времени.

Повторное использование кода стало трудным

Следующая проблема возникла, когда другому экрану потребовался тот же API-запрос.

Странице профиля также нужно было получить информацию о товаре. Поскольку сетевая логика находилась внутри ProductsViewController, повторно используемого сервисного слоя не было.

Самым быстрым решением было скопировать сетевой код.

func fetchProducts() {
    let url = URL(string: "https://api.example.com/products")!

URLSession.shared.dataTask(with: url) { data, response, error in
        guard let data = data else { return }
        let decoded = try? JSONDecoder().decode([Product].self, from: data)
        DispatchQueue.main.async {
            self.products = decoded ?? []
            self.tableView.reloadData()
        }
    }.resume()
}

Та же логика встречалась в нескольких контроллерах.

Поначалу это казалось безобидным, но дублирование кода имеет свойство создавать проблемы в будущем.

Через несколько дней формат ответа API немного изменился. Теперь нужно было обновить каждое место, где происходил вызов API.

Из-за дублирования кода изменения приходилось повторять в нескольких файлах.

Отладка стала сложнее

Настоящая проблема возникла, когда была обнаружена первая серьёзная ошибка.

Некоторые пользователи заметили, что в списке товаров иногда отображались устаревшие данные. Проблема возникала непостоянно, что затрудняло её воспроизведение.

Для отслеживания проблемы требовалось понять, откуда берутся, обрабатываются и отображаются данные.

Поскольку всё происходило внутри контроллера представления, файл содержал множество несвязанных между собой логических элементов. Сетевые операции, поведение кэширования, обновления пользовательского интерфейса и логика фильтрации — всё это взаимодействовало с одними и теми же переменными.

Структура кода на тот момент выглядела примерно так.

ViewController
   ↓
Network Request
   ↓
JSON Parsing
   ↓
Business Logic
   ↓
UI Updates
   ↓
Navigation

Между слоями не было четкой границы. Это означало, что для отладки требовалось читать большие фрагменты кода, чтобы понять, как данные перемещаются по экранам.

В конце концов, проблема была решена, но процесс занял гораздо больше времени, чем ожидалось.

Кодовая база стала сложнее в поддержке

По мере роста проекта небольшие изменения стали требовать больше усилий.

Простое изменение одной функции могло случайно повлиять на другую часть экрана. Поскольку вся логика находилась в одном месте, обязанности были тесно связаны.

Контроллеры представлений стали большими и сложными.

Разработчики иногда шутят о так называемом синдроме «массивного контроллера представления» в iOS-разработке. Это происходит, когда контроллеры становятся настолько большими, что берут на себя все обязанности в приложении.

Этот проект постепенно начал двигаться в этом направлении.

То, что начиналось как быстрый и простой подход, постепенно превратилось в нечто более сложное в управлении.

Почему существуют архитектурные шаблоны

Этот эксперимент помог мне понять нечто важное.

Архитектурные шаблоны не предназначены для того, чтобы проекты выглядели академически чистыми. Они существуют потому, что приложения естественным образом растут в сложности.

Когда обязанности правильно распределены, каждая часть системы становится проще для понимания.

Типичная структура в стиле MVVM может выглядеть следующим образом.

View
   ↓
ViewModel
   ↓
Service Layer
   ↓
Network Layer

Каждый слой имеет чётко определённую задачу.

Слой View отвечает за пользовательский интерфейс. Слой ViewModel отвечает за бизнес-логику. Слой Service управляет данными. Слой Network отвечает за взаимодействие с API.

Благодаря разделению этих слоёв, код становится проще тестировать, повторно использовать и поддерживать.

Отказ от архитектуры устраняет эту структуру.

На начальном этапе это ускоряет разработку. Но по мере роста приложения затраты проявляются по-разному.

Урок из этого эксперимента

В итоге клиент получил своё приложение и успешно запустил продукт. В их конкретной ситуации упрощённый подход был приемлем, поскольку приложение было небольшим и разрабатывалось быстро.

Но с точки зрения разработчика, этот опыт напомнил о важном моменте.

Архитектурные шаблоны не нужны для каждого небольшого проекта. Для прототипов, экспериментов или краткосрочных продуктов написание простого кода может быть даже быстрее.

Однако, как только приложение начинает расти, структура становится необходимой. Без неё сложность постепенно накапливается в местах, которые становится трудно контролировать.

Интересно то, что первая версия приложения работала идеально. Проблемы появились позже, по мере добавления новых функций и расширения кодовой базы.

Именно поэтому существуют архитектурные шаблоны.

Они существуют не для того, чтобы замедлять разработчиков. Они существуют для того, чтобы предотвратить превращение небольших проектов в неуправляемые по мере их роста.

Если вы нашли опечатку - выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor.ru.
Telegram

Популярное

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: