Рефакторинг доменной модели с изменчивостью и циклическими зависимостями для работы в Scala с хорошими практиками FP?
Я из OO (C#, javascript), и Scala - мой первый набег в FP.
Из-за моего прошлого у меня возникают проблемы с реализацией доменной модели, которая хорошо подходит для моей доменной проблемы, а также соответствует рекомендациям для FP, таким как минимальная изменчивость в коде.
Во-первых, краткое описание моей проблемы с доменом, как сейчас.
- Основные доменные объекты:
Event, Tournament, User, and Team
Teams
состоят изUsers
- И то и другое
Teams
а такжеUsers
может присутствоватьTournaments
которые происходят вEvent
Events
состоит изUsers
а такжеTournaments
- Результаты, статистика и рейтинги для
Teams
а такжеUsers
кто конкурирует черезTournaments
а такжеEvents
будет главной особенностью.
Учитывая это описание проблемы, моя первоначальная идея для домена заключается в создании объектов, в которых двунаправленные циклические отношения являются нормой - что-то вроде графа. Моя точка зрения заключается в том, что возможность доступа ко всем связанным объектам для любого заданного объекта предложит мне самый простой путь для программирования представлений для моих данных, а также для манипулирования ими.
case class User(
email: String,
teams: List[TeamUser],
events: List[EventUser],
tournaments: List[TournamentUser]) {
}
case class TournamentUser(
tournament: Tournament,
user: User,
isPresent: Boolean){
}
case class Tournament(
game: Game,
event: Event,
users: List[TournamentUser],
teams: List[TournamentTeam]) {
}
Однако, углубившись в лучшие практики FP, я обнаружил, что мой мыслительный процесс несовместим с принципами FP. Циркулярные ссылки осуждаются и кажутся почти невозможными с неизменными объектами.
Учитывая это, я сейчас борюсь с тем, как реорганизовать мой домен, чтобы он соответствовал требованиям для хорошего FP, и в то же время поддерживал в здравом смысле организацию "объектов реального мира" в домене.
Некоторые варианты я рассмотрел
- Используйте отложенные ссылки val и by-name - моя проблема в том, что кажется, что становится неуправляемым, когда домен становится нетривиальным
- Вместо этого используйте однонаправленные отношения - с помощью этого метода я вынужден объявить некоторые доменные объекты объектами второго класса, доступ к которым возможен только через другие объекты. Как бы я выбрал? Все они кажутся одинаково важными для меня. Кроме того, это потребует построения запросов "против структуры", чтобы получить простой список объектов второго класса.
- Используйте косвенное обращение и сохраняйте список идентификаторов для отношений - это удаляет циклические зависимости, но затем создает большую сложность, потому что мне пришлось бы писать дополнительную бизнес-логику, чтобы эмулировать обновления отношений и делать дополнительные поездки в БД, чтобы получить любые отношения.
Поэтому я борюсь с тем, как изменить либо мою реализацию, либо мою исходную модель, чтобы добиться связывания, которое, я думаю, мне нужно, но "правильным способом" для Scala. Как мне подойти к этой проблеме?
TL; DR - Как мне моделировать домен, используя хорошие методы FP, когда домен, кажется, требует двунаправленного доступа и изменчивости в своей основе?
1 ответ
Предполагая, что ваша модель домена поддерживается базой данных, в случае, который вы отметили выше, я бы установил свойства "groups", "events" и "tournaments" вашего класса User def, которые извлекают соответствующие объекты из базы данных (вы может реализовать стратегию кэширования, если вы беспокоитесь о чрезмерных вызовах БД). Это может выглядеть примерно так:
case class User(email: String)) {
def teams = TeamService.getAllTeams.filter( { t => t.users.contains(this) } )
//similar for events and tournaments
}
Другой способ сказать, что это может быть то, что ваши циклические зависимости имеют единственное "авторитетное" направление, тогда как ссылки в другом направлении рассчитываются из этого. Таким образом, например, когда вы добавляете пользователя в турнир, ваша функция должна только возвращать новый объект турнира (с добавленным пользователем), а не новый объект турнира и новый объект пользователя. Кроме того, вместо того, чтобы явно моделировать таблицу ссылок TournamentUser, Tournament может просто содержать список кортежей User/Boolean.
Другим вариантом может быть использование линз для изменения модели вашего домена, но я не реализовал их в такой ситуации. Может быть, кто-то с большим опытом в FP мог бы поговорить об их применимости здесь.