Рефакторинг доменной модели с изменчивостью и циклическими зависимостями для работы в 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 мог бы поговорить об их применимости здесь.

Другие вопросы по тегам