Laravel: Service/Repository 模式和复制代码
Laravel: Service/Repository Pattern and duplicating code
在我的项目中,我决定使用服务模式(可能与存储库模式一起使用)来处理我的应用程序中的业务逻辑。例如,我有一个代表客户的 Client
模型和一个负责特定于客户的业务逻辑的相应 ClientService
。
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
,将两者关联起来,最后发送一封电子邮件到具有详细信息的新用户。
在使用服务模式时,最好把这个逻辑放在哪里?
我考虑过:
创建服务和方法 ClientInterestService::createClientAndUser(...)
,它将使用 ClientService
和 UserService
classes 创建 Client
和User
个实例,然后在触发发送电子邮件的事件之前执行关联。这种方法意味着我没有复制代码,但是我将 classes 耦合在一起并且我打破了一些 SOLID 原则。我不确定,但我觉得这也不适合测试。
如上所述,创建服务 class 和方法来执行逻辑,但我不会使用其他两个服务,而是编写逻辑来创建 Client
和User
个实例,进行关联并触发发送邮件的事件。这种方法感觉更好,我的代码耦合更松散,并且我没有违反任何 SOLID 原则,但是,我可能会重复代码。
简单地将我在 ClientInterestService::createClientAndUser(...)
中的逻辑放入我的控制器中。这样做意味着我的控制器中有业务逻辑,这在某种程度上破坏了提供服务的意义。
最适合我的是您提出的 #2
解决方案。
我喜欢做的是构建两个服务 classes 并查看有什么重复,然后 refactor/extract 与另一个 class 的任何重复。这样,所有 classes 都非常可测试,并且您破坏任何 SOLID 原则的可能性最小。
我认为如果将其分解成更小的步骤,您就可以实现 DRY 架构。我看到的步骤是:
- 创建客户
- 创建用户
- 关联(通过枢轴 table、连接点 table 等)
- 电子邮件
为避免可怕的重复代码,您将在服务 class 或 classes 中围绕每个代码创建一个方法。然后,您将创建一个操作,封装所有基于这些方法的相关步骤。
不要害怕在您的服务之外实现东西 class - 这并不意味着它在您的服务层之外。
我将注册客户兴趣视为一项操作。您遵循同步步骤来实现您想要的操作。因此,基于创建用户、客户端等方法,我们可以构建一个动作来注册客户端兴趣,如下所示:
<?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 class,您仍然在服务层内部执行业务逻辑。
如果需要两项或更多服务,我会采取稍微不同的方法,将 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));
?>
在我的项目中,我决定使用服务模式(可能与存储库模式一起使用)来处理我的应用程序中的业务逻辑。例如,我有一个代表客户的 Client
模型和一个负责特定于客户的业务逻辑的相应 ClientService
。
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
,将两者关联起来,最后发送一封电子邮件到具有详细信息的新用户。
在使用服务模式时,最好把这个逻辑放在哪里?
我考虑过:
创建服务和方法
ClientInterestService::createClientAndUser(...)
,它将使用ClientService
和UserService
classes 创建Client
和User
个实例,然后在触发发送电子邮件的事件之前执行关联。这种方法意味着我没有复制代码,但是我将 classes 耦合在一起并且我打破了一些 SOLID 原则。我不确定,但我觉得这也不适合测试。如上所述,创建服务 class 和方法来执行逻辑,但我不会使用其他两个服务,而是编写逻辑来创建
Client
和User
个实例,进行关联并触发发送邮件的事件。这种方法感觉更好,我的代码耦合更松散,并且我没有违反任何 SOLID 原则,但是,我可能会重复代码。简单地将我在
ClientInterestService::createClientAndUser(...)
中的逻辑放入我的控制器中。这样做意味着我的控制器中有业务逻辑,这在某种程度上破坏了提供服务的意义。
最适合我的是您提出的 #2
解决方案。
我喜欢做的是构建两个服务 classes 并查看有什么重复,然后 refactor/extract 与另一个 class 的任何重复。这样,所有 classes 都非常可测试,并且您破坏任何 SOLID 原则的可能性最小。
我认为如果将其分解成更小的步骤,您就可以实现 DRY 架构。我看到的步骤是:
- 创建客户
- 创建用户
- 关联(通过枢轴 table、连接点 table 等)
- 电子邮件
为避免可怕的重复代码,您将在服务 class 或 classes 中围绕每个代码创建一个方法。然后,您将创建一个操作,封装所有基于这些方法的相关步骤。
不要害怕在您的服务之外实现东西 class - 这并不意味着它在您的服务层之外。
我将注册客户兴趣视为一项操作。您遵循同步步骤来实现您想要的操作。因此,基于创建用户、客户端等方法,我们可以构建一个动作来注册客户端兴趣,如下所示:
<?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 class,您仍然在服务层内部执行业务逻辑。
如果需要两项或更多服务,我会采取稍微不同的方法,将 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));
?>