Easy Way To Switch Email Driver in Go
In this article, we will explore a Go implementation for sending emails using different drivers (AWS SES and SMTP). We will discuss the patterns used, how to use the implementation.
Patterns Used
1. Builder Pattern
The builder pattern is used to create different configurations for sending emails. Depending on the environment configuration, the appropriate email driver (AWS SES or SMTP) is instantiated.
2. Interface Segregation
Interfaces are used to define contracts for sending emails. This allows for flexibility and easy switching between different email drivers.
3. Dependency Injection
Configuration values are injected into the structs, making the code more modular and testable.
Code Explanation
Configuration Structs
We have two main configuration structs: AwsConfig
and SmtpConfig
.
type AwsConfig struct {
AccessKeyID string
SecretAccessKey string
Region string
Session *session.Session
}
type SmtpConfig struct {
Host string
Port int
Username string
Password string
}
Creating Configurations
The NewAwsConfig
and NewSmtpConfig
functions create instances of these configurations using values from the environment.
func NewAwsConfig(configuration ViperConfig) *AwsConfig {
return &AwsConfig{
AccessKeyID: configuration.GetEnv("AWS_ACCESS_KEY"),
SecretAccessKey: configuration.GetEnv("AWS_SECRET_KEY"),
Region: configuration.GetEnv("AWS_REGION"),
}
}
func NewSmtpConfig(configuration ViperConfig) *SmtpConfig {
portInt, _ := strconv.ParseInt(configuration.GetEnv("SMTP_PORT"), 10, 32)
return &SmtpConfig{
Host: configuration.GetEnv("SMTP_HOST"),
Port: int(portInt),
Username: configuration.GetEnv("SMTP_USERNAME"),
Password: configuration.GetEnv("SMTP_PASSWORD"),
}
}
Sending Emails
Both AwsConfig
and SmtpConfig
implement the SendEmail
method to send emails using their respective drivers.
AWS SES
func (c *AwsConfig) SendEmail(to string, subject string, body string) error {
if err := c.validateCredentials(); err != nil {
return err
}
if c.Session == nil {
return errors.New("AWS session not set")
}
svc := ses.New(c.Session)
input := c.createSendEmailInput(to, subject, body)
response, err := svc.SendEmail(input)
if err != nil {
c.logSendEmailError(to, subject, err)
return err
}
log.Printf("Email: %s - Subject: %s - MessageID: %s", to, subject, response.String())
return nil
}
SMTP
func (c *SmtpConfig) SendEmail(to string, subject string, body string) error {
gm := gomail.NewMessage()
gm.SetAddressHeader("From", "arief@test.com", "Arief")
gm.SetHeader("To", to)
gm.SetHeader("Subject", subject)
gm.SetBody("text/html", body)
dialer := gomail.NewDialer(c.Host, c.Port, c.Username, c.Password)
if err := dialer.DialAndSend(gm); err != nil {
return err
}
return nil
}
Builder Pattern for Email Configuration
The EmailConfig
struct uses the builder pattern to create the appropriate email configuration based on the environment.
type EmailConfig struct {
AwsConfig *AwsConfig
SmtpConfig *SmtpConfig
configuration ViperConfig
}
func NewEmailConfig() *EmailConfig {
configuration := NewViperConfig()
driver := configuration.GetEnv("EMAIL_DRIVER")
switch driver {
case "aws":
return &EmailConfig{
AwsConfig: NewAwsConfig(configuration),
configuration: configuration,
}
case "smtp":
return &EmailConfig{
SmtpConfig: NewSmtpConfig(configuration),
configuration: configuration,
}
default:
panic("Email driver not set")
}
}
Sending Emails Using the Configured Driver
The SendEmail
method in EmailConfig
sends an email using the configured driver.
func (c *EmailConfig) SendEmail(to string, subject string, body string) error {
if c.AwsConfig != nil {
if err := c.AwsConfig.SetNewSession(c.configuration); err != nil {
log.Println("Error setting new session", err)
return err
}
return c.AwsConfig.SendEmail(to, subject, body)
}
if c.SmtpConfig != nil {
return c.SmtpConfig.SendEmail(to, subject, body)
}
return errors.New("Email driver not set")
}
How to Use
Set Environment Variables: Ensure that the necessary environment variables are set for your chosen email driver (AWS SES or SMTP).
Instantiate EmailConfig: Create an instance of
EmailConfig
using theNewEmailConfig
function.Send Email: Use the
SendEmail
method to send an email.
func main() {
emailConfig := NewEmailConfig()
err := emailConfig.SendEmail("recipient@example.com", "Test Subject", "Test Body")
if err != nil {
log.Fatalf("Failed to send email: %v", err)
}
}
Conclusion
In this article, we explored a Go implementation for sending emails using different drivers (AWS SES and SMTP). We discussed the patterns used, how to use the implementation. This modular and flexible approach allows for easy switching between different email drivers and makes the code more maintainable.
Subscribe to my newsletter
Read articles from Arief directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Arief
Arief
I am love to sharing everything i know about Web Developer especially for Backend Web Developer.