Portas como boundary claro
Envolva o domínio existente com interfaces explícitas (UseCases) e adapte frameworks externos nas bordas.
Adapter de notificação
interface Notifier {
public function send(string $recipient, string $message): void;
}
final class SESNotifier implements Notifier {
public function __construct(private SESClient $client) {}
public function send(string $recipient, string $message): void {
$this->client->sendEmail([...]);
}
}
Com isso, testar o domínio fica trivial e trocar provedores vira uma troca de adapter.