I'm working in a PHP API trying to follow a Clean Architecture pattern in order to be able to extract modules of the app to micro-services in the future.
My question is how application services should use each other without getting coupled. Even if I'm injecting a bound abstract (interface), injected service's methods are handling entities outside the host service's domain. So in the future i would have coupled services and i will not be able to externalize them.
<?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
}
}
at someMethod am i not coupling the services? managing an entity from another service/domain?
what happens when i want to move the HostService to a micro-service?
Thanks a lot in advance for your ideas.
Even though you are using PHP the following may provide useful on a conceptual level:
Probably the simplest would be to implement some generic mediator that you depend on in your application layer (integration concern).
I have a simple open-source implementation with another option being Jimmy Bogard's mediatr.
In my implementation a particular participant would depend on the relevant service(s) and act on the relevant message being passed. Any given participant can respond to various messages. In your case there may be two participants with each relying on the relevant service.
This mechanism also works well when trying to cut down on the number of dependencies injected into, or used by, a class.
In the past I have also used an application
Task(not to be confused with .NetTask) that represents a use-case and then it would accept bothOrderServiceas well asPaymentService(using your example here) and interact with the two in the relevant way. The code dependency of one on the other could be extracted into meaningful methods.The mediator is a more implicit implementation of this concept whereas some use-case specific class would be more explicit.