Сложная бизнес-логика. Как всё учесть?

Здравствуйте. Ситуация такая. Есть игровой сервер
(2 части у сервера: обработка запросов пользователей и realtime обработчики (полёты, бои и др))
На данный момент:
- есть около 20 сущностей (корабль, флот, пользователь...)
- 50 типов запросов от пользователей (модернизировать корабль, создать флот...)
- 5 realtime обработчиков (полёты, бои, добыча...).

Если я хочу добавить новый тип запроса от пользователя (например удалить космический корабль), я должен учесть это во всех местах (в бою и в других запросах). Если я это где-то забуду, то появляется баг. Что будет через год? Когда будет 100 сущностей...
Как справляться с такой сложной бизнес-логикой с большим набором правил? Может есть какие-то инструменты или подходы?

СМ. комментарий

ответ: #comment_433231
  • Вопрос задан
  • 817 просмотров
Решения вопроса 1
@nirvimel
Несколько банальных принципов ООП (применительно к вашему проекту):
Сущности, которых касается определенный запрос, должны быть либо унаследованы от одного класса, либо реализовывать один интерфейс (в дополнение к другим интерфейсам), который имеет методы управления этими сущностными, необходимые для реализации этого запроса.
Запросы также должны быть сгруппированы в дерево классов или наследовать набор интерфейсов. Сущности могут взаимодействовать не с конкретными классами запросов, а с общими интерфейсами, которых не так много, как различных запросов.
То же самое с realtime обработчиками и, вообще, с любыми элементами логики.
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
xmoonlight
@xmoonlight
https://sitecoder.blogspot.com
- есть около 20 сущностей (корабль, флот, пользователь...)
- 50 типов запросов от пользователей (модернизировать корабль, создать флот...)
- 5 realtime обработчиков (полёты, бои, добыча...).
По пунктам:
1. сущность = существительное
2. свойства объекта = прилагательное
3. тип запроса на действие к объекту = действие/глагол
4. real-time обработчик = триггер/heart-beat/time-tracker

Теперь, чтобы добавить что-то новое, просто используйте наследование объекта нужного архитектурного паттерна проектирования связей, созданного заранее.
Ответ написан
@Quilin
Full-stack разработчик
В очень плотно и сложно связанных системах, подобно описанной, хорошо работает паттерн ActorModel. Идея состоит в том, что ваш код разделен на огромную кучу крошечных подпрограмм, каждая из которых умеет
- выполнять атомарные действия
- отправлять сообщения другим акторам
- создавать новые акторы

Пользовательские запросы, таким образом, будут отправлять сообщение каким-то высокоуровневым акторам, которые в свою очередь будут отправлять новые сообщения, создавая граф действий. Любой процесс можно будет наглядно представить в виде такого графа.

Что касается вашего конкретного кейса, у меня создалось впечатление, что вас скорее интересует работа с единым хранилищем данных игровых объектов. Для этого тоже есть масса интересных решений, например Unidirectional Data Flow.
Хранение состояния игры в одном единственном месте, обращение к нему через прослойки-мапперы. Есть несколько вариантов реализации перехвата изменений - это может быть иммутабельное состояние, для которого достаточно будет считать дельту между старым и новым, публикуя изменения глобально, так чтобы участки программы, которым эти изменения интересны, сами их подхватывали, либо реактивное состояние, которое запоминает, кто к ней обращался за какими данными и уведомляет их о конкретных изменениях непосредственно в момент их применения.

Разумеется, для сложной игровой механики, совершенно необходимо писать и поддерживать юнит- и интеграционные тесты. С тестированием вы избежите огромной кучи багов, например пресловутого "если я это где-то забуду".

Но в любом из этих случаев, как грамотно сказал один программист: "Архитектуру легко придумать сложной", имея в виду, что нужно стремиться ее упрощать.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы