design pattern for switching email providers in the code
We need to send emails in our php app (who doesn't). Initially when our app was in infancy, we used simply linux sendmail.
A bit moving forward we switched to our own SMTP server. That means code change in every file that has email related function.
A year later we shifted to AWS and had to change the code again to start using AWS email service.
Now we moved to Google cloud and again email code is changed to use some third party provider.
Configuration of email, is spread all over the places and changing one provider means hundreds of files need to be updated and if you miss one, clients may not be able to receive email for one part but can get emails for other.
I just took a step back and realized that we are changing code that makes no sense to change only because our email provider is changed.
But for the life of me I am unable to figure out what would be a way out of this mess.
All I need to do is to remove emailing from my code and refactor it in such a way that my app code can function independent of the mail service provider.
Her is my take on this
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="");
}
}
And then in my code all I need is to call it like
# 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"));
In my app code, I can even go one step further and calla a factory to get the current default email provider
class EmailService()
{
public static function currentProvider(): EmailProviderInterface
{
return new MyMailServerMailer("192.168.0.3", "no_user", "secret_password");
}
}
That will make my above caller code even simpler one liner
# SMTP?/GMAIL?/Local?/Whatever
$mailGateway = new EmailGateway(EmailService::currentProvider());
So whenever I need to change the provider, all I will do is change the guts of currentProvider() and I am good to go.
Am I doing it the right way?
Is it a proper strategy pattern at all?
Should I even care about what pattern it is as long as it can solve my problem?
Is there a better way to pull myself out of this ever increasing mess?
php design-patterns refactoring strategy-pattern
add a comment |
We need to send emails in our php app (who doesn't). Initially when our app was in infancy, we used simply linux sendmail.
A bit moving forward we switched to our own SMTP server. That means code change in every file that has email related function.
A year later we shifted to AWS and had to change the code again to start using AWS email service.
Now we moved to Google cloud and again email code is changed to use some third party provider.
Configuration of email, is spread all over the places and changing one provider means hundreds of files need to be updated and if you miss one, clients may not be able to receive email for one part but can get emails for other.
I just took a step back and realized that we are changing code that makes no sense to change only because our email provider is changed.
But for the life of me I am unable to figure out what would be a way out of this mess.
All I need to do is to remove emailing from my code and refactor it in such a way that my app code can function independent of the mail service provider.
Her is my take on this
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="");
}
}
And then in my code all I need is to call it like
# 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"));
In my app code, I can even go one step further and calla a factory to get the current default email provider
class EmailService()
{
public static function currentProvider(): EmailProviderInterface
{
return new MyMailServerMailer("192.168.0.3", "no_user", "secret_password");
}
}
That will make my above caller code even simpler one liner
# SMTP?/GMAIL?/Local?/Whatever
$mailGateway = new EmailGateway(EmailService::currentProvider());
So whenever I need to change the provider, all I will do is change the guts of currentProvider() and I am good to go.
Am I doing it the right way?
Is it a proper strategy pattern at all?
Should I even care about what pattern it is as long as it can solve my problem?
Is there a better way to pull myself out of this ever increasing mess?
php design-patterns refactoring strategy-pattern
add a comment |
We need to send emails in our php app (who doesn't). Initially when our app was in infancy, we used simply linux sendmail.
A bit moving forward we switched to our own SMTP server. That means code change in every file that has email related function.
A year later we shifted to AWS and had to change the code again to start using AWS email service.
Now we moved to Google cloud and again email code is changed to use some third party provider.
Configuration of email, is spread all over the places and changing one provider means hundreds of files need to be updated and if you miss one, clients may not be able to receive email for one part but can get emails for other.
I just took a step back and realized that we are changing code that makes no sense to change only because our email provider is changed.
But for the life of me I am unable to figure out what would be a way out of this mess.
All I need to do is to remove emailing from my code and refactor it in such a way that my app code can function independent of the mail service provider.
Her is my take on this
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="");
}
}
And then in my code all I need is to call it like
# 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"));
In my app code, I can even go one step further and calla a factory to get the current default email provider
class EmailService()
{
public static function currentProvider(): EmailProviderInterface
{
return new MyMailServerMailer("192.168.0.3", "no_user", "secret_password");
}
}
That will make my above caller code even simpler one liner
# SMTP?/GMAIL?/Local?/Whatever
$mailGateway = new EmailGateway(EmailService::currentProvider());
So whenever I need to change the provider, all I will do is change the guts of currentProvider() and I am good to go.
Am I doing it the right way?
Is it a proper strategy pattern at all?
Should I even care about what pattern it is as long as it can solve my problem?
Is there a better way to pull myself out of this ever increasing mess?
php design-patterns refactoring strategy-pattern
We need to send emails in our php app (who doesn't). Initially when our app was in infancy, we used simply linux sendmail.
A bit moving forward we switched to our own SMTP server. That means code change in every file that has email related function.
A year later we shifted to AWS and had to change the code again to start using AWS email service.
Now we moved to Google cloud and again email code is changed to use some third party provider.
Configuration of email, is spread all over the places and changing one provider means hundreds of files need to be updated and if you miss one, clients may not be able to receive email for one part but can get emails for other.
I just took a step back and realized that we are changing code that makes no sense to change only because our email provider is changed.
But for the life of me I am unable to figure out what would be a way out of this mess.
All I need to do is to remove emailing from my code and refactor it in such a way that my app code can function independent of the mail service provider.
Her is my take on this
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="");
}
}
And then in my code all I need is to call it like
# 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"));
In my app code, I can even go one step further and calla a factory to get the current default email provider
class EmailService()
{
public static function currentProvider(): EmailProviderInterface
{
return new MyMailServerMailer("192.168.0.3", "no_user", "secret_password");
}
}
That will make my above caller code even simpler one liner
# SMTP?/GMAIL?/Local?/Whatever
$mailGateway = new EmailGateway(EmailService::currentProvider());
So whenever I need to change the provider, all I will do is change the guts of currentProvider() and I am good to go.
Am I doing it the right way?
Is it a proper strategy pattern at all?
Should I even care about what pattern it is as long as it can solve my problem?
Is there a better way to pull myself out of this ever increasing mess?
php design-patterns refactoring strategy-pattern
php design-patterns refactoring strategy-pattern
edited Nov 20 '18 at 2:34
Waku-2
asked Nov 20 '18 at 2:12
Waku-2Waku-2
321213
321213
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
Yes, basically you are doing right -- given your objective of easily modifying code when you need to change the mail provider.
However, you can make your design simpler and better.
Look into the EmailGateway
class: it does not do any significant thing. It has the same interface with the EmailProvider
and merely delegates the send
task to the EmailProvider
.
So you could just have an interface named EmailProvider
-- I'm using Java but it should be easy to translate to PHP:
interface EmailProvider {
void send(String from, String to, String subject, String bodyText, String bodyHtml);
}
And then several implementations:
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 ...
At the CompositionRoot of your application (like the main
method), you simple create one instance of the EmailProvider
that you need:
EmailProvider emailProvider = new GoogleEmailProvider("username", "password");
Then you can pass that instance to anywhere that needs to send emails:
Foo foo = new Foo(emailProvider);
This design offers some benefits. First, it is easier to unit test the classes like Foo
. You can always write a MockEmailProvider
and pass it to Foo
. Second, users of classes like Foo
should be easily aware that Foo
can send emails just by looking on its signature. Sending email, doing IO/Database/Network ... are significant things and should always be well-aware.
Hope this helps.
Thanks for a very detailed answer. Just needed the confirmation that I am on a right path. Removing Gateway did help to further simplify the design. thanks
– Waku-2
Nov 20 '18 at 11:21
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53385248%2fdesign-pattern-for-switching-email-providers-in-the-code%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
Yes, basically you are doing right -- given your objective of easily modifying code when you need to change the mail provider.
However, you can make your design simpler and better.
Look into the EmailGateway
class: it does not do any significant thing. It has the same interface with the EmailProvider
and merely delegates the send
task to the EmailProvider
.
So you could just have an interface named EmailProvider
-- I'm using Java but it should be easy to translate to PHP:
interface EmailProvider {
void send(String from, String to, String subject, String bodyText, String bodyHtml);
}
And then several implementations:
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 ...
At the CompositionRoot of your application (like the main
method), you simple create one instance of the EmailProvider
that you need:
EmailProvider emailProvider = new GoogleEmailProvider("username", "password");
Then you can pass that instance to anywhere that needs to send emails:
Foo foo = new Foo(emailProvider);
This design offers some benefits. First, it is easier to unit test the classes like Foo
. You can always write a MockEmailProvider
and pass it to Foo
. Second, users of classes like Foo
should be easily aware that Foo
can send emails just by looking on its signature. Sending email, doing IO/Database/Network ... are significant things and should always be well-aware.
Hope this helps.
Thanks for a very detailed answer. Just needed the confirmation that I am on a right path. Removing Gateway did help to further simplify the design. thanks
– Waku-2
Nov 20 '18 at 11:21
add a comment |
Yes, basically you are doing right -- given your objective of easily modifying code when you need to change the mail provider.
However, you can make your design simpler and better.
Look into the EmailGateway
class: it does not do any significant thing. It has the same interface with the EmailProvider
and merely delegates the send
task to the EmailProvider
.
So you could just have an interface named EmailProvider
-- I'm using Java but it should be easy to translate to PHP:
interface EmailProvider {
void send(String from, String to, String subject, String bodyText, String bodyHtml);
}
And then several implementations:
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 ...
At the CompositionRoot of your application (like the main
method), you simple create one instance of the EmailProvider
that you need:
EmailProvider emailProvider = new GoogleEmailProvider("username", "password");
Then you can pass that instance to anywhere that needs to send emails:
Foo foo = new Foo(emailProvider);
This design offers some benefits. First, it is easier to unit test the classes like Foo
. You can always write a MockEmailProvider
and pass it to Foo
. Second, users of classes like Foo
should be easily aware that Foo
can send emails just by looking on its signature. Sending email, doing IO/Database/Network ... are significant things and should always be well-aware.
Hope this helps.
Thanks for a very detailed answer. Just needed the confirmation that I am on a right path. Removing Gateway did help to further simplify the design. thanks
– Waku-2
Nov 20 '18 at 11:21
add a comment |
Yes, basically you are doing right -- given your objective of easily modifying code when you need to change the mail provider.
However, you can make your design simpler and better.
Look into the EmailGateway
class: it does not do any significant thing. It has the same interface with the EmailProvider
and merely delegates the send
task to the EmailProvider
.
So you could just have an interface named EmailProvider
-- I'm using Java but it should be easy to translate to PHP:
interface EmailProvider {
void send(String from, String to, String subject, String bodyText, String bodyHtml);
}
And then several implementations:
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 ...
At the CompositionRoot of your application (like the main
method), you simple create one instance of the EmailProvider
that you need:
EmailProvider emailProvider = new GoogleEmailProvider("username", "password");
Then you can pass that instance to anywhere that needs to send emails:
Foo foo = new Foo(emailProvider);
This design offers some benefits. First, it is easier to unit test the classes like Foo
. You can always write a MockEmailProvider
and pass it to Foo
. Second, users of classes like Foo
should be easily aware that Foo
can send emails just by looking on its signature. Sending email, doing IO/Database/Network ... are significant things and should always be well-aware.
Hope this helps.
Yes, basically you are doing right -- given your objective of easily modifying code when you need to change the mail provider.
However, you can make your design simpler and better.
Look into the EmailGateway
class: it does not do any significant thing. It has the same interface with the EmailProvider
and merely delegates the send
task to the EmailProvider
.
So you could just have an interface named EmailProvider
-- I'm using Java but it should be easy to translate to PHP:
interface EmailProvider {
void send(String from, String to, String subject, String bodyText, String bodyHtml);
}
And then several implementations:
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 ...
At the CompositionRoot of your application (like the main
method), you simple create one instance of the EmailProvider
that you need:
EmailProvider emailProvider = new GoogleEmailProvider("username", "password");
Then you can pass that instance to anywhere that needs to send emails:
Foo foo = new Foo(emailProvider);
This design offers some benefits. First, it is easier to unit test the classes like Foo
. You can always write a MockEmailProvider
and pass it to Foo
. Second, users of classes like Foo
should be easily aware that Foo
can send emails just by looking on its signature. Sending email, doing IO/Database/Network ... are significant things and should always be well-aware.
Hope this helps.
edited Nov 20 '18 at 5:03
answered Nov 20 '18 at 4:05
Nghia BuiNghia Bui
1,463813
1,463813
Thanks for a very detailed answer. Just needed the confirmation that I am on a right path. Removing Gateway did help to further simplify the design. thanks
– Waku-2
Nov 20 '18 at 11:21
add a comment |
Thanks for a very detailed answer. Just needed the confirmation that I am on a right path. Removing Gateway did help to further simplify the design. thanks
– Waku-2
Nov 20 '18 at 11:21
Thanks for a very detailed answer. Just needed the confirmation that I am on a right path. Removing Gateway did help to further simplify the design. thanks
– Waku-2
Nov 20 '18 at 11:21
Thanks for a very detailed answer. Just needed the confirmation that I am on a right path. Removing Gateway did help to further simplify the design. thanks
– Waku-2
Nov 20 '18 at 11:21
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53385248%2fdesign-pattern-for-switching-email-providers-in-the-code%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown