Могут ли сервисы общаться между собой?

Всем привет!
Вкратце архитектура такая:
entity -> repository -> service -> controller
entity - представлена анемичной моделью, поэтому вся логика находится в сервисах
Пример
сущности - CheckoutFlow, EmailTemplate. CheckoutFlow содержит EmailTemplate
сервисы CheckoutFlowService (CRUD), EmailTemplateService (CRUD)
Сервисы работают с сущностями через репозитории
Возникает задача дублирования CheckoutFlow, добавляем метод в сервис CheckoutFlowService::duplicate
Мы дублируем сущность и сохраняем
Но нам нужно также продублировать и EmailTemplate сущности соответственно и тут 2 варианта есть
1) Сервисы у нас обьщаються только с репозиториями и поэтому мы в CheckoutFlowService дублируем через репозиторий CheckoutFlowRepository свою сущность, а через репозиторий EmailTemplateRepository в этом же методе свою (EmailTemplate)
Минусы подхода - что если нам нужно будем просто продублировать EmailTemplate и нам придется дублировать код
2) Мы можем создать метод в сервисе EmailTemplateService::duplicate, который бы дублировал просто EmailTemplate
Плюсы - можем использовать отдельно дублирование
Минусы - тогда придется инжектить в CheckoutFlowService сервис EmailTemplateService и я не знаю насколько это верно. Но еще минус в перекрестных ссылках, когда CheckoutFlowService содержит EmailTemplateService, а EmailTemplateService в свою очередь CheckoutFlowService и решение для этого ленива загрузка, что как-то совсем не внушает доверия в том, что мы движемся в нужном направлении.

CheckoutFlowController
class CheckoutFlowController
{
    private $checkoutFlowService;

    public function __construct(
        CheckoutFlowService $checkoutFlowService
    ) {
        $this->checkoutFlowService = $checkoutFlowService;
    }
    
    public function duplicate(
        Request $request,
        int $id
    ): Response {
        $body = $request->request->all();

        $checkoutFlow = $this->checkoutFlowService->getCheckoutFlowById($id);

        $newCheckoutFlow = $this->checkoutFlowService->duplicateCheckoutFlow(
            $checkoutFlow,
            $body['name'],
            $body['slug_name']
        );

        $data = [
            'checkout_flow' => $newCheckoutFlow
        ];
        $response = new JSendResponse(JSendResponse::SUCCESS, $data);

        return new JsonResponse($response, Response::HTTP_OK);
    }
}


CheckoutFlowService
class CheckoutFlowService
{
    private $checkoutFlowRepository;
    
    private $emailTemplateService;

    public function __construct(
        CheckoutFlowRepositoryInterface $checkoutFlowRepository,
        EmailTemplateService $emailTemplateService

    ) {
        $this->checkoutFlowRepository = $checkoutFlowRepository;
        $this->emailTemplateService = $emailTemplateService;
    }

    public function duplicateCheckoutFlow(CheckoutFlow $checkoutFlow, string $name, string $slugName): CheckoutFlow
    {
        $campaign = $checkoutFlow->getCampaign();

        $newCheckoutFlow = null;
        $newEmailsData = null;

        $currency = $checkoutFlow->getCurrency();
        $currencyId = ($currency !== null) ? $currency->getId() : null;

        $shippingPreset = $checkoutFlow->getShippingPreset();
        $shippingPresetId = ($shippingPreset !== null) ? $shippingPreset->getId() : null;

        $countriesPrices = $checkoutFlow->getCountriesPrices();
        $countriesPrices = ($countriesPrices !== null) ? json_decode($countriesPrices) : null;

        $metaData = $checkoutFlow->getMetaData();
        $metaData = ($metaData !== null) ? json_decode($metaData) : null;

        $cfTemplate = $checkoutFlow->getCfTemplate();

        $emailsData = json_decode($checkoutFlow->getEmailsData());

        if ($emailsData !== null) {
            $newEmailsData = [];

            foreach ($emailsData as $emailData) {
                $emailTemplate = $this->emailTemplateService->getEmailTemplateById($emailData->email_template_id);
                $newEmailTemplate = $this->emailTemplateService->duplicateEmailTemplate($emailTemplate);
                $emailData->email_template_id = $newEmailTemplate->getId();
                $newEmailsData[] = (array) $emailData;
            }
        }

        $newCheckoutFlowArray = [
            'name' => $name,
            'slug_name' => $slugName,
            'flat_rate_shipping_enable' => $checkoutFlow->getFlatRateShippingEnable(),
            'currency_by_country' => $checkoutFlow->getCurrencyByCountry(),
            'countries_prices' => $countriesPrices,
            'campaign_id' => $campaign->getId(),
            'currency_id' => $currencyId,
            'checkout_flow_template_id' => $cfTemplate->getId(),
            'flat_rate_shipping_price' => $checkoutFlow->getFlatRateShippingPrice(),
            'meta_data' => $metaData,
            'emails_data' => $newEmailsData,
            'shipping_preset_id' => $shippingPresetId
        ];

        $newCheckoutFlow = $this->checkoutFlowRepository->save(new CheckoutFlow($newCheckoutFlowArray));

        return $newCheckoutFlow;
    }
}


EmailTemplateService
class EmailTemplateService
{
    private $emailTemplateRepository;

    private $checkoutFlowService;
    
    public function __construct(
        EmailTemplateRepositoryInterface $emailTemplateRepository,
        CheckoutFlowService $checkoutFlowService
    ) {
        $this->emailTemplateRepository = $emailTemplateRepository;
        $this->checkoutFlowService = $checkoutFlowService;
    }

    public function addEmailTemplate(array $fields): EmailTemplate
    {
        $emailTemplate = new EmailTemplate();

        $emailTemplate->setSubject($fields['subject']);
        $emailTemplate->setData($fields['data']);

        return $this->emailTemplateRepository->save($emailTemplate);
    }

    public function duplicateEmailTemplate(EmailTemplate $emailTemplate): EmailTemplate
    {
        $newEmailTemplateArray = [
            'subject' => $emailTemplate->getSubject(),
            'data' => $emailTemplate->getData()
        ];

        return $this->addEmailTemplate($newEmailTemplateArray);
    }
}


Код представлен просто в качестве примера, что-то опущено, что-то не работает

Какие есть способы реализации задуманного?
  • Вопрос задан
  • 318 просмотров
Пригласить эксперта
Ответы на вопрос 2
@Iloveski
Исходя из того, как описано - это норма. Главное правило, методы репозитория вызывает только свой сервис. А вот откуда вызывается сервис, из другого сервиса или с уровня выше, не столь важно.
Ответ написан
@hwnd32
Сервисы не должны перекрестно ссылаться. Меняйте бизнес логику.
Если у вас есть перекрестные ссылки значит вы делаете что-то не так.
Ответ написан
Ваш ответ на вопрос

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

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