шаблон проектирования для переключения поставщиков электронной почты в код

Нам нужно отправлять электронные письма в нашем приложении php (кто этого не делает). Первоначально, когда наше приложение находилось в зачаточном состоянии, мы использовали просто linux sendmail. Немного продвигаясь вперед, мы перешли на наш собственный SMTP-сервер. Это означает изменение кода в каждом файле, который имеет функцию, связанную с электронной почтой. Через год мы перешли на AWS и снова должны были изменить код, чтобы начать использовать службу электронной почты AWS. Теперь мы перешли в облако Google и снова изменили код электронной почты, чтобы использовать сторонний провайдер.

Конфигурация электронной почты распространяется по всем местам, а изменение одного провайдера означает, что необходимо обновить сотни файлов, и если вы пропустите один, клиенты могут не получить электронную почту для одной части, но могут получать электронные письма для других.

Я только что сделал шаг назад и понял, что мы меняем код, который не имеет смысла менять только потому, что наш поставщик электронной почты изменен.

Но для жизни я не могу понять, что будет выходом из этого беспорядка.

Все, что мне нужно сделать, это удалить электронную почту из моего кода и реорганизовать ее таким образом, чтобы мой код приложения мог функционировать независимо от поставщика почтовых услуг.

Это мое занятие

class EmailGateway()
{
  private $emailer;
  public function __construct($someEmailProvider)
  {
     $this->emailer = $someEmailProvider;
  }

  public function send($from, $to, $subject, $bodyText, $bodyHtml="")
  {

    this->emailer->send($from, $to, $subject, $bodyText, $bodyHtml="");
  }
}

И тогда в моем коде все, что мне нужно, это называть его как

# Gmail?
$mailGateway = new EmailGateway(new GmailEmailer("username", "password"));

# local?
$mailGateway = new EmailGateway(new SendMailer());

# SMTP?
$mailGateway = new EmailGateway(new MyMailServerMailer("192.168.0.3", "no_user", "secret_password"));

В моем коде приложения я даже могу сделать еще один шаг и вызвать фабрику, чтобы получить текущий поставщик почтовых сообщений по умолчанию

class EmailService()
{
   public static function currentProvider(): EmailProviderInterface
   {

      return new MyMailServerMailer("192.168.0.3", "no_user", "secret_password");

   }
}

Это сделает мой выше код вызывающего абонента еще проще одного лайнера

# SMTP?/GMAIL?/Local?/Whatever
$mailGateway = new EmailGateway(EmailService::currentProvider());

Поэтому всякий раз, когда мне нужно изменить поставщика, все, что я сделаю, это изменить кишки currentProvider (), и мне хорошо идти.

Правильно ли я делаю это? Является ли это правильной стратегией? Должен ли я даже заботиться о том, какой шаблон он до тех пор, пока он может решить мою проблему?

Есть ли лучший способ вытащить себя из этого все возрастающего беспорядка?

Всего 1 ответ


Да, в основном вы делаете правильно - учитывая вашу цель легко изменять код, когда вам нужно изменить поставщика почты.

Однако вы можете сделать ваш дизайн проще и лучше.

Посмотрите на класс EmailGateway : он не делает ничего существенного. Он имеет тот же интерфейс с EmailProvider и просто делегирует задачу send EmailProvider .

Таким образом, вы можете просто иметь интерфейс с именем EmailProvider - я использую Java, но его легко перевести на PHP:

interface EmailProvider {
    void send(String from, String to, String subject, String bodyText, String bodyHtml);
}

И затем несколько реализаций:

class GoogleEmailProvider implements EmailProvider {
    public GoogleEmailProvider (String username, String password) {
        ...
    }
    public void send(String from, String to, String subject, String bodyText, String bodyHtml) {
        ...
    }
}
// and so on ...

В CompositionRoot вашего приложения (например, main метод) вы просто создаете один экземпляр EmailProvider который вам нужен:

EmailProvider emailProvider = new GoogleEmailProvider("username", "password");

Затем вы можете передать этот экземпляр в любое место, где нужно отправлять электронные письма:

Foo foo = new Foo(emailProvider);

Этот дизайн предлагает некоторые преимущества. Во-первых, легче тестировать классы, такие как Foo . Вы всегда можете написать MockEmailProvider и передать его Foo . Во-вторых, пользователям таких классов, как Foo должно быть легко известно, что Foo может отправлять электронные письма, просто просматривая свою подпись. Отправка электронной почты, выполнение IO / Database / Network ... являются важными вещами и всегда должны быть хорошо осведомлены.

Надеюсь это поможет.


Есть идеи?

10000