В стародавние времена был такой язык Clipper (писала писала группа разрабов объединенных под крылом Nantucket). Они предложили революционную тогда технологию для приложения — микроядро(загружала/выгружала модули и делала сборку мусора). Идею портировали под Linix — но проект умер. По сравнению с Clipper Clean Architecture — просто дитя той архитектуры. Ты мог подключить динамически любые модули в любое время, разбить свое приложение как тебе нравиться — поддерживая интерфейс только модульности. Поэтому вопрос о Clean Architecture — это вопрос о частном разбиении на слои(модули), где каждый может разбивать свое приложение как хочет — и будет прав. Одно плохо, что в android начинают затаскивать кусочки всякой всячины. И приходится следовать моде. То узкие штаны, то широкие.
Для примера попробовать силы могу предложить — загрузите в андроид КЛАДР и попробуйте сделать в нем удобный поиск/фильтрацию (т.е. например проверку достоверности паспортных данных). И попробовать поработать например с местом жительства Солнечный/Советский
Клиент хочет список из > 10000 строк и чтоб он появлялся с задержкой не более 2 сек и чтоб он лазил по-нему/скроллил/фильтровал/искал с задержкой не более 2 сек. И его не волнует, что это андроид например 2.36 (и 48 метров под activity). Он заплатил. И вот теперь может, кто подскажет еще решения полной работы с длинными списками. В свете последней новомодной livedata (помним что content provider в 99 случаях из 100 возвращает только то что таблица изменилась)
Я максимально пытаюсь уйти от архитектуры андроид с ее неполноценными жизненными циклами. И максимально адаптировать архитектуру под активный легкий клиент — пассивный сервер. Но жизнь возвращается и все эти livedata, над которыми смеялись разрабы 20 лет назад — как будто и не было этих лет. И в данных условиях приходится отказываться от пассивных серверов, к каким-то другим решениям. И в настоящее время ищется такое решение. Которое видится в архитектуре ядро + подключаемые легкие модули (аналог Clipper 5, если кто знаком с его архитектурой). Админ только управляет загрузкой/связыванием модулей. Не путать c Dagger 2 — правильной будет архитектура ядро + подключаемые(именованные) процессы (c EventBus внутри каждого) — но она очень тяжеловесна — но позволяет все.
Клиент за 1 лицензию на 1 устройство платит на уровне лицензии 1с. Поэтому вопрос не стоит как-то отпадает сразу. Помним — это не Россия.
Использовалось несколько вариантов загрузки больших списков:
— тройная буферизация + метод скользящего окна
— постоянный курсор в памяти + fetch при достижении предпоследней страницы (самый тормозной)
— подгрузка данных в фоне небольшими порциями
у каждого метода есть свои плюсы и минусы. Хотелось бы узнать — кто еще что может предложить?
Хорошая ORM ни к каким реляционным возможностям доступ не ограничивает. Объектное возможности — это строгое надмножество реляционных возможностей. Делая объектный адаптер к реляционному, ми теоретически ничего не теряем.
Чисто только теоретически. Это тоже самое, что и связывание таблиц в SQL запросах. На бумаге и в теории все просто. В реальности уже на join таблиц > 8 получаешь проседание, а при >16 оно может быть просто критическим.
В реальности например всегда требуется просмотр всего инвойса. Соответственно ORM тянет все содержимое инвойса — целостной сущности. Меняем 1 строку и записываем — ORM по этой команде должна обновить инвойс — что делается почти всегда 2 операциями — удалить старую сущность, а затем добавить новую сущность. При этом вы всегда попадаете на удаление/вставку туевой хучи данных. Это как использовать 1с из коробки. Все прекрасно пока листьев мало. Т.е. все прекрасно — но только для конкретных условий. Разработчик не может мыслить реляционно — бога ради пускай использует ORM пока не нарвется (а в большинстве случаев — и нет) в провале производительности. Вопрос для примера — заказчик требует просмотра сразу отсортированных 10000 записей на экран смарта(ему так нравиться). Как это организовать в RecyclerView(помним что всего одна строчка кода — cursor.getCount() приводит к fetch всех 10000 строк в БД)? Но самый страшный вопрос — как организовать обновление этого списка при новомодном livedata(обновление UI при изменении данных). Помним, что при этом он может с легкостью выйти из просмотра списка(destroy fragment и все прелести после этого) и при этом он должен быть в рамках нашей архитектуры, когда UI отделен от выборки данных.
Сама идея прекрасна, но ее лучше всего реализовать на объектных БД. Она начинает сильно проседать на объектах с большим количеством листьев. Т.е. возьмем счет-фактуру — пока в ней до 10 строк все более менее прекрасно, но когда более 100 записей — тянуть хвост в 100 записей, когда дай бог изменяется 1 строка глупо. Т.е. для каждой области должны быть свои решения. Много деревьев и мало листьев — самое место ORM. Где надо работать с огромными простынями — глупо использовать ORM. Просто решения для БД в 2 Мб одни, а для 500 Мб на мобиле с андроид 2.36 и 48 метрами под activity совсем другие. Тогда уже смотрят как выбирать данные — курсорно (аля Oracle) или листами(кортежами — наборами строк как в MS SQL). Тогда уже не до объектов — а строго только через View БД, включающими только столбцы для просмотра.
RxJava пришла с платформы Windows. Она имеет все особенности Windows (потому что на ней писалась). Попытки привязать к ней lifecycle и прочие плюшки android — остаются плюшками. Использовать систему написанную под одну систему на другой — глупо, но можно. Как например использовать реляционные БД для хранения объектов (которые разрабатывались вообще не для этого и должны использоваться не для этого) и соответственно писать ORM для реляционных БД. Использовать можно все. И микроскопом можно забивать гвозди. Но давайте использовать лучше молоток для этого, а микроскоп использовать для другого. Т.е. оставим каждому инструменту свою область применения.
Реальный пример из жизни — крутой проект полностью на Rx. Дали оценить. Тыкаю пальцем и говорю — вот здесь может происходить смена логики — что делаете. Ответ — подменяем последовательность. Но этого же не видно в коде. Да не видно. И кто кроме разраба это знает? Сколько раз писались заново целые огромные куски кода — после того как уходил разработчик или забывал сам что и где. Как сопровождать такую систему. Да rxjava — мощная система — но лучше ее использовать для прямых по логике вычислений в условиях без смены внешнего окружения.
Последовательность — это разделенные по времени(асинхронные) события. Т.е. если смотреть сверху в течении промежутка времени — то параллельный поток событий можно отобразить на последовательность событий. Т.е. RxJava возможно использовать, если Вы гарантируете, что при дополнительном ветвлении параллельного потока событий не произойдет смена логики. Например — вы поставляете товар заказчикам. Обычная последовательность. Вдруг ваш основной заказчик — вдруг не может оплатить/разгрузить товар или любое др событие. Ваша последовательность заказал — доставил — оплатил ломается. Вывалиться по ошибке — можно (у RxJava только 2 состояния). Но это ваш основной заказчик. Или на Android — например Activity имеет несколько взаимозависимых фрагментов. Но Activity создает фрагменты в любом порядке — ей плевать на все ваши зависимости. Или жизненный цикл activity — когда событие onDestroy activity может приходить после события onCreate этой же activity — хотя по логике activity должна быть уничтожена, а потом создана — но это Activity — ей можно все (это встречается повсюду в тесте monkey, когда события сыплются как из пулемета). Вот на таких последовательностях и ломается RxJava. Или более сложная система — система с гарантированной доставкой. Поддерживает несколько каналов связи (абсолютно ничем несовместимых кроме интерфейса — принять/передать) и динамически сменяет каналы в зависимости от возможностей среды и имеет сложную логику контроля доставки, которая не подразумевает 100% вероятности доставки. Т.е. RxJava — это решение, когда подразумевается 100% вероятные решения (положительные/отрицательные), когда внешняя логика поведения гарантированно неизменна. А не так как на Android — захочется ей и тебе прилетит в listener null вместо объекта (monkey очень дает много пищи для размышлений)
Самое главное в этой статье — чтобы у всех в том числе у автора пришло бы понимание, что данная архитектура пришла из больших бизнес систем. Где основное требование — это их полная адаптивность и независимость, позволяющая быстро перестраиваться под изменяющиеся внешние требования. Поэтому например автор ратует за RxJava(которую я думаю специально притащили с винды) — не понимая, что она жестко требует от системы наличия ПОСЛЕДОВАТЕЛЬНОСТЕЙ и только ПОСЛЕДОВАТЕЛЬНОСТЕЙ. И если вам потребуется завтра где-то ввести параллельную новую ветку — вся ваша система слетит. В мире Clean Architecture царят только события. И правильная(продуманная) обработка событий — основа данной архитектуры. Поэтому вопрос как делить на слои и сколько будет слоев — абсолютно вторичен и не играет большой роли. Важно чтобы слои были абсолютно независимы. Чтобы объект слоя принимал и отдавал события (объект, который содержит все что нужно для дальнейшей обработки). Поэтому бесперебойная транспортная система — это основа данной архитектуры, от которой зависит вся система в целом. И на андроиде просто приходиться подстраиваться под ее lifecycle, соответственно подстраиваясь под все повороты. Как и в жизни. То заказчик отъехал, то деньги не заплатил, то ему уже не нужен товар. Clean Architecture — это попытка достоверно описать нашу жизнь. Ее оптимизировать и улучшить
Использовалось несколько вариантов загрузки больших списков:
— тройная буферизация + метод скользящего окна
— постоянный курсор в памяти + fetch при достижении предпоследней страницы (самый тормозной)
— подгрузка данных в фоне небольшими порциями
у каждого метода есть свои плюсы и минусы. Хотелось бы узнать — кто еще что может предложить?
Чисто только теоретически. Это тоже самое, что и связывание таблиц в SQL запросах. На бумаге и в теории все просто. В реальности уже на join таблиц > 8 получаешь проседание, а при >16 оно может быть просто критическим.
В реальности например всегда требуется просмотр всего инвойса. Соответственно ORM тянет все содержимое инвойса — целостной сущности. Меняем 1 строку и записываем — ORM по этой команде должна обновить инвойс — что делается почти всегда 2 операциями — удалить старую сущность, а затем добавить новую сущность. При этом вы всегда попадаете на удаление/вставку туевой хучи данных. Это как использовать 1с из коробки. Все прекрасно пока листьев мало. Т.е. все прекрасно — но только для конкретных условий. Разработчик не может мыслить реляционно — бога ради пускай использует ORM пока не нарвется (а в большинстве случаев — и нет) в провале производительности. Вопрос для примера — заказчик требует просмотра сразу отсортированных 10000 записей на экран смарта(ему так нравиться). Как это организовать в RecyclerView(помним что всего одна строчка кода — cursor.getCount() приводит к fetch всех 10000 строк в БД)? Но самый страшный вопрос — как организовать обновление этого списка при новомодном livedata(обновление UI при изменении данных). Помним, что при этом он может с легкостью выйти из просмотра списка(destroy fragment и все прелести после этого) и при этом он должен быть в рамках нашей архитектуры, когда UI отделен от выборки данных.