Здравствуйте, UberPsychoSvin, Вы писали:
UPS>В приложении есть такие нэймспейсы: UPS>BLL — вся основная логика приложения. UPS>UI — мордочка.
UPS>Как они должны быть взаимосвязаны. Какой вариант и почему предпочтительней?
Тут проблема не в раздедении, а в ментальной модели.
Принцип очень простой — любой из слоёв, на которые вы разделяете приложение, не должен ничего знать о внутренностях остальных слоёв.
Т.е. любая из частей использует API других слоёв и предоставляет своё API, которое спроектировано с учётом реальных сценариев использования и ограничивается ответственностями самого слоя.
Если этот подход работает (засел в подкорке) — то разделение не представляет особых проблем, т.к. проводится интуитивно, ещё на этапе формулировки требований.э
Если нет — от разделения не будет толку, т.к. оно будет выполнено чисто формально и будет постоянно страдать от утечки абстракций.
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, UberPsychoSvin, Вы писали:
UPS>>В приложении есть такие нэймспейсы: UPS>>BLL — вся основная логика приложения. UPS>>UI — мордочка.
UPS>>Как они должны быть взаимосвязаны. Какой вариант и почему предпочтительней?
S>Тут проблема не в раздедении, а в ментальной модели. S>Принцип очень простой — любой из слоёв, на которые вы разделяете приложение, не должен ничего знать о внутренностях остальных слоёв.
Ну окей, пускай в примере полный IOC. В обоих вариантах связь между DLL следующая.
== DLL_1 ==
namespace BLL : интерфейсы.
== DLL_25 ==
namespace KERNEL : DI фреймворк. Единственный класс с референсами на реализацию. Пусть там будет ninject какой-нибудь.
== DLL_2 ==
namespace UI : берёт реализацию от KERNEL работает через интерфейс.
== ВАРИАНТ 1.
* DLL_1 ("APP.dll")
namespace : APP.BLL;
* DLL_2 ("APP.UI.dll")
namespace : APP.UI;
== ВАРИАНТ 2.
* DLL_1 ("APP.dll")
namespace : APP.BLL;
Плюсы второго варианта мне видятся в том:
* Не надо юзингов прописывать. Всё что в UI получает доступ к тому что в неймспейсах APP И BLL, т.е. ко всему что ему нужно.
* Он более логичный.
Здравствуйте, UberPsychoSvin, Вы писали:
S>>Тут проблема не в раздедении, а в ментальной модели. S>>Принцип очень простой — любой из слоёв, на которые вы разделяете приложение, не должен ничего знать о внутренностях остальных слоёв.
UPS>Ну окей, пускай в примере полный IOC. В обоих вариантах связь между DLL следующая.
Давай ещё раз: не в инструментах дело. Дело в разделении ответственностей, а эта штука проводится сначала в голове, а уж затем переносится на код.
Ты _сначала_ определись с тем, что у тебя относится к UI, что не относится, а затем думай как по неймспейсам раскидывать.
Ну и попробуй ответить для себя: зачем UI-слою лезть в кишки БЛ, чем он так принципиально отличается от прочих слоёв?
А то у тебя критерии странные — "так юзингов меньше надо".
Все подобные улучшения вечно забывают простой момент: маппинг "один-к одному" очень легко и внезапно меняется на "ко многим".
Ну вот представь, что в UI надо будет выводить данные из нескольких биз-областей. Например, мастер принятия на работу обычно подразумевает заполнение анкеты с данными физлица, данными о отношениях с налоговой, данными об образовании, о детях (если нужны налоговые вычеты) и тыды и тыпы. И логику для каждой из областей хорошо бы использовать повторно.
Здравствуйте, Sinix, Вы писали: S>Ну и попробуй ответить для себя: зачем UI-слою лезть в кишки БЛ, чем он так принципиально отличается от прочих слоёв?
В моих двух примерах(в поправленном варианте) UI слой не лезет в кишки/реализацию к BLL, он напрямую референсит только интерфейсы.
1) У меня есть DLL'ка с интерфейсами.
APP.dll — namespace APP.BLL;
2) DLL-ка с реализацией того что объявлено в APP.dll .
APP.BLL.dll
3) Слой UI, который референсит DLL-ку с интерфейсами.
S>В какой из неймспейсов всё это будем складывать?
Если физлицу не надо лезть в налоговую и т.д. то логично всех их раскидать по поднеймспейсам.
namespace : APP.BLL.Физлицо;
namespace : APP.BLL.Налоговая;
namespace : APP.BLL.Образование;
namespace : APP.BLL.Дети;
Ну а дальше всё точно так же, только надо будет ещё на конкретный нэймспейс ссылаться(но для этого мы их и разделяли). Ну а UI нэймспейс можно по прежнему расположить теми же двумя способами.
1) APP.BLL.UI;
2) APP.UI;
Разница помимо понятности, только в том, что:
В первом варианте надо писать "using Физлицо;"
А во втором варианте надо писать "using BLL.Физлицо;"
Здравствуйте, Doc, Вы писали:
Doc>Здравствуйте, UberPsychoSvin, Вы писали:
UPS>>В приложении есть такие нэймспейсы: UPS>>BLL — вся основная логика приложения. UPS>>UI — мордочка.
Doc>IMHO если отталкиваться от предложенного, то Doc>MyApp.BLL Doc>MyApp.UI
Ну ваще, имхо, да, самый оптимальный вариант.
А как такая схема выглядит под IoC архитектуру?
MyApp // Интерфейсы, и наиболее общие классы данных.
MyApp.Logging; // Реализация с логгированием.
MyApp.Tools; // ОС специфика. WinAPI. WMI.
MyApp.Configuration; // конфигурация
MyApp.Database; // базовые классы для БД.
MyApp.Database.MsSql; // реализация для оракла.
MyApp.Database.Oracle; // реализация для MsSql.
MyApp.Service; // виндовый сервис.
MyApp.BL
MyApp.UI;
//ну и ещё наверное можно сделать.
MyApp.Kernell // а тут всё биндится. MyApp.Service MyApp.UI берут отсюда реализацию основного класса.
* MyApp.Core — все только про BL: интерфейсы, модели, реализации. Никаких пряямых завязок на облака, БД, UI, авторизацию итд. Только на сборку ниже.
* MyApp.Infrastructure — интерфейсы всего, что нужно для BL: доступ к данным, к авторизации, облакам. Но все в терминах BL.
ВНУТРИ:
MyApp.Infrastructure.DataAccess — интерфейсы доступа к данным
MyApp.Infrastructure.Logging — интерфейсы лога
итд для всех используемых сервисов
* MyApp.Infrastructure.DataAccess.MsSQL — реализация части Infrastructure касательно БД для MSSQL
* MyApp.Infrastructure.DataAccess.Oracle- тоже самое но для для Oracle
* MyApp.Infrastructure.[что-то там еще] — итд, реализации всего что есть MyApp.Infrastructure под конкретных провайдеров
* MyApp.Bootstrap — умеет по конфигу собрать нужные DLL и подготовить IoC контейнер
* MyApp.WebUI — веб-приложение
Ну и тесты MyApp.Core.MockTest, MyApp.Core.Test итд
Здравствуйте, UberPsychoSvin, Вы писали:
UPS>P.S. Я про C#, мало ли, может в разных языках разные нюансы есть.
Нужно исходить из практической выгоды:
Если BL будет знать об UI, то вы не сможете использовать BL повторно. К примеру, если вы захотите изменить UI, сделать Win-версию вместо Web -- облом, у вас BL завязан на UI. По этому BL ничего не знает об UI.
Если UI будет жестко привязан к BL, то вы не сможете сделать фейковую реализацию BL с целью тестирования определенных нюансов. К примеру, если вы делаете систему банкинга, вам нужно будет ждать пока возникнет исключительная ситуация в реальном окружении, а это проблематично. Однако многие на это забивают и тестируют в реальном окружении, ссылаясь на недостаток финансирования проекта.
Здравствуйте, Doc, Вы писали:
Doc>* MyApp.Infrastructure.DataAccess.MsSQL — реализация части Infrastructure касательно БД для MSSQL Doc>* MyApp.Infrastructure.DataAccess.Oracle- тоже самое но для для Oracle Doc>* MyApp.Infrastructure.[что-то там еще] — итд, реализации всего что есть MyApp.Infrastructure под конкретных провайдеров
Infrastructure оставляете для реализации? Обычно под этим подразумевают контракты. Иногда встречал MyApp.DataAccess.Infrastructure и MyApp.DataAccess.MsSQL или MyApp.DataAccess.Implementation.MsSQL
Какой смысл вкладываете в Infrastructure?
Doc>* MyApp.Bootstrap — умеет по конфигу собрать нужные DLL и подготовить IoC контейнер
Здравствуйте, Shmj, Вы писали:
S>Infrastructure оставляете для реализации? Обычно под этим подразумевают контракты. Иногда встречал MyApp.DataAccess.Infrastructure и MyApp.DataAccess.MsSQL или MyApp.DataAccess.Implementation.MsSQL
Интерфейсы, контракты итд — все в просто App.Infrastructure — все сборки с "уточненными" именами реализации. Мне кажется так меньше путаницы.
S>Какой смысл вкладываете в Infrastructure?
Все что связано в внешними по отношению к BL сервисами. Доступ к данным (в любой форме), т.к. BL не в курсе в какой конкретно БД или еще где лежат данные, доступы к внешним сервисам (например отправляем email) итд
Doc>>* MyApp.Bootstrap — умеет по конфигу собрать нужные DLL и подготовить IoC контейнер S>Подробнее.
А что именно? Тут лежит класс, который умеет из сборок выбирать нужное.
Здесь все от простого:
container.Register<ISomething, Something>()
до, например, обхода всех загруженных сборок и вытягивание классов и интерфейсов по заданному интерфейсу-маркеру.
На выход коентейнер, готовый выдавать реализации классов BL по интерфейсам.
Здравствуйте, Doc, Вы писали: Doc>При таком объеме я бы сделал иначе
А, ну в принципе мой вариант получается, только в твоём варианте иерархия нэймспейсов чуть поразветвлённее. Вероятно это "академичный" вариант. А значит я на верном пути. )
В какой статье/книжке такая архитектура описана?
Здравствуйте, Shmj, Вы писали: S>Если BL будет знать об UI, то вы не сможете использовать BL повторно.
Не знает он ничего об UI. У меня:
BL референсит MyApp.
UI референсит MyApp.
Ну и соотвественно:
MyApp.Database.MsSql.DLL и MyApp.Database.Oracle.DLL референсят MyApp.Database.DLL и MyApp.DLL.
Такие DLL'ки:
MyApp.DLL
MyApp.BL.DLL
MyApp.UI.DLL
MyApp.Database.DLL // базовые классы для MsSql и Oracle.
MyApp.Database.MsSql.DLL
MyApp.Database.Oracle.DLL
Т.е. и интерфейс и его реализация лежат _в_одном_нэймспейсе_, но "физически" находятся _в_разных_DLL_. Причём реализация всегда ссылается на интерфейс.
P.S. Т.е. так будет, когда я дорефакторю все свои "пока сошлюсь напрямую, а потом доделаю нормально" штуковины.