СОДЕРЖАНИЕ: 00:00:00 - что такое слой DATA 00:00:33 - кратко про предыдущий урок 00:03:17 - диаграмма Clean Architecture 00:05:09 - паттерн Repository на практике 00:09:19 - правильная связь Domain c Data 00:22:09 - пишем внутрянку для Repository 00:33:18 - кратко о том, что мы сделали в уроке 00:35:17 - подводим итоги и ставим лайки :) На канале также есть и другие уроки по Android Clean Architecture.
@bitmap Да, Боб Мартин. У него помимо "Clean code" есть статьи по архитектуре, собственно откуда все эти слои и пошли а также его книга "Clean architecture". Также есть адаптированный вариант для андроида у Фернандо Седжаса.
Тимофей, уважаемый Тимофей. Огромное вам спасибо. Вы великолепный преподаватель! Потрясающе интересные уроки. Желаю вам и вашим родным, крепкого здоровья и настоящего счастья!
Супер объяснение! Все ваши видео в захлеб смотрю, до этого сколько не смотрел по чистой архитектуре, ничего не понятно было - с вашей помощью со всем разобрался!
Тимофей, как обычно, Ваши видео просто супер. Для начинающих разработчиков Clean Architecture одна из самых тяжёлых и непонятных тем. Жду продолжения. Удачи!
+++ Плюсую. Все остальные видео которые я смотрел по архитектуре андроид приложений, там просто авторы говорили "Бла-бла-бла, тут короче Domain в Domain-е хранится вот это и там короче кроме Domain есть Presentation и Data. Конец". А в видеоуроках от Тимофея, именно создаешь вместе с автором, приложения используя Clean Architect на практике и после уроков откладывается в голове, что, как работает и что вообще такое Clean Architect
Огромное спасибо за урок. В моменте с объяснением элвис оператора 28:10 используется строка с firstName и параметр DEFAULT_NAME который создан для lastName. (Лично мне понятно, но тем кто видит ?: первый раз может показаться что-то искаженное =) Извиняюсь если уже подсвечивали ранее или если это избыточный комментарий. В любом случае урок потрясающий!
Очень понравилась серия видео, доходчиво и наглядно! Одно только смутило, если слой domain ни про кого не знает, почему от него идут стрелки зависимостей? Разве не другое направление у них должно быть, от data и presentation к domain.
господи, спасибо ВАМ за эти шикарные и понятные, составленные на русском (а не девелоперском) языке объяснения. Мне кажется, что по вашим урокам можно аутиста клин архитектуре научить
класс, спасибо вам большое!) Лайк и подписка однозначно!) Скажите, а планируются уроки по таким темам как ретрофит, корутины, рум? Думаю очень полезным был бы ролик с практикой на каком-то маленьком демо-проекте, но где бы сочетался такой стек ?)
Спасибо за урок, все очень доходчиво и понятно ! По твоим видео понял суть чистой архитектуры на практике У меня вопрос: Нужно ли помешать логику в отдельный usecase если usecase не взаимодействует с датой через интерфейс, а возвращает некий объект описанный в слое domen ? Было бы отлично если бы уроки по чистой архитектуры выходили и дальше, которые затрагивали бы и другие примеры которые очень часто встречаются на практике например: использование mvvm, recyclerView, взаимодействие с бэком в чистой архитектуре. Также не совсем по теме, нигде не смог найти внятное объяснение и пример использования DI (Dagger 2) в MVVM (Передача зависимостей во ViewModel + зависимости с контекстом) P.S спасибо еще раз, прошел собес -_-
Спасибо за отзыв! Конечно буду продолжать, еще много планов на новые видео по архитектуре. По поводу помещения логики в usecase, нет таких правил что бы usecase обязательно зависел от даты, в домен выносится вся логика, даже та которая просто что-то делает но не общается в data слоем.
Это уже ответственность DI, библиотеки разные могут быть, да и придется уже серьезно в конкретную библиотеку залазить. Глубоко по DI снимать пока плана нет.
Юзкейсы позволяют переиспользовать логику работы с репозиториями в UI слое. Т.е. когда у вас вызовы репозиториев - это например сложные цепочки и используются в разных местах. Это мне кажется первая причина, почему не используются репозитории в UI.
Все здорово! Спасибо! Помогите, плз, понять, почему на схеме Presentation знает про Data напрямую? В коде данного видео Activity знает только про UseCases. Разве в клин архитектуре можно "обходить" домен слой?
Да, presentation сам по себе не должен иметь доступ к data, только к use cases. Но, presentation в нашем случае находится внутри модуля app, а app занимается конфигурированием приложения и должен знать о всех, поэтому технически доступ к data возможен. Что бы избежать можно вынести presentation в отдельный модуль, а лучше разделять его на feature модули, про них планирую рассказать.
Здравствуйте, очень хотелось бы получить объяснение касательно presentation слоя. В видео вы сказали что presentation слой может знать и об domain и о data. Тогда как все мои знакомые это отвергли и сказали что напрямую presentation слой не должен знать о data. Так как на этом видео можно писать только в случае ну крайне быстрой и ленивой реализации, в очень простой бизнес логике. Presentation работает только через domain с data и друг о друге они знать не должны. Можно только подцепить даггерчкий элемент, но не более того, так как в видео делать - все сказали что нельзя, это нарушает принцип инверсии зависимости.
Во первых я в видео много раз говорю, что я даю вам фундаментальные принципы, на основе которых, вы уже дальше развиваете свой проект. Если бы я давал сложный пример сходу со всем функционалом, то мало кто разобрался бы с этим. Мои видео все же делаются для моих студентов, которые только начинают изучать программирование. Во вторых: "презентейшен" слой не знает о "дата", это правда, но "app" знает, так как этот модуль является точкой входа в приложение и нам просто больше негде инициализировать все зависимости: "домен" и "дата". И, "app" внутри себя содержит "presentation", соответственно "presentation", технически имеет доступ к этим зависимостям, но логически мы используем только домен. Решается это путем разделения "app" и "презентейшен" на разные модули, либо использование фича модулей. Так же посмотрите видео, где разбирается DI, тогда станет понятно.
Здравствуйте Тимофей, хотел спросить, GetUserNameUseCase и SaveUserNameUseCase принимают объект интерфейса UserRepository, но в MainActivity вы передали объект UserRepositoryImpl
Тимофей здравствуйте, вот вы говорите что в data не может быть никакой логики, у меня тогда вопрос может ли репозиторий управлять источником данных, а конкретно может ли быть там логика выбора источника данных в зависимости например от наличия интернет подключения? Или например из UseCase дергаем список фильмов а репозиторий решает какой из методов апи дернуть для получения списка фильмов, например если их несколько но есть условия их получения?
Спасибо больше за видео, очень интересно и понятно рассказана тема! Подскажите пожалуйста, что означает фраза "это класс, который делает корут операции" по отношению к репозиторию? Что такое корут операции?
Здравствуйте. Спасибо за видео и труда. У меня есть вопрос по поводу applicationContext. Сначала вы запустили программу. Там написал, что applicationContext on a null object reference. А когда вы писали by lazy он работал. Это означает, что applicationContext создаётся внутри функции onCreate?
:), очень много читал, особенно когда только начинал заниматься программированием. Сейчас уже меньше, опыт уже значительно выше чем большинство статей.
Спасибо за доходчивый разбор данной темы, в ру сегменте я такого еще не встречал. Вопрос у меня возник такой, пишу приложение в котом есть большое количество фоновой работы, WorkManager который запускает foreground сервисы, в каком из слоев будет правильно размещать подобный функционал? Мне вот кажется, что нужно выносить в совершенно отдельный модуль или в presentaition , но интересно мнение профессионала.
Если WorkManager используется только для синхронизации данных, то без проблем можно положить его в data слой. Data ведь как раз и предназначен для работы с данными. Выносить в отдельный модуль я бы не стал, только если необходимо сделать универсальную реализацию, которая будет использовать в нескольких проектах. А вообще тема синхронизации данных очень обширная, тут много нюансов. Думаю про эту тему тоже сделаю отдельную серию видео.
Очень круто! А скажи пожалуйста, вот у нас в домене есть модели и есть юзкейсы которые их берут на вход или выход. Нужно ли использовать для них интерфейсы или это избыточно? Спасибо
Избыточно. Но тут на вкус и цвет, кто как делает. Лично я считаю, что стоит делать только если это реально решает, какую-то проблему. Делать просто для красоты или потому, что так написано в умной книжке нет никакого смысла.
Тимофей привет! У меня такой вопрос: Как прогнать LiveData через domain, если в этом слое нельзя импортировать данную библиотеку? Просто у меня есть usecase: getMoviesFromApiUseCase. В app и в data я сохраняю всё и использую в виде LiveData, но сделать прогон через usecase в domain слое я не могу. Как поступить, если сохранять Clean Architecture?
Может ли репозиторий хранить в себе методы генерации данных, например генерация вопроса generateQuestion, реализацию этого метода из репозитория я добавляю в RepositoryImpl в data слое и генерирую вопрос на основе числа, которое ввел пользователь, будет ли это ошибкой? Возможно в GenerateQuestionUseCase даже не нужно передавать репозиторий, а можно сразу написать генерацию вопроса, это же вроде как бизнес логика, не требующая доступ к БД.
Эти вопросы ведь генерируются из какого-то списка? Откуда они берутся? Соответственно данные, на основе которых происходит генерация, где-то хранятся. Я бы в репозитории это делал.
@@TimofeyKovalenko эти вопросы генерируется во время работы приложения на основе данных, которые подает пользователь прямо во время работы приложения, затем выводятся на экран, после перезапуска забываются и генерируется снова
я вот или понял или нет) не могу понять)....... я так понял, это все "фокусы", посути, все даные храняться в одной компосибл функции (хмл не знаю, юзаю компос), я долго пытался понять, как даные куда передаються, но в этоге пришол к мнению, что не чего не куда не передаеться, а храниться тут: val userRepository = UserRepositoryImpl(context = LocalContext.current ) val usecaseGetName = UserCaseGETname(userRepository) val useCaseSaveName = UseCaseSaveName(userRepository) я прав?
Спасибо, очень интересно. Но вот урок с MVVM бы очень хорошо, чтобы было понятнее. И что делать, если UserRepositoryImpl данные берет из сети. У Вас он хардкодом
Если у вас сеть, то вы просто будите дергать http запросы оттуда, или сделаете дополнительно какой-то класс для запросов, по принципу стораджей. А хардкодом, потому что это слишком простой пример, что бы показать еще и работу с сетью. Полноценный пример позже будем делать.
Тимофей, при использовании UseCases, может ли репозиторий иметь методы обновления данных в репозитории, например поход в локалку, проверка наличия данных и в случае их отсутствия поход на удаленный источник, с сохранением полученных данных в локалке? Или все таки репозиторий должен строго иметь тупые методы сохранения и извлечения данных, а вся логика походов в источники данных уже в UseCases?
То, что вы описали, как раз и является тупыми методами репозитория). То есть это ответственность репозитория откуда и как брать данные. Но в зависимости от ситуации, иногда этим управляют и из UseCase
С чего вдруг presentation может знать о data слое? У вас тем самым образуются горизонтальные связи. Presentation может знать в лучшем случае о интерфейсе репозитория который лежит в domain слое. Так же у вас почему-то в domain слое логика синхронизации данных - когда вы сначала получаете данные, проверяете изменились ли они и если нет, то ничего не сохраняете. Это не бизнес логика, чтобы она была в domain слое. Логично будет вынести ее в репозиторий, и там уже настраивать политики синхронизации. В domain слое должна быть бизнес логика и только.
Отличное видео! Однако возник вопрос каким образом можно реализовать получение данных из различных источников? Например, мне в проекте нужно получить свежие данные с API, но если это не получилось необходимо отобразить данные из локального хранилища. На сколько я понял, в репозитории я должен создать два метода, например getUserFromAPI в котором происходит получение данных с API и getUserFromDb в котором происходит получение данных из Db. А саму логику с if(getUserFromAPI == null){ getUserFromDb } я должен реализовать в Use Case. Поправьте пожалуйста если что-то не так.
UseCase такое не должен делать, это логика получения данных. Тут все очень просто, в репозитории у вас должно быть 2 storage (api и локальная база), и дальше уже в зависимости от задач делаете логику того из какого источника брать данные. Обычно вы дергаете API и если ошибка или данных нет, то идете в локальную базу, либо сначала проверяете локальную базу и если нет данных, то идете в api. "getUserFromAPI" - такие методы делать нельзя, так как UseCase не стоит знать конкретику реализации и откуда пришли данные. Дергая метод в репе нам просто нужны данные, все равно откуда они.
Всем кого смог найти в мессенджерах отметил, если вам не ответил, то наверно у вас номер телефона скрыт от поиска оп нему. В этом случае оставьте ваш ник в телеграме.
Отправка тоже в репозитории. Отправка данных на бекенд, да и вообще все действия с данными по сохранению, получению, отправке - это все в репозитории, и не важно это сохранение в бекенд или в локальную базу.
Правильно ли я понял, что репозиторий выступает неким юзеркейсом для получения данных. В нем нет конкретного процесса получения но есть логика "условий" почему, откуда и зачем. Конкретное решение получения данных должно быть в сторедже. Вопрос, как определить абстракцию для репозитория, ведь он может раздутая до многих десятков функций. И когда надо создавать другие репозитории и надо ли. Спасибо большое.
Получение данных можно сделать как в репозитории, так и в сторадже, все зависит от того насколько у вас большое приложение. По поводу определения абстракции, как правило репозиторий делает CRUD операции с конкретной моделью. Например, у вас есть модель User, значит у вас будет UserRepository, который делает различные манипуляции с User. Или например для процесса авторизации у вас может быть AuthRepository с методами login, register, forgot password и logout. Один репозиторий под конкретную задачу.
Сначала подумал какой смысл от модуля data, если данные (models) хранятся не в нем а в domain. А потом снял ручник и вспомнил что models это не данные а типы данных. Получается что domain предоставляет логику взаимодействия модулю data с другими модулями, то есть domain, не зная о других, говорит как им взаимодействовать. Presentation показывает что-то, используя логику предоставляемую domain. Data отдает и получает данные, используя логику предоставляемую domain. Так или не так?
@@TimofeyKovalenko ну да, просто на уровне data я привык что это entities . Но сути не мешает :) я посмотрел потом след. Видео понял что ты имел ввиду.
Думаю, автор не захотел усложнять начало, чтобы не отпугнуть и не запутать начинающих p.s одно из следующих видео как раз посвящено разделению слоев на модули
Так в этом же и смысл), пока пишешь, набиваешь руку и становится все понятнее. Вообще не представляю даже, как можно научиться просто посмотрев ролик без написания кода.
Тимофей добрый день. override fun saveName(saveParam:SaveUserNameParam):Boolean{ Log.d("MyLog", "Save Param - ${saveParam.name}") sharedPreferences.edit().putString(KEY_FIRST_NAME, saveParam.name).apply() Log.d("MyLog", "Saved - ${sharedPreferences.getString("KEY_FIRST_NAME", "")?:""}") return true } В первом логе name отображается, но при вызове второго лога getString ничего не отображает. Попробовал реализовать тоже самое в новом проекте, но только, просто в активити, все работает. Подскажите, пожалуйста, в чем может быть проблема?
СОДЕРЖАНИЕ:
00:00:00 - что такое слой DATA
00:00:33 - кратко про предыдущий урок
00:03:17 - диаграмма Clean Architecture
00:05:09 - паттерн Repository на практике
00:09:19 - правильная связь Domain c Data
00:22:09 - пишем внутрянку для Repository
00:33:18 - кратко о том, что мы сделали в уроке
00:35:17 - подводим итоги и ставим лайки :)
На канале также есть и другие уроки по Android Clean Architecture.
Да, подправлю это.
боже храни тебя Всевышний за то, что ты умеешь так грамотно подходить к материалу и подавать его!
🔥🔥🔥 какая же это сложная тема и как все становится понятно после твоих роликов, долгих лет жизни, святой ты человек❤
Оуууууу еееее! Ну наконец-то дядю Боба кто-то доходчиво объясняет в ютубе!!! Жду модульность, тесты, маппинг, асинхронщину и MVVM :D
Чел, поддерживаю, очень этого жду, не хочешь списаться в телеге, может быть пообщаемся об андроиде, если будет интересно, пиши - @kostiggig
@bitmap Да, Боб Мартин. У него помимо "Clean code" есть статьи по архитектуре, собственно откуда все эти слои и пошли а также его книга "Clean architecture". Также есть адаптированный вариант для андроида у Фернандо Седжаса.
Как глоток свежего воздуха. Честно. Спасибо!
Тимофей, уважаемый Тимофей. Огромное вам спасибо. Вы великолепный преподаватель! Потрясающе интересные уроки. Желаю вам и вашим родным, крепкого здоровья и настоящего счастья!
Супер объяснение! Все ваши видео в захлеб смотрю, до этого сколько не смотрел по чистой архитектуре, ничего не понятно было - с вашей помощью со всем разобрался!
автора приятно слушать. последовательный курс будет отличным практическим дополнение к печатной литературе по теме. спасибо
мало лайков, поднажмите! это одно из лучших описаний чистой архитектуры :)
Большое спасибо ! С интерфейсом и двумя репозиториями, от души !!! Очень ценный материал !
Тимофей, как обычно, Ваши видео просто супер. Для начинающих разработчиков Clean Architecture одна из самых тяжёлых и непонятных тем. Жду продолжения. Удачи!
Спасибо большое за такое понятное объяснение!)
Отличнейший материал ! Благодарю
Спасибо большое за ваша видео, очень помогает! В начинаниях в Android Development
😀
Очень классно разжевано! Продолжайте еще!
Очень доходчиво объясняете , диаграммы сильно помогают, очень рад что попал на этот канал
Спасибо Вам огромное! Вы лучший!
Спасибо. Лучшего ресурса по архитектуре я еще не встречал
+++ Плюсую. Все остальные видео которые я смотрел по архитектуре андроид приложений, там просто авторы говорили "Бла-бла-бла, тут короче Domain в Domain-е хранится вот это и там короче кроме Domain есть Presentation и Data. Конец". А в видеоуроках от Тимофея, именно создаешь вместе с автором, приложения используя Clean Architect на практике и после уроков откладывается в голове, что, как работает и что вообще такое Clean Architect
Уроки просто супер!!! Лайк и ждем продолжение :)
Большое спасибо за подробное объяснение!!!
Господи, хочу сказать спасибо за такой подробный разбор, огромное спасибо) лайк подписку оставил)
Искал в ютубе видосы другого Тимофея, и нашёл вас. Но рад этому, у вас всё тут так классно объясняется, спасибо вам!
Ну очень круто!!!! Тимофей, спасибо вам за шикарные и очень понятные для новичка уроки!!!
Бесподобные примеры и объяснения. От души благодарю!
Очень, очень большое спасибо. Прям всё разжевано до мелочей. Большое спасибо
Спасибо за объяснение! Давно хотел узнать, как грамотно сделать архитектуру приложения. А то чем больше оно растет, тем больше беспорядка :D
Лайк! Хотим больше разных видео! У вас талант объяснять.
Спасибо ;).
Огромное спасибо за урок. В моменте с объяснением элвис оператора 28:10 используется строка с firstName и параметр DEFAULT_NAME который создан для lastName. (Лично мне понятно, но тем кто видит ?: первый раз может показаться что-то искаженное =)
Извиняюсь если уже подсвечивали ранее или если это избыточный комментарий. В любом случае урок потрясающий!
Спасибо, отличная подача материала.
Это оказывается так легко и просто 🤗👍 спасибо большое за отличные материал. Продолжаю обучение
Тимофей, это блестящий урок!
Большое спасибо за информацию, все очень доступно и по полочкам, безумно благодарен.
Это божественно, спасибо)
Все подробно и понятно, благодарю!
Ваши уроки просто замечательные, пожайлуйста продолжайте радовать нас=)
Продолжайте пожалуйста делать видео по clean architecture with Android, очень полезные видео
спасибо, приятно что видео помогают вам. Конечно буду продолжать ).
Очень понравилась серия видео, доходчиво и наглядно! Одно только смутило, если слой domain ни про кого не знает, почему от него идут стрелки зависимостей? Разве не другое направление у них должно быть, от data и presentation к domain.
Отличный видеоурок и вообще курс, понятно и интересно, большое спасибо!😀
Спасибо за видео и грамотную подачу
Спасибо за видео.Коммент в поддержку!
Спасибо)
Очень здорово! Спасибо большое! Жаль, что Вы перестали выпускать видео..((
До просмотра этого видео в голове была каша. Разложил по полочкам. Огромное спасибо!
И хотелось бы увидеть как правильно реализовать синхронизацию данных между web api (или firebase) и room)))
господи, спасибо ВАМ за эти шикарные и понятные, составленные на русском (а не девелоперском) языке объяснения. Мне кажется, что по вашим урокам можно аутиста клин архитектуре научить
Спасибо)
ставлю лайк . Надеюсь автор не забьет на канал .
Шикарные уроки!!!
Thank you for this very nice video. It helped me. I think that it is one from better explanation
класс, спасибо вам большое!) Лайк и подписка однозначно!)
Скажите, а планируются уроки по таким темам как ретрофит, корутины, рум? Думаю очень полезным был бы ролик с практикой на каком-то маленьком демо-проекте, но где бы сочетался такой стек ?)
Да, эти темы тоже будут.
Спасибо за урок, все очень доходчиво и понятно ! По твоим видео понял суть чистой архитектуры на практике
У меня вопрос: Нужно ли помешать логику в отдельный usecase если usecase не взаимодействует с датой через интерфейс, а возвращает некий объект описанный в слое domen ?
Было бы отлично если бы уроки по чистой архитектуры выходили и дальше, которые затрагивали бы и другие примеры которые очень часто встречаются на практике например: использование mvvm, recyclerView, взаимодействие с бэком в чистой архитектуре.
Также не совсем по теме, нигде не смог найти внятное объяснение и пример использования DI (Dagger 2) в MVVM (Передача зависимостей во ViewModel + зависимости с контекстом)
P.S спасибо еще раз, прошел собес -_-
Спасибо за отзыв! Конечно буду продолжать, еще много планов на новые видео по архитектуре.
По поводу помещения логики в usecase, нет таких правил что бы usecase обязательно зависел от даты, в домен выносится вся логика, даже та которая просто что-то делает но не общается в data слоем.
Спасибо большое
Спасибо
16:55 сделайте пожалуйста видео с примером несколько реализаций
Это уже ответственность DI, библиотеки разные могут быть, да и придется уже серьезно в конкретную библиотеку залазить. Глубоко по DI снимать пока плана нет.
Супер
А пример со связью можно? Интересно узнать про вложенность объектов.
Юзкейсы позволяют переиспользовать логику работы с репозиториями в UI слое. Т.е. когда у вас вызовы репозиториев - это например сложные цепочки и используются в разных местах.
Это мне кажется первая причина, почему не используются репозитории в UI.
Да, совершенно верно.
БОМБАА
Все здорово! Спасибо!
Помогите, плз, понять, почему на схеме Presentation знает про Data напрямую? В коде данного видео Activity знает только про UseCases. Разве в клин архитектуре можно "обходить" домен слой?
Да, presentation сам по себе не должен иметь доступ к data, только к use cases. Но, presentation в нашем случае находится внутри модуля app, а app занимается конфигурированием приложения и должен знать о всех, поэтому технически доступ к data возможен. Что бы избежать можно вынести presentation в отдельный модуль, а лучше разделять его на feature модули, про них планирую рассказать.
И благодарю
подскажите с контекстом в jetpack compose . Все работает без by lazy . Правильно ли делать так:
val context = LocalContext.current
Здравствуйте, очень хотелось бы получить объяснение касательно presentation слоя. В видео вы сказали что presentation слой может знать и об domain и о data.
Тогда как все мои знакомые это отвергли и сказали что напрямую presentation слой не должен знать о data.
Так как на этом видео можно писать только в случае ну крайне быстрой и ленивой реализации, в очень простой бизнес логике.
Presentation работает только через domain с data и друг о друге они знать не должны.
Можно только подцепить даггерчкий элемент, но не более того, так как в видео делать - все сказали что нельзя, это нарушает принцип инверсии зависимости.
Во первых я в видео много раз говорю, что я даю вам фундаментальные принципы, на основе которых, вы уже дальше развиваете свой проект. Если бы я давал сложный пример сходу со всем функционалом, то мало кто разобрался бы с этим. Мои видео все же делаются для моих студентов, которые только начинают изучать программирование.
Во вторых: "презентейшен" слой не знает о "дата", это правда, но "app" знает, так как этот модуль является точкой входа в приложение и нам просто больше негде инициализировать все зависимости: "домен" и "дата". И, "app" внутри себя содержит "presentation", соответственно "presentation", технически имеет доступ к этим зависимостям, но логически мы используем только домен.
Решается это путем разделения "app" и "презентейшен" на разные модули, либо использование фича модулей.
Так же посмотрите видео, где разбирается DI, тогда станет понятно.
👍🏻👍🏻👍🏻
Здравствуйте Тимофей, хотел спросить, GetUserNameUseCase и SaveUserNameUseCase принимают объект интерфейса UserRepository, но в MainActivity вы передали объект UserRepositoryImpl
Да, все верно. Полиморфизм ;)
Тимофей здравствуйте, вот вы говорите что в data не может быть никакой логики, у меня тогда вопрос может ли репозиторий управлять источником данных, а конкретно может ли быть там логика выбора источника данных в зависимости например от наличия интернет подключения? Или например из UseCase дергаем список фильмов а репозиторий решает какой из методов апи дернуть для получения списка фильмов, например если их несколько но есть условия их получения?
Может, так как смысл data это как раз таки и работа с данными. Но тут сильно зависит от ваших потребностей в бизнес логике, можно и так и так делать.
Спасибо больше за видео, очень интересно и понятно рассказана тема!
Подскажите пожалуйста, что означает фраза "это класс, который делает корут операции" по отношению к репозиторию? Что такое корут операции?
Возможно корутину имел ввиду. Напишите в каком конкретно месте такое говорил.
Здравствуйте. Спасибо за видео и труда.
У меня есть вопрос по поводу applicationContext. Сначала вы запустили программу. Там написал, что applicationContext on a null object reference. А когда вы писали by lazy он работал. Это означает, что applicationContext создаётся внутри функции onCreate?
Да, с этой активити стартует приложение и applicationContext просто еще не успел создаться.
спасибо
Спасибо мне это очень помогло
Тимофей, замечательное объяснение, вы случайно не читали статью на медиум по этой теме?
:), очень много читал, особенно когда только начинал заниматься программированием. Сейчас уже меньше, опыт уже значительно выше чем большинство статей.
Спасибо за доходчивый разбор данной темы, в ру сегменте я такого еще не встречал. Вопрос у меня возник такой, пишу приложение в котом есть большое количество фоновой работы, WorkManager который запускает foreground сервисы, в каком из слоев будет правильно размещать подобный функционал? Мне вот кажется, что нужно выносить в совершенно отдельный модуль или в presentaition , но интересно мнение профессионала.
Если WorkManager используется только для синхронизации данных, то без проблем можно положить его в data слой. Data ведь как раз и предназначен для работы с данными. Выносить в отдельный модуль я бы не стал, только если необходимо сделать универсальную реализацию, которая будет использовать в нескольких проектах.
А вообще тема синхронизации данных очень обширная, тут много нюансов. Думаю про эту тему тоже сделаю отдельную серию видео.
Очень круто!
А скажи пожалуйста, вот у нас в домене есть модели и есть юзкейсы которые их берут на вход или выход. Нужно ли использовать для них интерфейсы или это избыточно?
Спасибо
Избыточно.
Но тут на вкус и цвет, кто как делает. Лично я считаю, что стоит делать только если это реально решает, какую-то проблему. Делать просто для красоты или потому, что так написано в умной книжке нет никакого смысла.
Тимофей привет!
У меня такой вопрос:
Как прогнать LiveData через domain, если в этом слое нельзя импортировать данную библиотеку?
Просто у меня есть usecase: getMoviesFromApiUseCase. В app и в data я сохраняю всё и использую в виде LiveData, но сделать прогон через usecase в domain слое я не могу. Как поступить, если сохранять Clean Architecture?
Никак). Либо смириться с тем, что у тебя в domain будет подключен LiveData. Если используешь корутины, то советую поменять LiveData на Flow.
Может ли репозиторий хранить в себе методы генерации данных, например генерация вопроса generateQuestion, реализацию этого метода из репозитория я добавляю в RepositoryImpl в data слое и генерирую вопрос на основе числа, которое ввел пользователь, будет ли это ошибкой? Возможно в GenerateQuestionUseCase даже не нужно передавать репозиторий, а можно сразу написать генерацию вопроса, это же вроде как бизнес логика, не требующая доступ к БД.
Эти вопросы ведь генерируются из какого-то списка? Откуда они берутся? Соответственно данные, на основе которых происходит генерация, где-то хранятся. Я бы в репозитории это делал.
@@TimofeyKovalenko эти вопросы генерируется во время работы приложения на основе данных, которые подает пользователь прямо во время работы приложения, затем выводятся на экран, после перезапуска забываются и генерируется снова
Репозиторий хорошее место тогда.
я вот или понял или нет) не могу понять)....... я так понял, это все "фокусы", посути, все даные храняться в одной компосибл функции (хмл не знаю, юзаю компос), я долго пытался понять, как даные куда передаються, но в этоге пришол к мнению, что не чего не куда не передаеться, а храниться тут:
val userRepository = UserRepositoryImpl(context = LocalContext.current )
val usecaseGetName = UserCaseGETname(userRepository)
val useCaseSaveName = UseCaseSaveName(userRepository)
я прав?
27:01 Если всё равно ставите элвиса, тогда делайте дефолтовое значение в параметрах null и не надо будет два раза указывать дефолтное значение.
👍
Спасибо, очень интересно. Но вот урок с MVVM бы очень хорошо, чтобы было понятнее. И что делать, если UserRepositoryImpl данные берет из сети. У Вас он хардкодом
Если у вас сеть, то вы просто будите дергать http запросы оттуда, или сделаете дополнительно какой-то класс для запросов, по принципу стораджей. А хардкодом, потому что это слишком простой пример, что бы показать еще и работу с сетью. Полноценный пример позже будем делать.
Тимофей, при использовании UseCases, может ли репозиторий иметь методы обновления данных в репозитории, например поход в локалку, проверка наличия данных и в случае их отсутствия поход на удаленный источник, с сохранением полученных данных в локалке? Или все таки репозиторий должен строго иметь тупые методы сохранения и извлечения данных, а вся логика походов в источники данных уже в UseCases?
То, что вы описали, как раз и является тупыми методами репозитория). То есть это ответственность репозитория откуда и как брать данные. Но в зависимости от ситуации, иногда этим управляют и из UseCase
@@TimofeyKovalenko спасибо за ответ Тимофей!
С чего вдруг presentation может знать о data слое? У вас тем самым образуются горизонтальные связи. Presentation может знать в лучшем случае о интерфейсе репозитория который лежит в domain слое.
Так же у вас почему-то в domain слое логика синхронизации данных - когда вы сначала получаете данные, проверяете изменились ли они и если нет, то ничего не сохраняете. Это не бизнес логика, чтобы она была в domain слое. Логично будет вынести ее в репозиторий, и там уже настраивать политики синхронизации. В domain слое должна быть бизнес логика и только.
Отличное видео! Однако возник вопрос каким образом можно реализовать получение данных из различных источников? Например, мне в проекте нужно получить свежие данные с API, но если это не получилось необходимо отобразить данные из локального хранилища. На сколько я понял, в репозитории я должен создать два метода, например getUserFromAPI в котором происходит получение данных с API и getUserFromDb в котором происходит получение данных из Db. А саму логику с
if(getUserFromAPI == null){
getUserFromDb
}
я должен реализовать в Use Case. Поправьте пожалуйста если что-то не так.
UseCase такое не должен делать, это логика получения данных. Тут все очень просто, в репозитории у вас должно быть 2 storage (api и локальная база), и дальше уже в зависимости от задач делаете логику того из какого источника брать данные. Обычно вы дергаете API и если ошибка или данных нет, то идете в локальную базу, либо сначала проверяете локальную базу и если нет данных, то идете в api.
"getUserFromAPI" - такие методы делать нельзя, так как UseCase не стоит знать конкретику реализации и откуда пришли данные. Дергая метод в репе нам просто нужны данные, все равно откуда они.
Тимофей привет!
Есть ли у тебя сейчас возможность взять на обучение?
Оставил на твоем сайте заявку, но без ответа.
Привет! Все заявки есть, но не все еще обработал. В этом году обучение не проводиться, есть планы на начало следующего года.
@@TimofeyKovalenko отлично жду!
Благодарю за видео, просто💣
Всем кого смог найти в мессенджерах отметил, если вам не ответил, то наверно у вас номер телефона скрыт от поиска оп нему. В этом случае оставьте ваш ник в телеграме.
@@TimofeyKovalenko Тимофей, спасибо, уже добавили в чат ждунов😄
Можно ли использовать flow в domain модуле?
Конечно, корутины, как и RX - глобальные либы, которые пронизывают все слои.
Не понял, репозиторий только для для получения и сохранения данных? А отправка данных это не в репозитории?
Отправка тоже в репозитории. Отправка данных на бекенд, да и вообще все действия с данными по сохранению, получению, отправке - это все в репозитории, и не важно это сохранение в бекенд или в локальную базу.
Правильно ли я понял, что репозиторий выступает неким юзеркейсом для получения данных. В нем нет конкретного процесса получения но есть логика "условий" почему, откуда и зачем. Конкретное решение получения данных должно быть в сторедже. Вопрос, как определить абстракцию для репозитория, ведь он может раздутая до многих десятков функций. И когда надо создавать другие репозитории и надо ли. Спасибо большое.
Получение данных можно сделать как в репозитории, так и в сторадже, все зависит от того насколько у вас большое приложение. По поводу определения абстракции, как правило репозиторий делает CRUD операции с конкретной моделью. Например, у вас есть модель User, значит у вас будет UserRepository, который делает различные манипуляции с User. Или например для процесса авторизации у вас может быть AuthRepository с методами login, register, forgot password и logout. Один репозиторий под конкретную задачу.
@@TimofeyKovalenko Спасибо большое
Очень сильно бьёт по глазкам смена тёмной темы студии на белый лист дровио(
Может, в дровио тоже можно тему потемнее выбрать?)
Возможно есть темная тема, посмотрю.
Сначала подумал какой смысл от модуля data, если данные (models) хранятся не в нем а в domain. А потом снял ручник и вспомнил что models это не данные а типы данных.
Получается что domain предоставляет логику взаимодействия модулю data с другими модулями, то есть domain, не зная о других, говорит как им взаимодействовать.
Presentation показывает что-то, используя логику предоставляемую domain.
Data отдает и получает данные, используя логику предоставляемую domain.
Так или не так?
Да, все верно.
Спасибо за урок! Но не очень понятно зачем выносить константы из класса, это сделано осмысленно или для удобства чтения? (25 минута)
Потому что это котлин, в классе особо вариантов и нет что бы сделать константу.
Please show mvvm + clean architecture with Koin
Sure, it is plan for next videos
@@TimofeyKovalenko okey and please a case with onTextInputlayout validations how to implement in mvvm and clean architecture because it is tricky
@@TimofeyKovalenko Thank you sir
Странно что модели на уровне domain, вроде как они должны быть на уровне data
Ничего странного, есть модели на уровне domain и есть модели на уровне data, это разные вещи.
@@TimofeyKovalenko ну да, просто на уровне data я привык что это entities . Но сути не мешает :) я посмотрел потом след. Видео понял что ты имел ввиду.
почему все слои в одном модуле, а не в отдельных?
Думаю, автор не захотел усложнять начало, чтобы не отпугнуть и не запутать начинающих
p.s одно из следующих видео как раз посвящено разделению слоев на модули
как тут поставить 200 лайков 🤔
неплохо было бы на гит выкладывать, очень долго переписывать код, жесть. Мне проще сразу смотреть и тыкать чего и откуда.
Так в этом же и смысл), пока пишешь, набиваешь руку и становится все понятнее. Вообще не представляю даже, как можно научиться просто посмотрев ролик без написания кода.
Тимофей добрый день.
override fun saveName(saveParam:SaveUserNameParam):Boolean{
Log.d("MyLog", "Save Param - ${saveParam.name}")
sharedPreferences.edit().putString(KEY_FIRST_NAME, saveParam.name).apply()
Log.d("MyLog", "Saved - ${sharedPreferences.getString("KEY_FIRST_NAME", "")?:""}")
return true
}
В первом логе name отображается, но при вызове второго лога getString ничего не отображает.
Попробовал реализовать тоже самое в новом проекте, но только, просто в активити, все работает.
Подскажите, пожалуйста, в чем может быть проблема?