Здравствуйте уважаемое комьюнити!
Раньше меня всегда выручали на тостере, надеюсь, что и сейчас получится решить проблему.
Вводная:
У меня есть программа, которая написана в стиле N-Layer с использованием DTO для транспортировки данных между слоями.
Если подробнее, то есть слой DAL, BLL, UI и отдельный слой DTO, где лежат контейнеры для данных.
В слое DAL находится EF и классы-репозитории. Классы реализуют CRUD операции и всевозможные дополнительные (об этом позже).
Для каждой сущности свой класс-репозиторий (UserRepository, OrderRepository etc...). На уровне выше (BLL), у меня располагаются классы-сервисы. Эти классы разделены по назначению (ImportService, ExportService, MediaService). А на последнем уровне находится UI, который ничего особо не объявляет, кроме ViewModel. UI просто создает экземпляры сервисов и дергает за нужные методы, передавая необходимые параметры. Стоит упомянуть про DTO, в котором определены контейнеры, которые ходят от слоя к слою.
Т.о. когда я получаю в классе репозитория entity объект User, с помощью AutoMapper я его привожу к контейнерному типу и возвращаю в вызвавший метод из сервиса.
Суть проблемы:
Проблемы начинаются, когда мне нужно более детально проработать архитектуру.
Допустим у меня есть сервис, который проверяет пользователей по определенному критерию, а затем проставляет им соответствующий тип. Как это правильно реализовывать? Создавать также по назначению класс-репозиторий и в нем реализовывать все методы для работы сервиса? Или необходимо все методы добавлять в класс UserRepository? (Но это же GOD объект получается). Или нужно создать методы, а-ля "GetValidTypeId", потом получать таким методом идентификатор и инициализировать им все контейнеры, а затем отдавать коллецию на Update классу-репозиторию?
А может стоит писать кучу методов, вот таких например: "SetUserValid", "SetUserInvalid"?
Заключение:
В общем куча подобных вопросов возникает постоянно. День ото дня меня посещает мысль, что я делаю все неправильно и мой подход в корне неправильный. К использованию этой архитектуры пришел сам, так как в одной фирме спрашивали почему я не сделал на основе нее тестовое задание. Основы в интернете откопал, а вот более детально не могу найти. Мне нужна толковая литература или человек, которому я смогу изредка задать пару вопросов, предварительно конструктивно их оформив и проведя минимум пару тройку часов в интернете в поисках.
На мой взгяд у вас получилось слишком строгое следование принципам N-Layered Architecture и Repositiry Pattern. Всё вроде сделано правильно, но этот подход, по-моему, не покрывает все возможные случаи. С одним из таких вы и столкнулись. Строгое следование CRUD в репозитории не оставляет места для более высокоуровневых операций, поскольку либо их надо решать уровнем выше, но тогда это скажется на быстродействии и несколько усложнит код.
Здравствуйте Александр.
В первую очередь большое спасибо за ответ и за полезные ссылки.
Я ознакомился с содержимым большинства из них, но для меня по-прежнему остается непонятным, какой же подход при построении архитектуры использовать.
Допустим я буду использовать луковую архитектуру, тогда все равно у нас остаются сервисы и репозитории. В сервисах у нас бизнес логика, в репозиториях доступ к данным. И опять встает вопрос как правильно с этими данными оперировать.
Вернусь к примеру, что я приводил ранее - то ли стоит делать кучу методов в классе репозитория с почти идентичной функциональностью (SetUserValid, SetUserInvalid, SetUserFail), или стоит все таки делать методы для получения соответствующих идентификаторов и проставлять их в доменном объекте, а затем отдавать на обновление в репозиторий?
Мне хотелось получить именно best practice к построению архитектуры, чтобы не наступать на грабли, которые до меня кто-то уже собрал...
Антон, сама N-layered архитектура уже давно известна (есть даже книжка от MS - прямую ссылку дать не могу, но можно спросить у гугла "n-layered domain-oriented architecture guide with .net pdf").
Единственно верного решения, наверное, на данный момент не существует. Проблемы с паттерном "Репозиторий" я вам описал - есть лучше разделить на CUD-объекты (ваш репозиторий) и Query-объекты - для более сложных выборок.
Методы (SetUserValid, SetUserInvalid, SetUserFail) я думаю плодить не стоит. Ведь скорее всего у вас состояние пользователя выражено в каком-то enum, которое действительно лучше проставить в доменном объекте, на основе логики приложения.
Александр Евсеев: спасибо за гайд и все остальные советы. Насчет CQRS я читал ранее, но только сейчас подумал, что этой действительно может подойти мне)
Спасибо за помощь