Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
В этом PR опишу, как бы я порефакторил текущую версию приложения.
Классно, что вынес логику в service-объект – стало удобнее! По коду такие комменты:
attribute_color,attribute_price,experiment_params) я бы предложил использовать command-query separation принцип (https://ru.wikipedia.org/wiki/CQRS), когда у любого метода есть одна из двух функций: "команда", типа "сделай что-то" или "запрос", типа "дай мне что-то". Сейчас же получается смешение - имя как будто query, а логика – как будто command. То есть должно быть либоparams = experiment_params, либоset_experiment_paramsвнутри которого выставляем инстанс-переменную@params(у command в имени всегда есть глагол)query_to_db_valid?, внутри которого мы делаем запрос в базу. Из-за этого получается, что мы делаем лишний запрос – можно всё объединить в одно условие:@params, а потом на следующей строчке делаем это ещё раз https://github.com/Froz10/experiments-api/blob/master/app/services/api/experiment_service.rb#L14 – получается можно 13 строчку удалить и ничего не изменитсяcallприсваиваются instance-переменные, которые в итоге не используются (@experiment,@experiments) – лучше в таком случае использовать локальные переменные (а@experimentвообще можно в переменную не записывать)В итоге сервис получится вот таким:
После такого рефакторинга видно, что в начале метода снова нарушается CQRS (
Experiment.colors,Experiment.prices). Для этого надо ещё порефакторить модельExperiment(тут комменты, которые я в прошлый раз что-то не отметил):colorsиpricesна самом деле нам нужны только для того, чтобы инициализировать переменные@colorsи@prices. Получается, что мы можем вместо явного вызова этих методов в методахcolorиpriceвызывать методы, а не инстанс-переменные. Вот так:callможем вообще убрать и всё будет так же работать.find_or_create_by. Получается вообще весь сервис – это один вызов метода с блоком из 2 строк внутри:distributionизlib, а внутри этого файла декларируем 2 класса –ColorиPrice. Это можно назвать антипаттерном в rails, т.к. по стандартным соглашениям по имени класса должно быть понятно, где он лежит. Тут есть 2 варианта:color.rbиprice.rb), либо, например, добавить namespace, чтобы классы называлисьDistribution::ColorиDistribution::Price. Мне лично больше нравится второй вариант:ExperimentотDistribution– пусть они связываются только через наш сервис-объект. Для этого уберем метод color/colors и price/prices из модели, а сервис изменим следующим образом:По названию метода
distributionнепонятно, что он делает. Опять же, либо мы делаем command "сделай что-то" (в таком случае будетdistrubute), либо "дай мне что-то", напримерnext_value. Мне наверное больше нравится второй вариант – переименуем методdistributionвnext_valueТеперь перейдём к контроллеру. Тут можно немного упростить логику метода
index. Тут снова нарушение cqrs – у нас есть методdevice_header, который назван как query, а выполняет функциюcommand(т.к. выставляет значение instance-переменной). Вместо этого я бы сделал его как query (при этом добавив memoization и убрав лишние "сущности" и назвав instance переменную так же, как и метод – неtoken, аdevice_token):А теперь можно добавить стандартный подход к аутентификации в rails и сделать её в before_action:
А метод
index, тем самым разгрузим и оставим только основнвую логику:Кстати, тут у нас немного путанница: на самом деле пользователь может попасть только в один эксперимент (именно поэтому я в сервисе использовал не
where, который возвращает коллекцию, аfind_by, который возвращает конкретную сущность). А в названии у нас множественное число – что не верно. Поэтому переименуем)