Страницы

Поиск по вопросам

пятница, 20 декабря 2019 г.

Паттерн MVC и организация проекта, где хранить расчеты в ASP.NET MVC

#c_sharp #aspnet_mvc #mvc #aspnet_core #архитектура


Где правильно хранить методы расчетов? 

К примеру, пользователь заполняет страницу с данными, после чего данные отправляются
на контроллер и тут согласно введенным данным мне нужно выбирать с какой таблицы БД
дергать значения, проводить кучу расчетов, огромное по 500 строк методы, с кучей if,
switch и циклов и собственно, где хранить эти все методы расчетов? 

Логичнее всего вынести их в отдельный класс типа Calculation.cs и дергать методы
оттуда, тогда где должен хранится данный класс с точки зрения паттерна MVC в папке
моделей, контроллеров или просто внутри проекта? Гугл говорит, что контроллер должен
быть тонкий, а модели толстыми, означает ли это, что все методы, которые связаны непосредственно
с одним классом нужно распихивать в модели, а что делать с методами в которых пересекаются
несколько моделей сразу?
Опять же, в каждой модели создавать context базы данных, что бы обращаться к каким-то
другим таблицам? 
    


Ответы

Ответ 1



Давайте представим, что у вас в приложении есть следующие слои: Контроллеры (Controllers) Модели представления (ViewModels) Модели предметной области (Models) В современном подходе рекомендуется разделять зависимости, поэтому смешивать в классе модели предметной области две вещи (саму предметную область и знание о том, как модель хранится в базе данных) не рекомендуется. Ну разве что вы используете такой паттерн, как Active Record, который именно за это и ругают. Поэтому я бы не рекомендовал вам в классы моделей засовывать ещё и db context -- это вещь, относящаяся к инфраструктурному слою приложения, а абстракции высших слоёв (предметная область - как раз высшая абстракция) не должны зависеть от низших (таких как инфраструктурная), вы наверное про это читали. Далее. В контроллеры приходят не модели, а модели представления - информация, подготовленная в виде, пригодном для отображения. Тут нет уже никакой логики, все данные давно посчитаны и зафиксированы, если получены из базы данных - материализованы, все свойства являются простыми типами, никаких классов, просто плоский список. Контроллер просто дёргает класс из application слоя, который вернёт ему vm. Собственно весь application слой - это те самые классы, обычно их называют Services, Managers, Queries (для cqrs) и т.п. образом. В качестве примера приведу такой. Приложение содержит протоколы игры в мафию. Основные модели: /// /// Протокол игры. /// public class GameProtocol { public int Id { get; set; } public DateTime Dated { get; set; } public GameResult Result { get; set; } public virtual IEnumerable Players { get; set; } } public class Player { public int Id { get; set; } public int GameProtocolId { get; set; } public GameProtocol GameProtocol { get; set; } public byte Seat { get; set; } public int PeopleId { get; set; } public People People { get; set; } public GameRole Role { get; set; } } Это у нас модели предметной области. Эти данные сохраняются в базу. А вот есть данные, которые не хранятся в базе, но легко могут быть рассчитаны -- рейтинг за определённый период: public class Rating { public int PeopleId { get; set; } public string PeopleTitle { get; set; } public int GamesTotal { get; set; } public int WinsTotal { get; set; } public int TotalScore { get; set; } public Dictionary GanesByCard { get; } public Dictionary WinsByCard { get; } } И это - как раз то, что получает контроллер, готовое, рассчитанное, соединения с базой нет (как нет и IQueryable), никаких методов нет, никаких вложенных классов нет (посмотрите например: можно было бы подключить класс человека из домена, но есть лишь два поля PeopleId и PeopleTitle) Контроллер тонкий — в нём одна строка типа var model = RatingService.GetRatingByPeopleId(id). Всю кучу расчётов с 500 строками и кучей if вы будете хранить в apllication слое, где у вас сервисы. Если у вас появится ещё один потребитель (скажем, помимо веб-сайта сделаете мобильное приложение), у вас просто появится ещё один тонкий потребитель, а вся логика по-прежнему останется на уровне application и domain model. В принципе, по настоящему, харкорному DDD (он очень многословен) полагается делать все методы на моделях, но вот ни у меня самого не сложилась такая практика, да и все asp.net приложения, которые я видел на гитхабе они придерживались упрощённого понимания чистой архитектуры: вся логика расчётов была не на классах модели, а в слое application. Я пожалуй, приведу несколько примеров asp.net приложений которые мне нравятся, возможно вы найдёте в них что-то интересное: пример приложения из выступления Equinox Project Приложение с давней историей, несколько раз сильно переписывалось на более новые технологии (asp.net core 1 -> 2 -> 2.1), сейчас там cqrs и MediatR. Приложение интересно тем, что реализован достаточно честный DDD. Пример приложения из книги Марка Симана Dependency injection c# - интересен своей простотой, особенно тем, кто читал книгу Realworld приложение. Их вообще целое семейство. Ключевая фишка их - "показать реальное приложение, а не hello world". Лично мне они нравятся тем, что можно взять пару бекенд+фронт, которые умеешь, а потом не меняя бекенд заменить фронт. Скажем, asp.net-core + angular, а потом asp.net-core + vue. По архитектуре там MediatR

Ответ 2



Гугл говорит, что контроллер должен быть тонкий Гугл прав ). Смысл контроллера в данном шаблоне именно в том, чтобы быть связующим звеном между отображением (view) и данными (model). В его задачу входит получение данных от отображения в широком смысле (не только из гуи, но из сети, от устройства и так далее. В целом, это вопрос тонкий и зависит от конкретной задачи), первичная проверка этих данных (валидность, эксплойты и прочее, прочее. Хотя, опять же все это можно и нужно делать не методами самого контроллера, а методами вспомогательных классов), запрос соответствующих моделей и передача в методы этих модели соответствующих данных для дальнейшей обработки (например, сохранения). Если необходимо, то после обработки в объекте модели, уже обработанные (вторичные) данные отправляются обратно отображению. По аналогии с вождением автомобиля - контроллер это человек за рулем. Он решает когда что дернуть и повернуть в зависимости от входных данных - ситуации на дороге (отображения). Теперь на счет сложного и толстого - моделей. Если обратиться к той же аналогии с автомобилем, то модель эта вся та сложность, которая спрятана у машины под "капотом". Вся вычислительная сложность, работа с БД, а также, возможно, работа с оборудованием и сетью (может быть и в отображении, как я отмечал ранее, это вопрос задачи) должны быть спрятаны за интерфейсом модельных классов. а что делать с методами в которых пересекаются несколько моделей сразу В данном случае не нужно забывать про первичный смысл класса (класс - это данные, как состояние объекта класса и методы их обработки, как изменение состояния, объединенные единым сущностным/объектным смыслом). Модели это тоже классы и каждая модель должна соответствовать базовой идее ООП, в вашем случае ничто не мешает вам плодить необходимые не только первичные сущности (связанные с таблицами БД), но так же и вторичные, связанные со взаимодействием этих самых первичных сущностей/моделей. Судя по всему задача у вас не маленькая, поэтому рекомендую тщательно и не спеша подумать об архитектуре. Расписать все (лучше, основные) сущности (не вылезая за рамки первичного смысла класса) и методы их взаимодействия друг с другом

Комментариев нет:

Отправить комментарий