на счет VueEnt обязательно посмотрю поизучаю в свободное время, а про оверхед - все зависит от проекта конечно же в целом сама по себе чистая архитектура выглядит громоздкой и для небольших проектов на месяц-два они и будет являться громозкой это как взять кувалду чтобы забить гвоздик на шкафичк а для большого неповоротливого проекта будет дешевле заниматься неким "оверхедом" чем потом заниматься разгребанием всего этого есть много ньюансов которыми очень часто пренепрегают например в идеальном мире все действия во вью модели должны проходить только через use cases и не тянуть репозитории и в 99% случаев это как раз уже будет лишним проще достать репозиторий во вью модели и использовать его
и таких ньюансов достаточно чтобы упроситить себе жизнь
тут уже вопрос в любом случае, как и с любым масштабом приложения и без разницы какая архитектура, в экспертности разработчика, нужно понимать где можно "схалтурить" без последстивй, а где лучше не стоит
а на счет больших бандлов тут добавлю что такой подход к архитектуре дает большой потенциал по разбиению бандла на чанки с большей гибкостью а если вы говорите про то что приходится при таком подходе писать больше кода то тут уже нужно тоже смотреть на потребности вашего продукта в данном случае при чуть большем количестве кода лучше идет работа с количеством памяти который используем, не на столько сильно нагружем garbage collector за счет di и view model
поэтому в любом случае нужно смотреть потребности проекта и рассматривать все за и против архитектуры для продукта в моем случае уже как будто фича новая добавляется не дольше а может и быстрее чем с fsd архитектурой всетаки дело привычки тоже никто не отменял к хорошему быстро привыкаешь))
EDIT: добавлю еще что в целом нужно понимать нужны ли вам абстракции. в моем случае приложение так же используется на разных платформах и нужна безболезненная подмена реализаций и тут уже получается что для меня это базовый минимум а в случае с другим проектом это может быть не базовым минимумом а раскошным максимумом (по типу "давайте затащим чистую, чтобы если вдруг мы потом захотим поменять axios обратно на fetch то было просто")
В нормальном ЯП думаю медиатор более чем хорошее решение)
Но тут js без множественного наследования) ввше я приводил пример с Clearable. Похожая ситуцаия. Хотел сделать через наследование, по факту получился бы тот же самый медиатор, но отказался от этого, так как мы в репозитории должны отнаследоваться от ее абстракции а места для наследования от "коллеги" нет
Попробовал поискать реализацию миксинов в js. Понял что лучше в такие дебри не лезть)
На счёт бить по рукам сложная тема. Как выше упомянул если это слишком частая проблема, возможно есть смысл действительно на уровне линтера запретить использовать на прямую дата слой. Да прижется простые сеттеры писать, но тут уже смотрите что больше проблем приносит. К сожалению другого выхода мне кажется нету. Либо добавляем четкое правило, либо терпим :)
В иммутабельном аодходе все просто и прозрачно. В мутабельном же подходе подходе приходится за всем следить. Думать как работать со сложенными сущностями. К иммутабельному подходу у меня появились негативные впечатления еще давно пока я работал на flutter, там как раз мы использовали mobx. Да и в иммутабельном варианте все измнения как раз прозрачны. И мы не можем просто взять где нибудь написать user.role.name = "ADMIN", то что вы как раз упоминали.
Помимо этого мутабельный подход в целом дороже (в плане ресурсов) мы должны постоянно следить за изменениями и уведомлять при необходимости. При добавлении декоратора для экшенов и тем более computed значений, каждый раз нужно за всем следить чтобы обновлять или же не обновлять) этим всем занимается mobx.
Детерминированность это всегда и везже хорошо. А мутабельность это постоянные сайд эффекты скажем так
Самописный di изначально была задумка как вызов для себя. "Чем я хуже других, не смогу свой написать не менее хороший что-ли?". И потом уже в последствии методом проб и ошибок на своем же опыте пришел к тому что сюда функционал изоляции был бы очень даже кстати. Как по мне такой подход к обучению самый эффективный. Как минимум пока я это делал очередной раз убедился что js та еще штука :) (в плохом смысле)
Ну вообще мы в целом не можем взять и написать user.name = "Alex" У flow в даннос случае нет сеттера Мы можем делать только emit новых данных Тут подход идет иммутабельности Новое состояние, новый объект
что мы и можем видеть в методе update для MutableStateFlow мы передаем данные которые нужно обновить и рестом собираем новый объект с пред данными и заменяем нужные поля новыми данными
а на счет запрета использования репозитория данных в обход use case, не совсем понимаю зачем, и не совсем понимаю чем может мешать изменение данных не через use case
use case нужен если мы хотим хранить кэш, дополнительно отправлять метрики, или чтото подобное Если мы делаем todo list который работает локально и нам просто нужно без каких либо дополнительных действий изменить checked, то для этого можно забрать репозиторий прямо во вью модель и дергать метод toggleChecked допустим
ну и если я правильно понял то вы подразумеваете под моделью сам репозиторий? тоесть источник данных для какойто сущности но у сущности может быть несколько источников данных например тот же самый todo Мы делаем репозиторий для работы с тудушками на удаленном сервере И так же репозиторий для работы с ними в локальном хранилище
Вот тут то нам и нужен use case, например ToggleTodoCheched который будет работать например по принципу optimistic. Изменит локально сразу же состояние, и отправит запрос на сервер. Если успешно то все хорошо, в локальном хранилище уже изменили, если не успешно то откатили
похожим образом можно сделать работу оффлайн сохранять данные локально при отсутсвии интернета при появлении сети сделать реализацию синхронизации
вот реальные примеры для use cases
если нам нужно просто получить accsess token из репозитория токенов, то можно взять репозиторий и вытянуть оттуда данные
если нам нужно просто закинуть новую пару ключей, то так же можно сделать на прямую через репозиторий если мы хотим закинуть пару ключей, но при этом сохранить refresh token в локальное хранилище, правильно будет уже use case который это будет делать
все что влияет на бизнес логику мы выносим в use cases
если же вы имеете ввиду что у нас есть use case для того чтобы ставить новую пару токенов, а разработчик решил идти на прямую в репозиторий, то тут уже линтерами не спастись. Это уже на совести разработчика. Перед тем как чтото делать в любом случае нужно изучить что уже написано, не зависимо от архитектуры. Тут же удобно тем что весь доменный слой просмотреть не трудно. Доменный слой полностью описывает работу приложения. Если по доменному слою не понятно что должно происходить возможно чтото не так.
если это на столько частый кейс в проекте, то возможно имеет смысл просто отказаться от прямого использования репозиториев в вью моделях. тут уже линтер вам поможет.
Можете пожалуйста объяснить что вы подразумеваете под менять модель в обход use case? Никак не могу понять что вы хотите спросить Да и в целом наверное еще что вы понимаете под моделью?
Нет, создание use case на каждый чих не обязательно. Use cases это пользовательские сценарии которые мы создаем при необходимости Конечно в идеальном мире хочется для всего создавать, ведь в дальнейшем может понадобиться модернизация, но для обычных сеттеров создавать отдельно use case не стоит view model может использовать репозиторий на прямую (опять же знает исключительно абстракции, реализации закидываем только через di) - и это нормальная практика
но как только появляется момент что вы во вью модели начинаете както дополнительно работать с этими данными (изменять, кешировать, перекидывать в другие репозитории), ну в двух словах появляется как раз бизнес логика, то нужно уже выносить в пользовательские сценарии (use cases)
Я кажется опять не до конца вас понял. Но во первых, дто не становится основой для модели. Дто у нкс лежат исплбчительно в data слое. С dto мы работаем исключительно в рамках слоя данных. Модели с которыми мы работем в доменном слое а в последующем и в слое представления уже описаны в доменном слое - это уже как раз наши бизнес сущности. Слой данных может работать с dto как хочет, на слой выше слой данных отдает именно доменные модели. Такая практика очень часто встречается не только в чистой архитектуре. Все что связано с описанием наших бизнес сущностей находится в досенном слое, и доменному слою без разницы что и как будет делать слой данных, главное чтобы совпадали входные и выходные данные которые мы описали в "контракте" (можель а длменом слое)
А вот на счет изоляции моделей и что нельязя к ним получить доступ обойдя бизнес правила я не понял.
Если нужно при изменении данных в одном месте ревалидировать данные в другом месте, то это ты делаем в use cases. Они как раз и нужны для оркестрации данными, они и описывают пользовательские сценарии.
Но как пример мне привести из реального кейса, нужно было очищать данные со всего приложения на уровне всех слоев при выходе из аккаунта. Решили тем что создаем в core класс Clearable который даёт метод для подписки и метод для очистки. Далее все репозитории которые должны очищаться через di получают и каким либо образом реагируют на эвент очистки
Тут к сожалению приходится самому везде подписываться на событие. В андроиде я бы сделал более красиво через наследовние. Сделал бы класс Clearable от которого наследовался бы и нужно было бы реализовать метод onClear, но пришлось откзааться в js в счет отсутствия миксинов, множественного наследования
Первый вопрос не понял, можете пожалуйста подробнее описать. Не пойму о каких изменениях модели в обход бизнес логики вы говорите.
А на счет взаимодействия между друг другом. Либо у нас связующее app, либо если они связаны, то скорее всего это изначально не имело смысла делить на разные доменные слои. Но бывает и такое что они неизбежно связаны друг с другом. Тогда мы явно указываем в di модулях что они связаны между друг с другом. Это не рекомендованно, но не запрещено. Поэтому и попробовал для себя использовать не готовую DI, а сделать свой вместе с явным указанием зависимостей.
В android разработке такие фичи можно сказать разделяются на отдельные приложения, там создаются отдельные пакеты со своей конфигурацией сборщика который наследуется от основного конфига. И там мы можем настроить зависимости между модулями.
Для более простого понимания можно сделать аналогию с микрофронтами. В большинстве случаев разделение в фичах можно сделать так же как делили бы на микрофронты. В микрофронтах явно указываем откуда что мы тянем.
Ну опять же я и говорил что используется не изначальная концепция чистой, а немного модернизированная Если уж брать изначально как должно быть, то как у меня сделано, папки features не должно быть Есть единый доменный слой всего приложения и все Там вообще никаких проблем не возникает, кому что надо тот то и использует Отдельный features вынесен для более четкого разделения логики При этом тут если же смотреть если мы делаем такое разделение по фичам, то так же как и в FSD кроссимпорты между фичами тоже могут быть. В случае с андроид мы на уровне сборщика указываем зависимости между пакетами. Тут же только если прибегать к настройкам линтера. С разделением на фичи просто удобнее работать с разделением на несколько команд разработки. Ну и в большинстве случаев app слой является связующим, где так же можно хранить логику Тоесть грубо говоря если бы мы убрали слой фичей то был бы в app domain data presentation и там все хранилось бы
везде будут свои сложности. утопии нет) ничего не идеально) чем сильнее усложнять тем больше проблем
тут же чтобы понимать как делить фичи наверное можно взять аналогию с микрофронтов фичи это микрофронты, а app собирает их как нужно
---
размазано в данном случае говорил о том что мы вынесем всю сущность этих уведомлений в слой виджета пропустив банально entity слой что размажет дополнительно логику так мы отходим от стандартной практики разработки по хорошему мы идем снизу вверх сделали утилиты в шаред описали сущность в энтити написали можно сказать юз кейсы в фичах скомпоновали в логические блоки в виджетах собрали в страницы
мы же пропустили тут 2 слоя запихнув все в виджет
---
ну тут core как shared из fsd а app и features тут как написал уже выше можно было бы сделать один доменный слой для всего приложения тут же мы просто разбиваем приложение на отдельные логические блоки а app их объединяет опять же аналогия с микрофронтами
---
по use cases шаблона тут какогото нет мы просто в рамках проекта договариваемся на нейминг если брать документацию андроида там функцию executor'a назвали invoke
---
на счет переподписки в useFlow абсолютно правы, почему туда закрался эндлер в депсы не пойму, скорее всего линтер ругнулся что в хуке используется, а в депсы не добавлены там хэндлер не должен быть в депсах да и сам flow можно оттуда убрать, но как защита от дурака пусть там лежит
а на счет этого всего у меня давно закралась идея попробовать использовать zustand для этих целей, но никак руки не доходят
Ну с FSD я с момента когда у них и документации толком не было. И сейчас абсолютное большинство проектов делаются на FSD Не подумайте что я как-то хейчу FSD Мне он до сих пор нравится и я пишу и буду писать на нем проекты. Методология еще молодая и еще все дополняется. И многие непонятные моменты они стандартизируют.
При этом не могу сказать точно приняли этот подход или нет, но видел что в версии 2.1 методологии должно было появиться описания page first подхода. Это как раз что вы и описали, что можно логику оставить в слое page Опять же считаю, что это сильно ломает принципы FSD Это еще сильнее размазывает логику по приложению. У всего должно быть свое место. Новый разработчик приходит видит что везде все вынесено в сущности. Допустим хочет посмотреть как работать с какойто сущностью а в папке entities ее нет. А оказывается он маленький и разработчик засунул в pages Так же далее при масштабировании она может разрасититься и эта сущность останется раздутой в рамках pages слоя
---
Здесь нет никакого уровня features. А это значит что любой state holder имеет доступ к любому репозиторию. А любой репозиторий к любому источнику данных. Добавив такой слой вы нарушили архитектуру гугла. Потому что если у вас репозиторий захочет иметь доступ к нескольким источникам данных он этого сделать не сможет.
Не совсем понял что вы имеете ввиду. Да и как понять репозиторий имеет доступ к источникам данных? Репозиторий это в целом реализация как источник данных. У нас для работы с сущностью может быть несколько репозриториев. Например у сущности будет репозиторий для получения данных с удаленного сервера и будет репозиторий для работы с этой сущностью в локальном хранилище. А уже use case будет оркестрировать ими. Допустим будет брать с удаленного сервера данные, кешировать их через репозиторий для работы с локальным хранилищем. И при последующем запросе на получение этих данных мы можем сразу отдать закешированные данные. Как один из кейсов.
Возможно не правильно понял вашу мысль, если что поправьте меня.
---
если мы выносим его полностью в виджеты то получается что мы опять же размажем логику по слоям. как вы делали аналогию с чистой, entity слой тоже что и domain, не совсем согласен конечно с этим, но допустим получается что мы в данном случае вытащим все в слой виджетов оставив доменный слой пустым
Т.е. приложение состоит из ясного набора слоев, каждый из которых имеет ясное назначение и отношения к другим слоям. И названы эти слои и их назначение.
Тут тоже не пойму. Это в целом относится ко всем архитектурам. Суть любой архитектуры это разделить области приложения таким или иным образом и определить их отношения между друг другом. Суть архитектур в целом везде чем то похожа друг на друга. В прямое сравнение FSD и чистой, что мы сейчас делаем, не самое удачное. Каждая из них решает свой спектр задач. Опять же допустим тот же самый МТС часто любят менять координально технологии судя по их рассказам с конференций и митапов. Они как раз активно используют в своих проектах именно чистую архитектуру. Если интересно можете поискать их статьи, выступления. Если быстро смогу найти, то прикреплю.
---
на счет use cases опять же если я правильно понял о чем вы говорите, то ответ остается тот же что и в прошлый раз. создание интерфейса тут мне просто напросто кажется избыточным, хотя тоже имеет место быть execute просто общепринятое название для выполнения действия у каждой команды он может быть свой тут больше момент уже договоренности внутри команды
видел и такое что создается LoginUseCase с функцией экзекьютором login
---
ну так useSyncExternalStore тоже хук, просто с другой сигнатурой можно сказать дали инструмент из под коробки я либо делаю сам себе, либо подгоняю свою реализацию под то что дает реакт опять же во вью на сколько я знаю нет аналога этому хуку в реакте и там придется все равно добавлять чтото если чтото в реакте сделано из под коробки не значит что есть везде, тоже самое и наоборот
статья это не гайд как нужно делать это как можно сделать и у все равно как бы мы не хотели стандартизировать все и вся невозможно, везде будет чтото отличаться у меня ход мыслей пошел сначала с создания логики потом дошел до ui тут же вы пошли получается от обратного взять ui, в данном случае react и подогнать под него, что конечно же тоже имеет смысл
никто не мешает потом в том же vue тоже создать инструмент который будет подогнан под реализацию которая уже сделана
---
Поведение страницы логина в зависимости от состояния авторизации и есть бизнес логика приложения. Например, в вашем случае у вас есть бизнес правила при которых страница переходит на страницу дашбоард, или показывает пользователю тостик. В других приложениях будут свои описанные поведения.
Тут с вами соглашусь, пример привел не совсем правильный, почему то я сразу этого не заметил. NavigateToLogin, ShowToast и так далее согласен это не правильно Тут нужно не говорить что нужно сделать ui, а что произошло допустим LoginSuccsess, ConfirmationSuccsess, Error А ui уже каким либо образом реагирует на эти действия
Тут конечно моя ошибка, посмотрю есть ли функионал редактирования статьи, исправлю ошибку
---
Добавив литерал в useFlow вы забыли, что этот литерал идет в зависимости useEffect [flow, handler], поэтому всякий раз при изменении состояния у вас подписка и отписка от этого литерала.
Тут же не согласен, так как view model создается один раз и живет на протяжении всего жизненного цикла компонента А flow и handler находятся именно в view model. Получается нет пересоздания view model нет и пересоздания ее полей и методов, а значит ссылки те же
---
тут как раз таки и идет координальное отличие fsd от чистой так как суть разделения на слои у них разный, то и методологии тестирования могут отличаться если в fsd мы берем компонент и его тестируем вместе слогикой вместе хотя тут тоже было бы хорошо даже в рамках того же fsd логику тоже тестировать отдельно
то в чистой тестирование происходит изолированно по слоям слои имеется ввиду что мы ui тестируем изолированно от логики а логику изолированно от ui
---
а на счет того что в FSD без проблем можно добавить DI инверсию зависимостей адекватную и так далее я сомневаюсь возможно это так, но я у себя в голове не могу представить себе как это адекватно должно выглядеть было бы больше свободного времени я бы с радостью попробовал бы чтото сделать все равно это так же очень интересная тема и можно было бы попробовать
да и если брать тот же самый редакс как стейт менеджер, то тут тоже возникают различия в presentation слое если берем чистую так как redux реализует скорее flux подход чем какой либо из MV но это тоже отдельная тема для разговора
core по хорошему должен быть платформонезависемым. конечно же я уже упоминал что утопии не бывает и на каждый чих делать абстракции абсолютно с вами согласен это избыточно главное понимать проект. Скажем так, чувствовать момент когда нужно делать дополнительную абстракцию, а когда можно этим пренебречь
А на счет DI я помоему не говорил что нужно писать самому. Вроде даже упоминал что можно взять готовый. Если я этого не упомянул, то каюсь :) Свой DI написать скорее было вызовом для себя. У каждого проекта свои требования. Слышал от знакомых случай где по требованиям в целом не было возможности использовать какие либо внешние библиотеки вовсе и работать система должна исключительно под локальной сетью. Но это уже совсем другая история)
В ответе на комментарий выше привел пример с уведомлениями. Бизнес редко требует что-то стандартизированное. И как пример уведомления завязанные как раз на конкретных сущностях
Об этом я уже упоминал при ответе на комментарий выше. Это больше выглядиь как костыль чтобы залотать дыру.
Опять же когда в проекта 1-2 раза встречается можнт стерпеть
Конечно как я и писал клин тянуть во все проекты не нужно. Для большинства задач достаточно и fsd
На клин нужно смотреть когда либо уже чувствуешь что команда разрослась и в проекте хаос. Либо на этапе проектирования если уже понимаешь что в недавнем будущем уже прижется сильно масштабироваться, можно сразу закладывать архитектуру.
Все крупные компании так или иначе начинали с монолитов, потом безизбежно все переписывали на микросервисы, микрофронты и так далее и тому подобное. И от части это все тоже про архитектуру
Проблемы в импорте разных фичей на уровне выше нет. Это абсолютно нормальная практика. Проблема возникает когда в рамках одного слоя появляется зависимость между сущностями
Именно для болшей изолированности у меня было принято решение раздеить каждую фичу на слои и чтобы у каждой был свой доменный слой, что как можно сильнее минимизирует какието связности между фичами и можно сказать связывает руки разработчику чтобы так не сделать
конечно если брать тот же самый андроид можно настроить зависимости на уровне gradle что разработчикам вообще не позволит сделать какой либо кросимпоррт даже случайно но мы в вебе, так что имеем что имеем)
Спасибо за подрбный фидбек. Мой первый опыт написания статьи, поэтому могу гдето в тексте теряться в повествовании :) Сейчас попробую более подробно довести свою мысль
Разъясню на счет кроссимпортов. Самое банальное что можно предложить у нас есть некая admin панель для работы с товарами в интернет магазине. У нас есть сущности для материалов и товаров.
type Material = {
id: string
name: string
img: string
}
type Product = {
id: string
name: string
materials: Material[]
}
и тут у нас уже на уровне сущностей появляется связность возникает вопрос как это обходить либо мы делаем кроссимпорты на прямую либо делаем кроссимпорты через @x
Кроссимпорты хоть и запрещены, но неизбежны. С этим методология давно борется и до сих пор в документации раздел по кроссимпортам не опубликован. При этом там же в этом разделе есть сноски на сообщения в телеграмме, где тоже описаны эти моменты. При этом по итогу мы имеем лишь костыль с @x нотацией. Возможно кому-то покажется что это нормально, но лично мое мнение это просто костыль который придумали чтобы залатать дыру
Ну и далее уже понятно что раз на уровне сущностей появилась связность, то и на уровне фичей автоматически появляются кроссимпорты при взаимодействии этих двух сущностей.
---
Далее на счет сервиса уведомлений. Опять же это тоже можно косвенно отнести к связности между сущностями. Допустим на том же примере с админкой в котором есть материалы и товары. И у нас появляется сервис уведомлений который ловит уведомления об изменении товаров и материалов (допустим чтобы другие админы видели изменения) и эти уведомления в себе держат материал или продукт который изменили, так как по дизайну предусмотрено отображение карточки товара прямо в списке уведомлений. И получается что это не shared слой который не привязан к бизнес сущностям, но добавляя его в слои выше получаем дополнительную связность.
---
Далее замечание по тестированию. Тут больше момент того что в FSD работа с сущностью скажем так размазана по нескольким слоям, не изолирована в одном слое. Часть работы с сущностью лежит в entity, часть в feature и так далее И тестирование уже сводится к тому что нужно всю эту цепочку найти и правильно это все организовать при тестировании.
Тут же благодаря абстракциям все части приложения друг о друге знаю только в рамках абстракции, вся реализация для друг друга это просто "черный ящик". Что упрощает поиск логической цепочки при тестировании. При этом появляется более гибкая возможность тестировать отдельные части приложения более изолированно.
Возможно как Вы упоминали, добавив DI в тот же FSD, то множество проблем решилось. Возможно это так и есть, но опять же мы решаем не все проблемы. В fsd нет конкретных абстракций над реализациями, у нас фича это и есть use case который использует "то что дали", это я про entity слой, который часто так же подвержен изменениям. Немного тут наверное тоже отошел от темы, но надеюсь смог объяснить.
Так же хочу заметить что добавление абстракций, инкапсулирования данных и так далее так же дает некую защиту от дурака. Нужно писать так и никак иначе. Что немного минимизирует плохой код. А чем меньше плохого кода, тем проще становится и тестирование приложения.
---
На счет врезки с документации гугла по чистой из андроид не совсем понял. Если зайти на документацию по архитектуре то ясно видно что говорится в первую очередь об однонаправленном потоке состояния. У нас есть четкое и ясное разделение ui, логики и хранилища данных А в случае с FSD эти слои размазаны по всему приложению. Банально есть entity в котором и состояние и ui и некая логика для сущности, при этом далее в фиче реализована дополнительная функциональность. Как будто сходится, фича - это юз кейс, но при этом у нас в фиче так же может быть своя ui которая связана так же как и с фичами, так же и со слоем ниже с сущностью отделенной ей
---
core слой это скорее как shared в FSD если уж брать аналогию. Это слой полностью не зависимый от бизнес сущностей. При этом никак не зависящие от внешних каких то библиотек (в идеальном мире опять таки, часто всетаки встречаются что выкрадываются реализации которые зависят от чегото внешнего, это уже минус в карму разработчика который это сделал. если мы зависим от чегото внешнего нужно от этого абстрагироваться) (опять же это все про идеальный мир, утопии не существует, нужно понимать где можно чем то пренебреч, а где нет)
А domain слой это как раз уже сердце приложения. Тут возможно возникает путанница из за дополнительного добавления изолирования фичей через папку features. В стандартном понимании чистой архитектуры мы не имеем этой папки и у нас domain это сплошная абстракция всего и вся в нашем приложении. Даже если уж мы идем еще дальше в андроид, то там фичи изолируются еще лучше, там есть практика изолирования фичей таким образом, что мы делаем отдельный условно java модуль в котором мы отдельно настраиваем так сказать мини сборщик для этой фичи. Далее углубляться я думаю не стоит, потому что там все на много сложнее и уйду в дебри.
Если уж идти прямо действительно по клин, то даже обращение к window должно быть абстрагировано. У нас весь доменный слой должен быть полностью платформонезависемым. Опять же как пример показывал, что можно поменять все на vue. Можно так же без проблемно мигрировать все на ReactNative просто переписав ui и добавить реализации для платформозависемых фичей.
---
Для юз кейсов не нужна дополнительная абстракция. UseCases это можно сказать интеракторы в нашей бизнес логике. Они и так не зависят от реализаций. Это действия которые мы выполняем на основе исключительно доменного слоя. Это как должны работать и взаимодействовать наши абстракции. Мы тут на уровне абстракции как раз говорим как должен работать сценарий. А делать абстракцию для абстракции это уже чушь. Если же вы имели ввиду добавить абстракцию чтобы стандартизировать названию функции экзекьютора, то по моему мнению это уже будет лишним. Тут больше уже код стайл в команде скорее. Если интереснее наследоваться дополнительно чтобы именовать экзекьютор - дело ваше
---
Если в рамках fsd вы так же создавали сущности, которые на вход принимают зависимости там бы тоже все было прозрачно и тестируемо и не пришлось бы мокать модули.
Не до конца понял вашу мыль тут. Но опять же подмечу что в случае с fsd use cases аркестрирует реализациями а не на уровне абстракций. FSD в целом как будто это только про ФП. Clean базируется на ООП и принципах SOLID. Брать аналогии у друг друга в целом очень холеварное месево получится.
---
далее вы говорите про data слой и network в нем и что похоже на entity слой из фсд. не понял к чему это, но вы правы, это в рамках fsd должно лежать именно в entity слое
---
можно было бы адаптировать и для useSyncExternalStore но он ждет просто эмита чтобы далее забрать состояние самому у меня же в flow реализовано так что колбэк подписки аргументом принимает новое значение. Можно взять аналогию с делегатов в шарпе
---
Но в вашем примере вы написали логику обработки события модели прямо в компоненте при том, что эта логика вообще не имеет ни одного замыкания на LoginPage.
Логика в компонентах имеется ввиду какая либо бизнес логика приложения. Отображение это не логическая составляющая приложения, так же как навигация. Это слой UI. Тут как раз таки для этого и используется паттерн MVI. Мы с контроллера (в нашем случае ViewModel) отправляем UI триггеры на которые он должен отреагировать. Ну или не отреагировать. Вью модели в данном случае без разницы. Он уведомил о каком либо действии, а UI либо както реагирует либо нет.
---
Это, конечно дело вкуса, но вроде так куда выразительнее.
Как вы и сказали это дело вкуса. Я же вынес это отдельно чтобы каждый раз не писать subscribe, unsibscribe
---
на счет тестирования не совсем вас понял. почему нет смысла отдельно тестировать. Мы отдельно протестировали логику чтобы она корректно работала. И отдельно протестировали UI чтобы он корретктно отображал состояние и корректно реагировал на эвенты как будто тут мне больше нечего добавить либо я так вас и не понял
---
И на счет добавления Di в FSD и дополнительная эта вся реорганизация про которую вы говорите это уже немного не в ту сторону. Это уже все так же будет выглядеть как дополнительные костыли над FSD чтобы както походить на давно проверенные паттерны проектирования ООП При том такая модернизированная FSD возможно будет не сильно проще, если вообще не еще сложнее в понимании чем та же самая clean
Надеюсь ответить на вопросы я смог достаточно хорошо. Так же большое спасибо за такой подробный фидбэк, очень приятно было подготовить вам ответ. Всегда рад когда можнно обменяться опытом. Даже пока отвечал еще пару раз заглянул в документацию андроида и вспомнил некоторые уже забытые для меня вещи оттуда. Такие дискуссии всегда полезны, получается как некая рефлексия на всем изученным и не изученным :)
на счет VueEnt обязательно посмотрю поизучаю в свободное время, а про оверхед - все зависит от проекта
конечно же в целом сама по себе чистая архитектура выглядит громоздкой
и для небольших проектов на месяц-два они и будет являться громозкой
это как взять кувалду чтобы забить гвоздик на шкафичк
а для большого неповоротливого проекта будет дешевле заниматься неким "оверхедом" чем потом заниматься разгребанием всего этого
есть много ньюансов которыми очень часто пренепрегают
например в идеальном мире все действия во вью модели должны проходить только через use cases
и не тянуть репозитории
и в 99% случаев это как раз уже будет лишним
проще достать репозиторий во вью модели и использовать его
и таких ньюансов достаточно чтобы упроситить себе жизнь
тут уже вопрос в любом случае, как и с любым масштабом приложения и без разницы какая архитектура, в экспертности разработчика, нужно понимать где можно "схалтурить" без последстивй, а где лучше не стоит
а на счет больших бандлов тут добавлю что такой подход к архитектуре дает большой потенциал по разбиению бандла на чанки с большей гибкостью
а если вы говорите про то что приходится при таком подходе писать больше кода
то тут уже нужно тоже смотреть на потребности вашего продукта
в данном случае при чуть большем количестве кода лучше идет работа с количеством памяти который используем, не на столько сильно нагружем garbage collector за счет di и view model
поэтому в любом случае нужно смотреть потребности проекта и рассматривать все за и против архитектуры для продукта
в моем случае уже как будто фича новая добавляется не дольше а может и быстрее чем с fsd архитектурой
всетаки дело привычки тоже никто не отменял
к хорошему быстро привыкаешь))
EDIT: добавлю еще что в целом нужно понимать нужны ли вам абстракции. в моем случае приложение так же используется на разных платформах и нужна безболезненная подмена реализаций
и тут уже получается что для меня это базовый минимум
а в случае с другим проектом это может быть не базовым минимумом а раскошным максимумом (по типу "давайте затащим чистую, чтобы если вдруг мы потом захотим поменять axios обратно на fetch то было просто")
В нормальном ЯП думаю медиатор более чем хорошее решение)
Но тут js без множественного наследования) ввше я приводил пример с Clearable. Похожая ситуцаия. Хотел сделать через наследование, по факту получился бы тот же самый медиатор, но отказался от этого, так как мы в репозитории должны отнаследоваться от ее абстракции а места для наследования от "коллеги" нет
Попробовал поискать реализацию миксинов в js. Понял что лучше в такие дебри не лезть)
На счёт бить по рукам сложная тема. Как выше упомянул если это слишком частая проблема, возможно есть смысл действительно на уровне линтера запретить использовать на прямую дата слой. Да прижется простые сеттеры писать, но тут уже смотрите что больше проблем приносит. К сожалению другого выхода мне кажется нету. Либо добавляем четкое правило, либо терпим :)
В иммутабельном аодходе все просто и прозрачно. В мутабельном же подходе подходе приходится за всем следить. Думать как работать со сложенными сущностями. К иммутабельному подходу у меня появились негативные впечатления еще давно пока я работал на flutter, там как раз мы использовали mobx. Да и в иммутабельном варианте все измнения как раз прозрачны. И мы не можем просто взять где нибудь написать user.role.name = "ADMIN", то что вы как раз упоминали.
Помимо этого мутабельный подход в целом дороже (в плане ресурсов) мы должны постоянно следить за изменениями и уведомлять при необходимости. При добавлении декоратора для экшенов и тем более computed значений, каждый раз нужно за всем следить чтобы обновлять или же не обновлять) этим всем занимается mobx.
Детерминированность это всегда и везже хорошо. А мутабельность это постоянные сайд эффекты скажем так
Самописный di изначально была задумка как вызов для себя. "Чем я хуже других, не смогу свой написать не менее хороший что-ли?". И потом уже в последствии методом проб и ошибок на своем же опыте пришел к тому что сюда функционал изоляции был бы очень даже кстати. Как по мне такой подход к обучению самый эффективный. Как минимум пока я это делал очередной раз убедился что js та еще штука :) (в плохом смысле)
Ну вообще мы в целом не можем взять и написать user.name = "Alex"
У flow в даннос случае нет сеттера
Мы можем делать только emit новых данных
Тут подход идет иммутабельности
Новое состояние, новый объект
что мы и можем видеть в методе update для MutableStateFlow
мы передаем данные которые нужно обновить и рестом собираем новый объект с пред данными и заменяем нужные поля новыми данными
а на счет запрета использования репозитория данных в обход use case, не совсем понимаю зачем, и не совсем понимаю чем может мешать изменение данных не через use case
use case нужен если мы хотим хранить кэш, дополнительно отправлять метрики, или чтото подобное
Если мы делаем todo list который работает локально и нам просто нужно без каких либо дополнительных действий изменить checked, то для этого можно забрать репозиторий прямо во вью модель и дергать метод toggleChecked допустим
ну и если я правильно понял то вы подразумеваете под моделью сам репозиторий?
тоесть источник данных для какойто сущности
но у сущности может быть несколько источников данных
например тот же самый todo
Мы делаем репозиторий для работы с тудушками на удаленном сервере
И так же репозиторий для работы с ними в локальном хранилище
Вот тут то нам и нужен use case, например ToggleTodoCheched
который будет работать например по принципу optimistic. Изменит локально сразу же состояние, и отправит запрос на сервер. Если успешно то все хорошо, в локальном хранилище уже изменили, если не успешно то откатили
похожим образом можно сделать работу оффлайн
сохранять данные локально при отсутсвии интернета
при появлении сети сделать реализацию синхронизации
вот реальные примеры для use cases
если нам нужно просто получить accsess token из репозитория токенов, то можно взять репозиторий и вытянуть оттуда данные
если нам нужно просто закинуть новую пару ключей, то так же можно сделать на прямую через репозиторий
если мы хотим закинуть пару ключей, но при этом сохранить refresh token в локальное хранилище, правильно будет уже use case который это будет делать
все что влияет на бизнес логику мы выносим в use cases
если же вы имеете ввиду что у нас есть use case для того чтобы ставить новую пару токенов, а разработчик решил идти на прямую в репозиторий, то тут уже линтерами не спастись. Это уже на совести разработчика. Перед тем как чтото делать в любом случае нужно изучить что уже написано, не зависимо от архитектуры. Тут же удобно тем что весь доменный слой просмотреть не трудно. Доменный слой полностью описывает работу приложения. Если по доменному слою не понятно что должно происходить возможно чтото не так.
если это на столько частый кейс в проекте, то возможно имеет смысл просто отказаться от прямого использования репозиториев в вью моделях. тут уже линтер вам поможет.
Можете пожалуйста объяснить что вы подразумеваете под менять модель в обход use case?
Никак не могу понять что вы хотите спросить
Да и в целом наверное еще что вы понимаете под моделью?
Нет, создание use case на каждый чих не обязательно. Use cases это пользовательские сценарии которые мы создаем при необходимости
Конечно в идеальном мире хочется для всего создавать, ведь в дальнейшем может понадобиться модернизация, но для обычных сеттеров создавать отдельно use case не стоит
view model может использовать репозиторий на прямую (опять же знает исключительно абстракции, реализации закидываем только через di) - и это нормальная практика
но как только появляется момент что вы во вью модели начинаете както дополнительно работать с этими данными (изменять, кешировать, перекидывать в другие репозитории), ну в двух словах появляется как раз бизнес логика, то нужно уже выносить в пользовательские сценарии (use cases)
Я кажется опять не до конца вас понял. Но во первых, дто не становится основой для модели. Дто у нкс лежат исплбчительно в data слое. С dto мы работаем исключительно в рамках слоя данных. Модели с которыми мы работем в доменном слое а в последующем и в слое представления уже описаны в доменном слое - это уже как раз наши бизнес сущности. Слой данных может работать с dto как хочет, на слой выше слой данных отдает именно доменные модели. Такая практика очень часто встречается не только в чистой архитектуре. Все что связано с описанием наших бизнес сущностей находится в досенном слое, и доменному слою без разницы что и как будет делать слой данных, главное чтобы совпадали входные и выходные данные которые мы описали в "контракте" (можель а длменом слое)
А вот на счет изоляции моделей и что нельязя к ним получить доступ обойдя бизнес правила я не понял.
Если нужно при изменении данных в одном месте ревалидировать данные в другом месте, то это ты делаем в use cases. Они как раз и нужны для оркестрации данными, они и описывают пользовательские сценарии.
Но как пример мне привести из реального кейса, нужно было очищать данные со всего приложения на уровне всех слоев при выходе из аккаунта. Решили тем что создаем в core класс Clearable который даёт метод для подписки и метод для очистки. Далее все репозитории которые должны очищаться через di получают и каким либо образом реагируют на эвент очистки
Тут к сожалению приходится самому везде подписываться на событие. В андроиде я бы сделал более красиво через наследовние. Сделал бы класс Clearable от которого наследовался бы и нужно было бы реализовать метод onClear, но пришлось откзааться в js в счет отсутствия миксинов, множественного наследования
Спасибо вам за очень интересную дисскуссию! Было очень здорово получить такой фидбэк!
Первый вопрос не понял, можете пожалуйста подробнее описать. Не пойму о каких изменениях модели в обход бизнес логики вы говорите.
А на счет взаимодействия между друг другом. Либо у нас связующее app, либо если они связаны, то скорее всего это изначально не имело смысла делить на разные доменные слои. Но бывает и такое что они неизбежно связаны друг с другом. Тогда мы явно указываем в di модулях что они связаны между друг с другом. Это не рекомендованно, но не запрещено. Поэтому и попробовал для себя использовать не готовую DI, а сделать свой вместе с явным указанием зависимостей.
В android разработке такие фичи можно сказать разделяются на отдельные приложения, там создаются отдельные пакеты со своей конфигурацией сборщика который наследуется от основного конфига. И там мы можем настроить зависимости между модулями.
Для более простого понимания можно сделать аналогию с микрофронтами. В большинстве случаев разделение в фичах можно сделать так же как делили бы на микрофронты.
В микрофронтах явно указываем откуда что мы тянем.
Ну опять же я и говорил что используется не изначальная концепция чистой, а немного модернизированная
Если уж брать изначально как должно быть, то как у меня сделано, папки features не должно быть
Есть единый доменный слой всего приложения и все
Там вообще никаких проблем не возникает, кому что надо тот то и использует
Отдельный features вынесен для более четкого разделения логики
При этом тут если же смотреть если мы делаем такое разделение по фичам, то так же как и в FSD кроссимпорты между фичами тоже могут быть. В случае с андроид мы на уровне сборщика указываем зависимости между пакетами. Тут же только если прибегать к настройкам линтера. С разделением на фичи просто удобнее работать с разделением на несколько команд разработки.
Ну и в большинстве случаев app слой является связующим, где так же можно хранить логику
Тоесть грубо говоря если бы мы убрали слой фичей
то был бы в app domain data presentation и там все хранилось бы
везде будут свои сложности. утопии нет) ничего не идеально)
чем сильнее усложнять тем больше проблем
тут же чтобы понимать как делить фичи наверное можно взять аналогию с микрофронтов
фичи это микрофронты, а app собирает их как нужно
---
размазано в данном случае говорил о том что мы вынесем всю сущность этих уведомлений в слой виджета пропустив банально entity слой
что размажет дополнительно логику
так мы отходим от стандартной практики разработки
по хорошему мы идем снизу вверх
сделали утилиты в шаред
описали сущность в энтити
написали можно сказать юз кейсы в фичах
скомпоновали в логические блоки в виджетах
собрали в страницы
мы же пропустили тут 2 слоя запихнув все в виджет
---
ну тут core как shared из fsd
а app и features
тут как написал уже выше
можно было бы сделать один доменный слой для всего приложения
тут же мы просто разбиваем приложение на отдельные логические блоки
а app их объединяет
опять же аналогия с микрофронтами
---
по use cases шаблона тут какогото нет
мы просто в рамках проекта договариваемся на нейминг
если брать документацию андроида там функцию executor'a назвали invoke
---
на счет переподписки в useFlow абсолютно правы, почему туда закрался эндлер в депсы не пойму, скорее всего линтер ругнулся что в хуке используется, а в депсы не добавлены
там хэндлер не должен быть в депсах
да и сам flow можно оттуда убрать, но как защита от дурака пусть там лежит
а на счет этого всего у меня давно закралась идея попробовать использовать zustand для этих целей, но никак руки не доходят
Ну с FSD я с момента когда у них и документации толком не было. И сейчас абсолютное большинство проектов делаются на FSD
Не подумайте что я как-то хейчу FSD
Мне он до сих пор нравится и я пишу и буду писать на нем проекты.
Методология еще молодая и еще все дополняется. И многие непонятные моменты они стандартизируют.
При этом не могу сказать точно приняли этот подход или нет, но видел что в версии 2.1 методологии должно было появиться описания page first подхода. Это как раз что вы и описали, что можно логику оставить в слое page
Опять же считаю, что это сильно ломает принципы FSD
Это еще сильнее размазывает логику по приложению.
У всего должно быть свое место.
Новый разработчик приходит видит что везде все вынесено в сущности.
Допустим хочет посмотреть как работать с какойто сущностью а в папке entities ее нет. А оказывается он маленький и разработчик засунул в pages
Так же далее при масштабировании она может разрасититься и эта сущность останется раздутой в рамках pages слоя
---
Не совсем понял что вы имеете ввиду. Да и как понять репозиторий имеет доступ к источникам данных? Репозиторий это в целом реализация как источник данных. У нас для работы с сущностью может быть несколько репозриториев. Например у сущности будет репозиторий для получения данных с удаленного сервера и будет репозиторий для работы с этой сущностью в локальном хранилище. А уже use case будет оркестрировать ими. Допустим будет брать с удаленного сервера данные, кешировать их через репозиторий для работы с локальным хранилищем. И при последующем запросе на получение этих данных мы можем сразу отдать закешированные данные. Как один из кейсов.
Возможно не правильно понял вашу мысль, если что поправьте меня.
---
если мы выносим его полностью в виджеты то получается что мы опять же размажем логику по слоям.
как вы делали аналогию с чистой, entity слой тоже что и domain, не совсем согласен конечно с этим, но допустим
получается что мы в данном случае вытащим все в слой виджетов оставив доменный слой пустым
Тут тоже не пойму. Это в целом относится ко всем архитектурам. Суть любой архитектуры это разделить области приложения таким или иным образом и определить их отношения между друг другом.
Суть архитектур в целом везде чем то похожа друг на друга. В прямое сравнение FSD и чистой, что мы сейчас делаем, не самое удачное. Каждая из них решает свой спектр задач. Опять же допустим тот же самый МТС часто любят менять координально технологии судя по их рассказам с конференций и митапов. Они как раз активно используют в своих проектах именно чистую архитектуру. Если интересно можете поискать их статьи, выступления. Если быстро смогу найти, то прикреплю.
---
на счет use cases опять же если я правильно понял о чем вы говорите, то ответ остается тот же что и в прошлый раз. создание интерфейса тут мне просто напросто кажется избыточным, хотя тоже имеет место быть
execute просто общепринятое название для выполнения действия
у каждой команды он может быть свой
тут больше момент уже договоренности внутри команды
видел и такое что создается LoginUseCase с функцией экзекьютором login
---
ну так useSyncExternalStore тоже хук, просто с другой сигнатурой можно сказать
дали инструмент из под коробки
я либо делаю сам себе, либо подгоняю свою реализацию под то что дает реакт
опять же во вью на сколько я знаю нет аналога этому хуку в реакте и там придется все равно добавлять чтото
если чтото в реакте сделано из под коробки не значит что есть везде, тоже самое и наоборот
статья это не гайд как нужно делать
это как можно сделать
и у все равно как бы мы не хотели стандартизировать все и вся невозможно, везде будет чтото отличаться
у меня ход мыслей пошел сначала с создания логики потом дошел до ui
тут же вы пошли получается от обратного
взять ui, в данном случае react и подогнать под него, что конечно же тоже имеет смысл
никто не мешает потом в том же vue тоже создать инструмент который будет подогнан под реализацию которая уже сделана
---
Тут с вами соглашусь, пример привел не совсем правильный, почему то я сразу этого не заметил. NavigateToLogin, ShowToast и так далее согласен это не правильно
Тут нужно не говорить что нужно сделать ui, а что произошло
допустим LoginSuccsess, ConfirmationSuccsess, Error
А ui уже каким либо образом реагирует на эти действия
Тут конечно моя ошибка, посмотрю есть ли функионал редактирования статьи, исправлю ошибку
---
Тут же не согласен, так как view model создается один раз и живет на протяжении всего жизненного цикла компонента
А flow и handler находятся именно в view model. Получается нет пересоздания view model нет и пересоздания ее полей и методов, а значит ссылки те же
---
тут как раз таки и идет координальное отличие fsd от чистой
так как суть разделения на слои у них разный, то и методологии тестирования могут отличаться
если в fsd мы берем компонент и его тестируем вместе слогикой вместе
хотя тут тоже было бы хорошо даже в рамках того же fsd логику тоже тестировать отдельно
то в чистой тестирование происходит изолированно по слоям
слои имеется ввиду что мы ui тестируем изолированно от логики
а логику изолированно от ui
---
а на счет того что в FSD без проблем можно добавить DI инверсию зависимостей адекватную и так далее я сомневаюсь
возможно это так, но я у себя в голове не могу представить себе как это адекватно должно выглядеть
было бы больше свободного времени я бы с радостью попробовал бы чтото сделать
все равно это так же очень интересная тема и можно было бы попробовать
да и если брать тот же самый редакс как стейт менеджер, то тут тоже возникают различия в presentation слое если берем чистую
так как redux реализует скорее flux подход чем какой либо из MV
но это тоже отдельная тема для разговора
core по хорошему должен быть платформонезависемым. конечно же я уже упоминал что утопии не бывает и на каждый чих делать абстракции абсолютно с вами согласен это избыточно
главное понимать проект. Скажем так, чувствовать момент когда нужно делать дополнительную абстракцию, а когда можно этим пренебречь
А на счет DI я помоему не говорил что нужно писать самому. Вроде даже упоминал что можно взять готовый. Если я этого не упомянул, то каюсь :)
Свой DI написать скорее было вызовом для себя.
У каждого проекта свои требования. Слышал от знакомых случай где по требованиям в целом не было возможности использовать какие либо внешние библиотеки вовсе и работать система должна исключительно под локальной сетью. Но это уже совсем другая история)
В ответе на комментарий выше привел пример с уведомлениями. Бизнес редко требует что-то стандартизированное. И как пример уведомления завязанные как раз на конкретных сущностях
Об этом я уже упоминал при ответе на комментарий выше. Это больше выглядиь как костыль чтобы залотать дыру.
Опять же когда в проекта 1-2 раза встречается можнт стерпеть
Конечно как я и писал клин тянуть во все проекты не нужно. Для большинства задач достаточно и fsd
На клин нужно смотреть когда либо уже чувствуешь что команда разрослась и в проекте хаос. Либо на этапе проектирования если уже понимаешь что в недавнем будущем уже прижется сильно масштабироваться, можно сразу закладывать архитектуру.
Все крупные компании так или иначе начинали с монолитов, потом безизбежно все переписывали на микросервисы, микрофронты и так далее и тому подобное. И от части это все тоже про архитектуру
Проблемы в импорте разных фичей на уровне выше нет. Это абсолютно нормальная практика. Проблема возникает когда в рамках одного слоя появляется зависимость между сущностями
Буду признателен ссылкам на рекомендуемые ресурсы где лучше почитать. С радостью почитаю в свободное время!
Именно для болшей изолированности у меня было принято решение раздеить каждую фичу на слои и чтобы у каждой был свой доменный слой, что как можно сильнее минимизирует какието связности между фичами и можно сказать связывает руки разработчику чтобы так не сделать
конечно если брать тот же самый андроид можно настроить зависимости на уровне gradle что разработчикам вообще не позволит сделать какой либо кросимпоррт даже случайно
но мы в вебе, так что имеем что имеем)
Спасибо за подрбный фидбек. Мой первый опыт написания статьи, поэтому могу гдето в тексте теряться в повествовании :)
Сейчас попробую более подробно довести свою мысль
Разъясню на счет кроссимпортов. Самое банальное что можно предложить у нас есть некая admin панель для работы с товарами в интернет магазине. У нас есть сущности для материалов и товаров.
и тут у нас уже на уровне сущностей появляется связность
возникает вопрос как это обходить
либо мы делаем кроссимпорты на прямую
либо делаем кроссимпорты через @x
Кроссимпорты хоть и запрещены, но неизбежны. С этим методология давно борется и до сих пор в документации раздел по кроссимпортам не опубликован. При этом там же в этом разделе есть сноски на сообщения в телеграмме, где тоже описаны эти моменты.
При этом по итогу мы имеем лишь костыль с @x нотацией. Возможно кому-то покажется что это нормально, но лично мое мнение это просто костыль который придумали чтобы залатать дыру
Ну и далее уже понятно что раз на уровне сущностей появилась связность, то и на уровне фичей автоматически появляются кроссимпорты при взаимодействии этих двух сущностей.
---
Далее на счет сервиса уведомлений. Опять же это тоже можно косвенно отнести к связности между сущностями.
Допустим на том же примере с админкой в котором есть материалы и товары. И у нас появляется сервис уведомлений который ловит уведомления об изменении товаров и материалов (допустим чтобы другие админы видели изменения) и эти уведомления в себе держат материал или продукт который изменили, так как по дизайну предусмотрено отображение карточки товара прямо в списке уведомлений. И получается что это не shared слой который не привязан к бизнес сущностям, но добавляя его в слои выше получаем дополнительную связность.
---
Далее замечание по тестированию. Тут больше момент того что в FSD работа с сущностью скажем так размазана по нескольким слоям, не изолирована в одном слое. Часть работы с сущностью лежит в entity, часть в feature и так далее
И тестирование уже сводится к тому что нужно всю эту цепочку найти и правильно это все организовать при тестировании.
Тут же благодаря абстракциям все части приложения друг о друге знаю только в рамках абстракции, вся реализация для друг друга это просто "черный ящик". Что упрощает поиск логической цепочки при тестировании. При этом появляется более гибкая возможность тестировать отдельные части приложения более изолированно.
Возможно как Вы упоминали, добавив DI в тот же FSD, то множество проблем решилось. Возможно это так и есть, но опять же мы решаем не все проблемы. В fsd нет конкретных абстракций над реализациями, у нас фича это и есть use case который использует "то что дали", это я про entity слой, который часто так же подвержен изменениям.
Немного тут наверное тоже отошел от темы, но надеюсь смог объяснить.
Так же хочу заметить что добавление абстракций, инкапсулирования данных и так далее так же дает некую защиту от дурака. Нужно писать так и никак иначе. Что немного минимизирует плохой код. А чем меньше плохого кода, тем проще становится и тестирование приложения.
---
На счет врезки с документации гугла по чистой из андроид не совсем понял. Если зайти на документацию по архитектуре то ясно видно что говорится в первую очередь об однонаправленном потоке состояния. У нас есть четкое и ясное разделение ui, логики и хранилища данных
А в случае с FSD эти слои размазаны по всему приложению. Банально есть entity в котором и состояние и ui и некая логика для сущности, при этом далее в фиче реализована дополнительная функциональность. Как будто сходится, фича - это юз кейс, но при этом у нас в фиче так же может быть своя ui которая связана так же как и с фичами, так же и со слоем ниже с сущностью отделенной ей
---
core слой это скорее как shared в FSD если уж брать аналогию. Это слой полностью не зависимый от бизнес сущностей. При этом никак не зависящие от внешних каких то библиотек (в идеальном мире опять таки, часто всетаки встречаются что выкрадываются реализации которые зависят от чегото внешнего, это уже минус в карму разработчика который это сделал. если мы зависим от чегото внешнего нужно от этого абстрагироваться) (опять же это все про идеальный мир, утопии не существует, нужно понимать где можно чем то пренебреч, а где нет)
А domain слой это как раз уже сердце приложения. Тут возможно возникает путанница из за дополнительного добавления изолирования фичей через папку features. В стандартном понимании чистой архитектуры мы не имеем этой папки и у нас domain это сплошная абстракция всего и вся в нашем приложении.
Даже если уж мы идем еще дальше в андроид, то там фичи изолируются еще лучше, там есть практика изолирования фичей таким образом, что мы делаем отдельный условно java модуль в котором мы отдельно настраиваем так сказать мини сборщик для этой фичи. Далее углубляться я думаю не стоит, потому что там все на много сложнее и уйду в дебри.
Если уж идти прямо действительно по клин, то даже обращение к window должно быть абстрагировано. У нас весь доменный слой должен быть полностью платформонезависемым. Опять же как пример показывал, что можно поменять все на vue. Можно так же без проблемно мигрировать все на ReactNative просто переписав ui и добавить реализации для платформозависемых фичей.
---
Для юз кейсов не нужна дополнительная абстракция. UseCases это можно сказать интеракторы в нашей бизнес логике. Они и так не зависят от реализаций. Это действия которые мы выполняем на основе исключительно доменного слоя. Это как должны работать и взаимодействовать наши абстракции. Мы тут на уровне абстракции как раз говорим как должен работать сценарий. А делать абстракцию для абстракции это уже чушь.
Если же вы имели ввиду добавить абстракцию чтобы стандартизировать названию функции экзекьютора, то по моему мнению это уже будет лишним. Тут больше уже код стайл в команде скорее. Если интереснее наследоваться дополнительно чтобы именовать экзекьютор - дело ваше
---
Не до конца понял вашу мыль тут. Но опять же подмечу что в случае с fsd use cases аркестрирует реализациями а не на уровне абстракций. FSD в целом как будто это только про ФП. Clean базируется на ООП и принципах SOLID. Брать аналогии у друг друга в целом очень холеварное месево получится.
---
далее вы говорите про data слой и network в нем и что похоже на entity слой из фсд. не понял к чему это, но вы правы, это в рамках fsd должно лежать именно в entity слое
---
можно было бы адаптировать и для useSyncExternalStore
но он ждет просто эмита чтобы далее забрать состояние самому
у меня же в flow реализовано так что колбэк подписки аргументом принимает новое значение. Можно взять аналогию с делегатов в шарпе
---
Логика в компонентах имеется ввиду какая либо бизнес логика приложения. Отображение это не логическая составляющая приложения, так же как навигация. Это слой UI. Тут как раз таки для этого и используется паттерн MVI. Мы с контроллера (в нашем случае ViewModel) отправляем UI триггеры на которые он должен отреагировать. Ну или не отреагировать. Вью модели в данном случае без разницы. Он уведомил о каком либо действии, а UI либо както реагирует либо нет.
---
Как вы и сказали это дело вкуса. Я же вынес это отдельно чтобы каждый раз не писать subscribe, unsibscribe
---
на счет тестирования не совсем вас понял. почему нет смысла отдельно тестировать. Мы отдельно протестировали логику чтобы она корректно работала. И отдельно протестировали UI чтобы он корретктно отображал состояние и корректно реагировал на эвенты
как будто тут мне больше нечего добавить либо я так вас и не понял
---
И на счет добавления Di в FSD и дополнительная эта вся реорганизация про которую вы говорите это уже немного не в ту сторону. Это уже все так же будет выглядеть как дополнительные костыли над FSD чтобы както походить на давно проверенные паттерны проектирования ООП
При том такая модернизированная FSD возможно будет не сильно проще, если вообще не еще сложнее в понимании чем та же самая clean
Надеюсь ответить на вопросы я смог достаточно хорошо. Так же большое спасибо за такой подробный фидбэк, очень приятно было подготовить вам ответ. Всегда рад когда можнно обменяться опытом. Даже пока отвечал еще пару раз заглянул в документацию андроида и вспомнил некоторые уже забытые для меня вещи оттуда. Такие дискуссии всегда полезны, получается как некая рефлексия на всем изученным и не изученным :)