Чистая архитектура: как уменьшить сложность при написании бизнес-правил приложения?
Предположим, у нас есть следующий сценарий "Создать пользователя":
- Пользователи могут зарегистрироваться в приложении, используя Facebook, Google+ или LinkedIn;
- Бэкэнд должен получить некоторую базовую информацию профиля для регистрации пользователя (электронная почта, firstName и lastName);
- Пользователи регистрируются с помощью "идентификатора клиента" (просто добавляя сложность бизнес-правилу);
- Когда процесс регистрации завершен, данные должны быть отправлены в тему уведомлений.
Я могу представить запрос на создание пользователя со следующей структурой:
{
"clientId": "someClientId",
"authProvider": "FACEBOOK | GOOGLE | LINKEDIN",
"accessToken": "someAccessToken"
}
Итак, подумав о процессе регистрации / проверки, мы бы получили:
- Проверьте, действителен ли запрос на создание пользователя;
- Проверьте, действительно ли clientId;
- Попробуйте получить информацию о профиле из социальной сети API;
- Проверьте, заполнена ли вся необходимая информация профиля;
- Проверьте, существует ли пользователь в базе данных;
- Зарегистрировать пользователя;
- Отправьте данные в тему уведомлений;
- Передайте данные докладчику.
Если перейти прямо к варианту использования, у нас будет такой конструктор, как:
CreateUserUseCase(
ApplicationClientGateway applicationClientGateway,
SocialNetworkGateway socialNetworkGateway,
UserGateway userGateway,
NotificationGateway notificationGateway,
Presenter presenter
)
и метод execute:
execute(CreateUserRequest request)
// validates the payload
// something like
if (request == null)
presenter.setError(someError);
// validates the clientId
applicationClientGateway.findById(request.getClientId())
// retrieves the profile information
// how to inject dinamically the implementation for
// Facebook, Google or LinkeIn based on a request parameter?
profile = socialNetworkGateway.findByAccessToken(request.getAccessToken());
// checks if the user exists
userGateway.findByEmailAndAuthProvider(profile.getEmail(), request.getAuthProvider());
//register the user
userGateway.insert(user);
//sends the notification
notificationGateway.send(user);
// sets the result
presenter.setResult(user);
Теперь у нас есть конструктор с множеством аргументов (запах кода?) И как минимум 5 шагов проверки в методе execute.
Это выглядит как нарушение SRP, поэтому, как мы можем разложить этот код, чтобы уменьшить сложность в интеракторе?
2 ответа
Прежде всего, давайте разберем это в несколько небольших шагов:
1) Относительно докладчика, похоже, что вы заинтересованы в том, чтобы дать рабочему процессу какой-то вывод, верно? Предполагая, что, возможно, будет лучше вернуть то, что вы хотите из сценария использования и обработать этот слой выше. (-1 параметр в конструкторе)
2) Как и в других ответах, похоже, что ваш сценарий использования имеет много ответственности прямо сейчас. Я предлагаю вам разбить это более чем в одном случае. Что-то вроде:
... Your first gateway (API)
..... ValidateClientId.execute();
..... profile = RetrieveProfile.execute();
..... InsertUser.execute(...)
3.1) В отношении ввода правильного компонента на основе правильной социальной сети вы можете обрабатывать эту логику ВНУТРИ шлюза, а не до его вызова. Помните, что один шлюз МОЖЕТ вызвать другой шлюз (они находятся на одном уровне). Итак, я предлагаю вам использовать что-то вроде.
В usercase -> socialNetworkGateway.findByAccessToken(...) Внутри шлюза вы можете переключаться и вызывать что-то вроде FacebookGateway, GoogleGateway и т. Д.
SRP не говорит, что у вас должны быть маленькие методы или только несколько параметров конструктора. SRP говорит, что "должна быть только одна причина для изменения кода".
Насколько я вижу, вы явно реализовали "последовательность бизнес-логики", необходимую для регистрации нового пользователя. Несмотря на то, что для этого могут потребоваться некоторые "внешние сервисы" и / или репозитории, есть еще одна причина изменить логику этого кода: если меняется логика "как зарегистрировать пользователя".
С этой точки зрения вы не нарушаете SRP.
Согласно рисунку дяди Бобса о "потоке управления" ( https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html), также совершенно правильно передать докладчика.
Если вы все еще чувствуете необходимость уменьшить зависимости вашего класса сценария использования, я бы посоветовал изучить шаблон "единица работы" и проверить, имеет ли смысл объединять некоторые из зависимостей.
Более подробную информацию о "Что такое вариант использования в чистой архитектуре" вы можете найти в моей серии блогов: http://www.plainionist.net/Implementing-Clean-Architecture-UseCases/