Je travaille dans une API PHP en essayant de suivre un modèle d'architecture propre afin de pouvoir extraire des modules de l'application vers des micro-services à l'avenir.
Ma question est de savoir comment les services d'application devraient s'utiliser les uns les autres sans être couplés. Même si j'injecte un résumé lié (interface), les méthodes du service injecté gèrent des entités en dehors du domaine du service hôte. Donc à l'avenir j'aurais des services couplés et je ne pourrai pas les externaliser.
<?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 } }
chez someMethod
suis-je pas coupler les services? gérer une entité à partir d'un autre service / domaine? que se passe-t-il lorsque je souhaite déplacer HostService
vers un micro-service?
Merci d'avance pour vos idées.
3 Réponses :
Même si vous utilisez PHP, les éléments suivants peuvent s'avérer utiles au niveau conceptuel :
Le plus simple serait probablement d'implémenter un médiateur générique dont vous dépendez dans votre couche application (problème d'intégration).
J'ai une implémentation open-source simple avec une autre option étant le mediatr de Jimmy Bogard.
Dans ma mise en œuvre, un participant particulier dépendrait du (des) service (s) concerné (s) et agirait en fonction du message pertinent transmis. Tout participant donné peut répondre à divers messages. Dans votre cas, il peut y avoir deux participants, chacun s'appuyant sur le service concerné.
Ce mécanisme fonctionne également bien lorsque vous essayez de réduire le nombre de dépendances injectées ou utilisées par une classe.
Dans le passé, j'ai également utilisé une Task
application (à ne pas confondre avec .Net Task
) qui représente un cas d'utilisation, puis elle accepterait à la fois OrderService
et PaymentService
(en utilisant votre exemple ici) et interagirait avec les deux dans le manière pertinente. La dépendance de code de l'un sur l'autre pourrait être extraite dans des méthodes significatives.
Le médiateur est une implémentation plus implicite de ce concept alors qu'une classe spécifique à un cas d'utilisation serait plus explicite.
Si je pouvais bien comprendre votre question, il existe deux services, par exemple OrderService
et PaymentService
. OrderService
est nécessaire au PaymentService
.
Il existe deux scénarios
Premier scénario: PaymentService
est un tiers pour OrderService
, qui pourrait être développé indépendamment. Si c'est vrai, il est préférable de placer PaymentService
dans la couche Infrastructure et de l'injecter dans les services de la couche application comme OrderService
.
Deuxième scénario: PaymentService
n'est pas un tiers. En termes de code propre, il y a un problème dans ce cas. Le principe de responsabilité unique serait violé. Il y a plus d'une raison pour changer des méthodes comme someMethod
. L'appel d'une méthode à partir d'un autre service à l'intérieur d'un autre fait plusieurs raisons de changer. Il serait préférable d'appeler PaymentService
dans la couche de présentation et de récupérer les données nécessaires pour OrderService
et après cela, une someMethod
dans OrderService
pourrait être invoquée en passant les données sans qu'il soit nécessaire d'injecter PaymentService
.
Discuter de ce genre de questions est un peu difficile. J'espère bien dire ma signification. ;)
Vous aurez toujours un couplage si le service X utilise le service Y, MAIS:
L'architecture propre et surtout son principe d'inversion de dépendance vous donne les outils nécessaires pour rendre le couplage ou la dépendance unidirectionnel.
Dans votre cas, la DI et les interfaces vous permettent de faire dépendre le module Host
module Injected
mais jamais l'inverse. Vous pouvez même faire dépendre le module Host de rien si vous déplacez InjectedServiceInterface
et une interface pour InjectedServiceDomainEntity
dans le module Host. Si vous appelez get
now, vous ne traiterez que le code source du module Host.
Avec DI, ces interfaces sont implémentées dans un module différent. Avec Microservices, ces interfaces sont implémentées par un composant d'infrastructure d'appel à distance.
Il ne sert à rien d'essayer de réduire le couplage à zéro car alors vos modules n'interagissent pas du tout. Au moins cela apparaît comme ça dans le code source
Une dernière réflexion: dans un langage de types dynamiques comme PHP, vous avez probablement l'opportunité d'avoir plus de découplage si vous le souhaitez en utilisant des types de fonctions au lieu d'interfaces déclarées comme paramètres et en tapant duck pour les entités / objets.
Etes-vous sûr que les 2 services sont des services applicatifs? I le code que vous avez fourni, le service injecté peut être un service de domaine ou vous pouvez simplement injecter le dépôt si vous avez seulement besoin d'obtenir l'entité
@MohamedBouallegue oui,
OrderService
etPaymentService
respectivement. L'un a besoin de l'autre. La question est plutôt de savoir si c'est l'endroit pour les DTO ou si ceux-ci doivent uniquement passer des données entre les couches