Как стать автором
Обновить
10
0
Некрутов Эдуард @Not_coolEd

Senior android engineer

Отправить сообщение

И все же я никак не могу найти время на реализацию демо проекта. Все время про это вспоминаю. Много изменений в жизни, которые совершенно не оставляют на это времени. Возможно когда-то, но не сейчас...

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

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

Приемлемый вариант, это одно кастомное исключение, которое внутри содержит код ошибки. И по этому коду внешние разработчики должны обрабатывать эти исключения. А коды должны быть зафиксированы в документации, чтобы внешние разработчики их хорошо понимали. Как пример, коды REST ответов. 404 это код ошибки, который знают все. Но ни кто не завязывается на текст Not found.

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

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

Хотелось бы верить, что у человечества были рациональные причины, но верится с трудом) Можете загуглить первые чемоданы от луи виттон, они так и просят приделать к ним не менее маленькие колеса)

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

Много вопросов в одном комменте) Давайте по порядку)

Entity

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

Я предлагаю оперировать только понятием структура данных, которая при этом не является ядром системы. Структуры данных изменчивы и не постоянны. А вот правила работы со структурами, являются более стабильными частями, которые меняются реже. Наглядным примером является мак. Была одна система с сущностями в виде бигмака и прочего. Теперь это другая система с теми же сущностями и правилами, но структуры данных оказались изменены. Терминалы, стойка выдачи заказа, кухня и прочее никак не изменились. Только бигмака больше нету. Есть что-то похожее, с измененным названием и составом. Но правила работы с "этим" не изменились. Поэтому entity не является ядром системы и системы не стоит проектировать вокруг них.

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

Слабый архитектурный контроль

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

Ультимативные заявления

Я рад, что они царапают глаза) Именно этого эффекта я и пытался добиться)
Они являются ультимативными, чтобы уменьшить возможность для споров без аргументов. А если у вас есть весомые аргументы того, что что-то можно сделать по другому, в каких-то случаях, то вы уже являетесь опытным проектировщиком, который в состоянии сделать хорошую архитектуру для этих конкретных случаев. И тут я уже буду рад выслушать эти аргументы, вместе спроецировать их на абстрактную систему, которая опишет большинство случаев, и внести правки в статью и схему. Так что если есть такие аргументы и предложения по улучшению, буду очень раз их услышать или даже созвониться и обсудить)

Разбиение на модули

К сожалению, я полностью с вами согласен в этом вопросе. Обычно говорят как правильно разбить, а не о том зачем. Если вдуматься в вопрос "зачем", то мы получим примерно такой ответ: "выделение частей программы в отдельные самостоятельные части, для облегчения восприятия кода и улучшения контроля над сложностью". Отличная формулировка благих целей. Вот только: "Для моего маленького проекта, это совсем не нужно, поэтому я буду делать все в монолите. Я же не бигтех)"

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

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

При этом цель разбиения по контексту, решается на более раннем шаге, при определении зон ответственности и выделении сущностей. Вам остается только объединить сущности одного типа в модули с общим, конкретно для вашего проекта, контекстом.

Если у вас есть предложения по улучшению каких-либо формулировок или блоков в статье, я буду крайне признателен, и обязательно их рассмотрю и внесу исправления в статью)

Мне не были знакомы принципы FSD, так что предки разные) Постараюсь подробнее почитать и найти сходства, а пока отвечу так.

Основное заимствование для статьи это шаблон mvvm и принцип разделения на слои из чистой архитектуры. При этом он дополнительно переосмыслен во фрактальное представление зон ответственности. Все остальное описано и спроектировано без дополнительных источников или ссылок на что-либо)

Касаемо офф доки гугла. Там написано, что domain слой является опциональным. Я в корне не согласен с этим утверждением, поэтому эту статью можно считать противоречащей официальной документации от гугл. Я этого не отрицал и даже упомянул про это утверждение в описании одной из картинок, про разделение на слои.

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

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

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

Я так и не понял как связаны ресурсы и библиотека)

Работа с ресурсами происходит на View, но при этом ViewModel может знать про существование ресурсов, их id и возможно способ как их достать,

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

Пока что я ничего не предлагаю, потому что не понимаю вопроса. Раскройте, пожалуйста, вопрос более подробно, чтобы я мог предложить вам какое-либо решение)

Отличный вопрос!) Чисто физически можно сделать и так и так. Однако, сложив все в один модуль, мы теряем преимущества модуляризации. Разбив модули по 1 интерфейсу, мы создаем излишний оверхед.

Лучшим критерием объединения модулей является общий контекст. Например:
data-api которые возвращают Chat и Message, лучше объединять, потому что это связанные сущности. Однако feature-api по работе с чатами, можно отделить от feature-api по работе с сообщениями, так как использоваться они будут в разных контекстах. При этом мы не отменяем того, что в контексте чата мы можем возвращать список чатов вместе с последним сообщением.

Только начал, поэтому не судите строго) И пока особо нету времени активно продолжить, но обещаю, что не заброшу) https://github.com/NekrutovEd/EasyFinance

Доступ к контексту есть только в 2 местах.
AppContext - при создании графа зависимостей, чтобы создавать различные библиотечные зависимости.
ActivityContext - на View, чтобы достать что-то из ресурсов. Можете почитать о том, как делаются обертки Text, которые получают значения из ресурсов при установке во View, или про то, как делают ResourceWrapper, для ViewModel.

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

В данном случае, мы можем вынести в отдельный модуль, на уровень common, view для нашего списка и модель этого списка. Они связаны 1к1. Маппинг мы не сможем сюда вынести, потому что это уровень common, а связи снизу вверх запрещены.
В 2 разных screen модулях мы можем зависеть от одних и тех же feature-api и common-my-list-ui.

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

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

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

Большое спасибо за обратную связь, я старался)

А где этот enum лежит? Где и для чего используется? Я не очень понимаю условия вопроса. Можете, пожалуйста, предоставить более подробное описание проблемы?

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

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

Нужно понимать объем приложения. Проблемы скорости сборки ни кто не отменял для больших приложений. И одно из решений это разбиение на маленькие модули по принципу api/impl, чтобы завязка была на интерфейсы, которые имеют гораздо меньший коэффициент изменчивости, по сравнению с реализацией.

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

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

В рамках текущей статьи это не имеет значения, поскольку модули feature level имеют право зависеть от публичных модулей из уровней ниже. А управление зависимостями и инициализации происходят на Application уровне.

Информация

В рейтинге
Не участвует
Откуда
Москва и Московская обл., Россия
Дата рождения
Зарегистрирован
Активность

Специализация

Mobile Application Developer, Software Architect
Lead
Java
Kotlin
Android development
Clean Architecture
DDD
Designing application architecture
OOP
JavaScript
C#
Scrum