问题描述
我正在使用PHP API尝试遵循Clean Architecture模式,以便将来能够将应用程序的模块提取到微服务中。
我的问题是应用程序服务应如何彼此使用而又不相互耦合。 即使我注入绑定的抽象(接口),注入的服务的方法也正在处理宿主服务域之外的实体。因此,将来我将拥有耦合的服务,而我将无法将其外部化。
<?PHP
/* Domain: INJECTED */
class InjectedService implements InjectedServiceInterface
{
public function get(int $id): InjectedServiceDomainEntity
{
return $this->repo->findById($id);
}
}
/* Domain: HOST */
class HostService implements HostServiceInterface
{
/** @var InjectedServiceInterface $injectedService */
private $injectedService;
public function __construct(InjectedServiceInterface $injectedService)
{
$this->injectedService = $injectedService;
}
public function someMethod($someId)
{
/** @var InjectedServiceDomainEntity $injectedServiceEntity */
$injectedServiceEntity = $this->injectedService->get($someId);
// here I'm managing an outsider entity
}
}
在someMethod
,我是否没有耦合服务?从另一个服务/域管理实体?
我想将HostService
移至微服务时会发生什么?
非常感谢您的想法。
解决方法
即使您使用的是PHP,以下内容在概念上也可能会有用:
可能最简单的方法是在应用程序层中实现您依赖的某些通用介体(集成方面)。
我有一个简单的开源implementation,另外一个选择是Jimmy Bogard的mediatr。
在我的实现中,特定参与者将取决于相关服务,并根据所传递的相关消息采取行动。任何给定的参与者都可以响应各种消息。在您的情况下,可能有两个参与者,每个参与者都依赖相关的服务。
当尝试减少注入到类中或由类使用的依赖项的数量时,此机制也很好用。
过去,我还使用了一个代表用例的应用程序Task
(不要与.Net Task
混淆),然后接受 both OrderService
和PaymentService
(在此处使用您的示例),并以相关方式与二者进行交互。一个人与另一个人的代码依赖关系可以提取为有意义的方法。
中介程序是此概念的更隐式实现,而某些用例特定的类将更明确。
,如果我能正确理解您的问题,则有两种服务,例如OrderService
和PaymentService
。 OrderService
需要PaymentService
。
有两种情况
第一种情况:
PaymentService
是OrderService
的第三方,可以独立开发。如果确实如此,最好将PaymentService
放在基础结构层中,并将其注入到应用层服务中,例如OrderService
。
第二种情况:
PaymentService
不是第三方。
就干净代码而言,这种情况下存在问题。违反单一责任原则。更改someMethod
之类的方法有多个原因。从另一个服务中的另一个服务调用方法使更改的原因不止一个。最好在表示层中调用PaymentService
并获取OrderService
所需的数据,然后someMethod
中的OrderService
可以通过传递数据来调用而无需注入PaymentService
。
讨论此类问题有点困难。希望正确地说出我的意思。 ;)
,如果服务X使用服务Y,但您将始终处于耦合状态:
Clean Architecture及其主要的依赖关系反转原理为您提供了使耦合或依赖关系成为单向的必要工具。
在您的情况下,DI和接口允许您使Host
模块依赖于Injected
模块,但绝不能相反。如果将InjectedServiceInterface
和InjectedServiceDomainEntity
的接口移入Host模块,甚至可以使Host模块不依赖任何内容。如果现在调用get
,则将只处理主机模块中的源代码。
使用DI时,这些接口在不同的模块中实现。借助微服务,这些接口由远程调用基础结构组件实现。
试图将耦合降低到零没有任何意义,因为那样您的模块根本就不会交互。至少在源代码中是这样的
再想一想:在像PHP这样的动态类型语言中,如果您愿意,可以使用函数类型而不是使用声明的接口作为参数,并使用实体/对象的鸭子类型来进行更多的去耦。