
Sergey
15.06.2017
23:25:31
если это все в рамках одного контекста - я бы сделал сервис сверху который руководит процессом и декларирует "создай, пересчитай" или "удали, пересчитай"

Big_Shark
15.06.2017
23:26:19

Sergey
15.06.2017
23:26:21
у меня же в проекте подобная штука между "добавь, вот та статистика где-то вдалеке должна пересчитаться"

Google

Sergey
15.06.2017
23:26:45
и я тупо обхожу все сущности в UoW и собираю ивенты
а потом тригерю. Могу в очередь их запихнуть, могу там же обработать
но
это повторюсь - если у тебя между двумя модулями надо связь организовать. В пределах одного - чем явнее тем лучше
вопрос в том нужно тебе связанность понижать или с ней все ок
ну и смотреть с позиции open/close

Big_Shark
15.06.2017
23:28:42
там по факту 3 строки
+ public function updatePropertyRating()
+ {
+ $property = $this->getProperty();
+ $property->calculateRating();
+ $property->save();
+ }
Хотя конечно calculateRating в модели не должен находится, это косяк
В любом случае пихать все в 1 сервис, не очень кошерно, а у меня есть один основной объект
по этому лучше делать через ивенты

Sergey
15.06.2017
23:32:24

Big_Shark
15.06.2017
23:32:32
@fes0r а разве в доктрине нет походего функционала который может кидать ивенты?

Google

Sergey
15.06.2017
23:32:55
ну то есть, это не от "в модели/в сервисе" зависит, а просто от того как ты на объекты дробишь и соблюдаешь ли ты принцип информационного эксперта

Big_Shark
15.06.2017
23:33:17
кто тебе такое сказал
Ты думаешь норм что модель достает все ревью который одобрены и считает рейтинг, а потом записывает у себя в переменную?

Sergey
15.06.2017
23:33:49
public function updateSomething()
{
// do something
$this->remember(new SomethingHasBeenUpdated($this->id));
}
и тогда да - зашить в сервис
у меня к примеру есть логика - надо пересчитать лимиты продавцов

Big_Shark
15.06.2017
23:34:49

Sergey
15.06.2017
23:35:28
public function calculateLimits(LimitsCalculator $calculator)
{
$this->limit = $calculator->calculate($this->id, $this->registeredAt);
$this->remember(new MerchantLimitsUpdated($this->id, $this->limit));
}

Big_Shark
15.06.2017
23:36:41
Ну тут если судить по принципу открытости закрытости, то проперти знает о своих ревью и их рейтингах, поэтому может это делать)

Sergey
15.06.2017
23:36:55
так... давай по другому... я использую ивенты потому что мне не хочется что бы "тригеры" знали что по ним происходит.

Big_Shark
15.06.2017
23:37:57
то запихнуть его в ивент, и ивент вызывать по событию из модели при сейве. или типа того

Sergey
15.06.2017
23:38:24
// Add review
// recalculate rating
две строчки кода, явно описывающие что происходит

Big_Shark
15.06.2017
23:39:32

Google

Sergey
15.06.2017
23:39:56
ну то есть лучше уж явно это сделать
так намного проще найти ошибку
и не надо прыгать по файлам
что бы отследить что происходит

Big_Shark
15.06.2017
23:40:44


Sergey
15.06.2017
23:40:53
то есть мое мнение - по дефолту надо делать именно так. Что бы у тебя на операцию, юзкейс, был файлик, который описывает полностью что происходит.
короч, если ты хочешь что бы я тебе сказал "сделать страшную волшебную хрень которая по каким-то стремным правилам мониторит изменения и тд." - нет. Это неявно, это потенциально баги, это сложно, это дорого суппортить, это сложнее объяснять новым челикам
у меня алгоритм "как сделать цепочку операций" простой:
- Можно ли это сделать явно? Ну то есть описать "сценарий" который декларирует порядок действий в одном месте? Если можно - так и делаем.
- Порядок действий хочет зависимости из разных мест и мне не очень хочется что бы зависимости знали друг о друге. Или не хочется делать циклические зависимости. Тогда доменные ивенты.


Big_Shark
15.06.2017
23:43:27
пиши тесты)
Мне кажется достаточно странные стесты будут

Sergey
15.06.2017
23:43:40

