2016-06-09 3 views
4

В моем проекте я решил использовать шаблон обслуживания (возможно, с шаблоном репозитория), чтобы иметь дело с бизнес-логикой в ​​моем приложении. Я имею, например, модель Client, которая представляет клиента и соответствующий ClientService, который отвечает за специфическую бизнес-логику клиента.Laravel: Service/Repository Шаблон и код дублирования

class ClientService extends Service implements ClientServiceContract 
{ 
    public function create(array $attributes) 
    { 
     // Create a new client... 
    } 

    public function doSomethingElse(Client $client) 
    { 
     // Do something else 
    } 
} 

Скажем, например, у меня есть еще один сервис UserService, который похож на ClientService выше в том, что он имеет методы для создания и делать другие вещи, чтобы User моделей.

Теперь, на моем сайте, представьте, что у меня есть форма, которую кто-то может заполнить, чтобы зарегистрировать свою заинтересованность в том, чтобы стать клиентом. В моей задней системе я хотел бы создать кнопку, которая берет интересную запись клиента ClientInterest и создает Client, User, связывает два и, наконец, отправляет электронное письмо новому пользователю с подробной информацией.

Где, используя шаблон обслуживания, было бы лучше всего поставить эту логику?

Я рассмотрел:

  1. Создание службы и метод ClientInterestService::createClientAndUser(...), который будет использовать ClientService и UserService классы для создания Client и User экземпляров, а затем осуществляют связь перед запуском события, которое посылает Эл. адрес. Этот подход означает, что я не дублирую код, однако я объединяю классы вместе, и я нарушаю некоторые принципы SOLID. Я не уверен, но у меня такое чувство, что это не будет здорово для тестирования.

  2. Как описано выше, создать класс обслуживания и метод для выполнения логики, но вместо того, чтобы использовать два других услуг, я хотел бы написать логику, чтобы создать Client и User экземпляров, осуществлять связь и инициировать событие для отправки электронной почты. Этот подход кажется более приятным, мой код более слабо связан, и я не нарушаю никаких принципов SOLID, однако я потенциально дублирую код.

  3. Проще говоря, логика, которую я бы имел в ClientInterestService::createClientAndUser(...) в моем контроллере. Выполнение этого будет означать, что у меня есть бизнес-логика в моем контроллере, которая поражает точку зрения на наличие сервисов.

ответ

1

Я думаю, что если вы разделите это на более мелкие шаги, вы сможете достичь СУХОЙ архитектуры.Эти шаги, которые я вижу, являются:

  • Создание клиента
  • Создать пользователь
  • Associate (по сводной таблице, соединение таблицы и т.д.)
  • Email

Чтобы избежать ужасного дубликата кода, вы должны создать метод вокруг каждого из них в классе или классах службы. Затем вы создадите действие, инкапсулирующее все шаги, связанные с этими методами.

Не бойтесь реализовать вещи за пределами своего класса обслуживания - это не значит, что он находится за пределами вашего уровня обслуживания.

Я вижу учет интересов клиента как действие. Вы выполняете синхронные шаги для достижения желаемого действия. Так на основе методов, как создание пользователя, клиент и пр мы можем построить действия, чтобы зарегистрировать заинтересованность клиента, например, так:

<?php 

class ClientService { 

public function addAction(IAction $action) 
{ 
    return $action->process(); 
} 

public function createUser() {} // business logic for creating a user. 

public function createClient() {} // business logic for creating a client. 

public function createAssociation() {} // business logic for creating an association. 

} 

interface IAction { 

    public function process(); 

} 

class RegisterClientInterestAction implements IAction { 

    protected $client; 

    public function __construct(ClientService $client) 
    { 
    $this->client = $client; 
    } 

    public function process() 
    { 
    $this->createUser()->createClient()->createAssociation(); 
    } 

    private function createUser() {} // interact with your client service to call the method $client->createUser() 

    private function createClient() {} // interact with your client service to call the method $client->createClient() 

    private function createAssociation() {} // interact with your client service to call the method $client->createAssociation() 

} 

//USAGE 

$service = new ClientService; 
$results = $service->addAction(new RegisterClientInterestAction($service)); 

?> 

Делая это таким образом вы в состоянии использовать и т.д. методы CreateUser в новом действии, но без дублирования кода. Имея addAction в классе службы, вы все еще выполняете бизнес-логику внутри своего уровня обслуживания.


Если требуется два или большее количество услуг, я бы немного другой подход, переместив, где я бы execute действия.

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

Как это:

<?php 

class Service { 

    public function addAction(IAction $action) 
    { 
    return $action->process(); 
    } 

    // Other stuff for a base service... 

} 

class UserService extends Service { 

    public function createUser() {} // business logic for creating a user. 

} 

class ClientService extends Service { 

public function createClient() {} // business logic for creating a client. 

public function createAssociation() {} // business logic for creating an association. 

} 

interface IAction { 

    public function process(); 

} 

class RegisterClientInterestAction implements IAction { 

    protected $client; 

    protected $service; 

    public function __construct(ClientService $client, UserService $user) 
    { 
    $this->user = $user; 
    $this->client = $client; 
    } 

    public function process() 
    { 
    $this->createUser()->createClient()->createAssociation(); 
    } 

    private function createUser() {} // interact with your user service to call the method $client->createUser() 

    private function createClient() {} // interact with your client service to call the method $client->createClient() 

    private function createAssociation() {} // interact with your client service to call the method $client->createAssociation() 

} 

//USAGE 

$service = new Service; 
$results = $service->addAction(new RegisterClientInterestAction(new ClientService, new UserService)); 

?> 
+0

Приятный подход к нему. Но как бы вы приблизились к нему, если бы у меня было два разных класса обслуживания «ClientService» и «UserService», каждый из которых имел свои методы 'create'? – Jonathon

+0

Я отредактировал свой ответ, чтобы дать обзор того, как вы можете обрабатывать несколько сервисов. Это хороший подход до тех пор, пока вам не понадобится вводить много классов для обработки действия. (возможно, 5 или более?). Если дело дошло до этого, вы должны переделать иерархию. – jakehallas

+0

Спасибо за обновление. Мне очень нравится ваш подход к созданию «действия», который может принимать и использовать службы таким образом. Возможно, я мог бы написать более общие, сущностные методы в моих классах услуг, а затем реализовать действия, которые принимают и используют эти службы для выполнения необходимых функций. Я был бы склонен писать действия почти для всех, поскольку это кажется мне более естественным. Он немного похож на архитектуру стиля командной шины, к которой я привык в ранних версиях Laravel, но намного чище. – Jonathon

1

Что для вас чувствует себя лучше всего - ваше предложенное решение #2.

Что мне нравится делать, это построить два класса обслуживания и посмотреть, что такое дублирование, а затем реорганизовать/извлечь любое дублирование в другой класс. Таким образом, все классы очень проверяемы, и у вас есть наименьшая вероятность нарушения любых принципов SOLID.

+0

Спасибо за ваш ответ. Это определенно то, к чему я склонялся. Если я пойду для шаблона репозитория, то это будет долгий путь к удалению большого количества дублирования. – Jonathon

Смежные вопросы