Sergey
15.06.2017
23:43:49
ты же запускаешь код когда работу ведешь?

Big_Shark
15.06.2017
23:44:07

Sergey
15.06.2017
23:44:13
еще лайххак небольшой - когда планируешь работу - прописывай критерии приемки. Типа что и как должно работать.
обычный интеграционный тест по позитивному сценарию
и несколько юнитов которые проверят отдельные элементы

Big_Shark
15.06.2017
23:45:27

Sergey
15.06.2017
23:45:55

Google

Sergey
15.06.2017
23:46:18
но да, как-то так
НО, рейтинг этот как к ревью относится?

Big_Shark
15.06.2017
23:46:32

Sergey
15.06.2017
23:46:45

Big_Shark
15.06.2017
23:46:56

Sergey
15.06.2017
23:47:04
ты же понимаешь что вся соль не в том сколько кода надо написать, а в том как легко его потом читать
ну то есть сэкономишь ты 1 минуту
и потом потратишь лишние пол часа на то что бы разбираться с логикой

Big_Shark
15.06.2017
23:48:46

Sergey
15.06.2017
23:49:36
у меня есть проект где чел сделал "ядро" которое должно было ему позволить сделать CRUD не в 5 строчек а в 3. Делал он эту херню неделю. По итогу спустя год что бы разобраться в какой-то мелочи разработчик который после него сидел потратил день. Хотя если бы не эта желания "экономить на строчках" можно было бы логику прописать явно и в целом потратилось бы 5 минут на то что бы разобраться что куда и сделать фичу

Admin
ERROR: S client not available

Sergey
15.06.2017
23:49:47

Big_Shark
15.06.2017
23:50:27
так не юзай ивенты)
Ну если не ивент, то сервис, но в любом случае надо вызывать пресчет, вопрос где, когда и как)

Sergey
15.06.2017
23:51:26
$property->submitReview(Review::builder()
->withAuthor($author)
->withRating($rating)
->withMessage($message)
->build()
);
а внутри
public function submitReview(Review $review)
{
$this->reviews->add($review);
$this->remember(new ReviewSubmittedEvent($this->id));
}
а потом
public function onReviewSubmittedEvent(ReviewSubmittedEvent $event)
{
$propertyId = $event->property;
$this->connection->exec('UPDATE properties SET rating = AVG(..)', ['id' => $propertyId]);
}
если тебе вообще не надо разделять Updated или Submitted

Google

Big_Shark
15.06.2017
23:54:23
Ок, спасибо, идею понял

Sergey
15.06.2017
23:54:25
можно обобщить событие до ReviewsChangedEvent
профит
ты тригеры все можешь юнит тестами протестить, что ивент есть
ивенты отработают ТОЛЬКО если транзакция успешно зафлашится
ну и останется проверить простеньким интеграционным тестом что сама операция работает корректно

Big_Shark
15.06.2017
23:55:28
Ну это уже просто

Sergey
15.06.2017
23:55:38
у меня так например одна и та же операция требуется по 10-ти тригерам с весьма непростой логикой

Big_Shark
15.06.2017
23:55:39
слушай, а ты такие билдеры используешь?

Sergey
15.06.2017
23:55:45
причем тригеры разные

Big_Shark
15.06.2017
23:56:05
Review::builder()
->withAuthor($author)
->withRating($rating)
->withMessage($message)
->build()
или это просто пример из головы)

Sergey
15.06.2017
23:57:18
ну это просто класс ReviewBuilder. Что бы не делать сущность с сеттерами и при этом не делать сущность с миллионом аргументов в контрукторе. Можешь в better java почитать например.
Если лень писать руками - ну можно плагин к шторму написать например))))

Big_Shark
15.06.2017
23:58:09

Sergey
15.06.2017
23:58:17
да

Big_Shark
15.06.2017
23:58:34
У меня просто както так
public static function createBlockout(Period $datePeriod, Property $property, String $description, User $user, $status): Booking
{

Sergey
15.06.2017
23:58:37
все что имеет больше двух аргументов в контрусторе стараюсь для таких вещей делать билдеры
потому что где 3 там станет 4
а где 2 - далеко не всегда становится 3

Big_Shark
15.06.2017
23:59:11
Есть логика

Sergey
15.06.2017
23:59:22
больше кода приходится писать