A flexible and extensible email notification system built with C# that demonstrates the Adapter and Factory design patterns. This project allows seamless integration with multiple email service providers through a unified interface.
- Features
- Project Structure
- Getting Started
- Usage
- How It Works
- Code Examples
- Design Patterns Deep Dive
- Key Classes
- Extending the System
- Benefits of This Architecture
- Learning Resources
- Contributing
- License
- Author
- Support
- SMTP - Traditional email protocol support
- SendGrid - Cloud-based email delivery service
- Gmail API - Google's email service integration
- Mailgun - Transactional email API
- Adapter Pattern - Converts incompatible email service APIs into a common interface
- Factory Pattern - Centralizes object creation for email services and notifications
- Dependency Injection - Promotes loose coupling and testability
- Welcome Email
- Password Reset Email
- Order Confirmation Email
Switch between email providers without changing your business logic - just change the factory selection!
- Email format validation
- User input validation
- Connection testing before sending
Email_notification_system/
βββ Models/
β βββ EmailMessage.cs # Email data model
β βββ EmailResult.cs # Result object for email operations
βββ Interfaces/
β βββ IEmailService.cs # Common interface for all email services
βββ Third_Party_Services/
β βββ SmtpClient.cs # SMTP client implementation
β βββ SendGridApiClient.cs # SendGrid API client
β βββ GmailClient.cs # Gmail API client
β βββ MailgunClient.cs # Mailgun API client
βββ Adaptors/
β βββ SmtpClient_Adaptor.cs # SMTP adapter
β βββ SendGridApi_Adaptor.cs # SendGrid adapter
β βββ GmailClientAdaptor.cs # Gmail adapter
β βββ MailgunClient_Adaptor.cs # Mailgun adapter
βββ Service/
β βββ NotificationManager.cs # High-level notification service
βββ Factories/
β βββ ThirdParty_service_Factory.cs # Email service factory
β βββ NotificationManager_Factory.cs # Notification factory
βββ Program.cs # Main application entry point
- .NET 8.0 SDK or higher
- Visual Studio 2022 / VS Code / Rider
- Clone the repository
git clone https://github.com/yourusername/email-notification-system.git
cd email-notification-system- Build the project
dotnet build- Run the application
dotnet runWhen you run the application, you'll see an interactive menu:
ββββββββββββββββββββββββββββββββββββββββββ
β Email Notification System - Demo β
ββββββββββββββββββββββββββββββββββββββββββ
Select Third-Party Email Service:
1. SMTP
2. SendGrid
3. Gmail
4. Mailgun
Please select an option (1-4):
-
Select Email Service Provider
- Choose from SMTP, SendGrid, Gmail, or Mailgun
-
Enter Email Details
- Receiver Email
- Sender Email
- Subject
- Body
-
Choose Notification Type
- Welcome Email
- Password Reset Email
- Order Confirmation Email
-
Email Sent!
- Confirmation message displayed
Each email service has its own unique API. The Adapter pattern converts these different APIs into a single IEmailService interface:
public interface IEmailService
{
EmailResult SendEmail(EmailMessage message);
bool ValidateConnection();
}Example Adapter:
public class SendGridApi_Adaptor : IEmailService
{
private readonly SendGridApiClient _client;
public SendGridApi_Adaptor(SendGridApiClient client)
{
_client = client;
}
public EmailResult SendEmail(EmailMessage message)
{
// Convert EmailMessage to SendGrid format
var response = _client.Send(message.From, message.To,
message.Subject, message.Body);
// Convert SendGrid response to EmailResult
return new EmailResult
{
Success = response.StatusCode == 202,
MessageId = response.MessageId
};
}
}The Factory pattern centralizes object creation, making it easy to switch between providers:
IEmailService service = thirdPartyServiceFactory.GetThirdPartyService(userSelection, email);High-level service that uses any email provider through dependency injection:
var notificationManager = new NotificationManager(emailService);
notificationManager.SendWelcomeEmail("user@example.com", "John Doe");// Create email message
var emailMessage = new EmailMessage
{
To = "newuser@example.com",
From = "noreply@myapp.com",
Subject = "Welcome!",
Body = "Welcome to our platform!"
};
// Get SendGrid service from factory
IEmailService emailService = factory.GetThirdPartyService(2, emailMessage);
// Send using Notification Manager
var notificationManager = new NotificationManager(emailService);
notificationManager.SendWelcomeEmail("newuser@example.com", "John");// Use SMTP
IEmailService smtp = factory.GetThirdPartyService(1, emailMessage);
// Switch to Gmail - same interface!
IEmailService gmail = factory.GetThirdPartyService(3, emailMessage);
// Both work with NotificationManager
var manager1 = new NotificationManager(smtp);
var manager2 = new NotificationManager(gmail);Problem: Each email service has different APIs:
- SendGrid:
Send(from, to, subject, htmlContent) - Mailgun:
SendMessage(MailgunMessage) - SMTP:
Send(from, to, subject, body)
Solution: Create adapters that convert all APIs to a common interface IEmailService
Benefits:
- β Unified interface for all providers
- β Easy to add new providers
- β Business logic doesn't depend on specific APIs
Problem: Creating email service objects requires knowing:
- Which concrete class to instantiate
- What parameters each service needs
- How to configure each service
Solution: Factory handles all object creation logic
Benefits:
- β Centralized object creation
- β Hides complexity from client code
- β Easy to extend with new services
Data model representing an email:
public class EmailMessage
{
public string To { get; set; }
public string From { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
}Result of an email operation:
public class EmailResult
{
public bool Success { get; set; }
public string MessageId { get; set; }
public string ErrorMessage { get; set; }
}Common interface for all email services:
public interface IEmailService
{
EmailResult SendEmail(EmailMessage message);
bool ValidateConnection();
}- Create the third-party client
public class NewEmailClient
{
public ResponseType Send(parameters) { }
}- Create an adapter
public class NewEmailAdapter : IEmailService
{
private readonly NewEmailClient _client;
public EmailResult SendEmail(EmailMessage message)
{
// Adapt the interface
}
}- Add to factory
case 5: return CreateNewEmailAdapter(mail);That's it! No changes needed in business logic.
| Benefit | Description |
|---|---|
| Flexibility | Switch providers without code changes |
| Maintainability | Each provider isolated in its own adapter |
| Testability | Easy to mock IEmailService interface |
| Scalability | Add new providers without breaking existing code |
| Clean Code | Clear separation of concerns |
This project demonstrates:
-
SOLID Principles
- Single Responsibility Principle
- Open/Closed Principle
- Liskov Substitution Principle
- Interface Segregation Principle
- Dependency Inversion Principle
-
Design Patterns
- Adapter Pattern
- Factory Pattern
- Dependency Injection
Contributions are welcome! Here are some ideas:
- Add more email providers (AWS SES, Postmark, etc.)
- Implement unit tests with xUnit
- Add email templates system
- Implement retry logic for failed sends
- Add logging functionality
- Create a fallback mechanism
This project is licensed under the MIT License - see the LICENSE file for details.
MarwanFarouq
- GitHub: @MarwanFarouq
- LinkedIn: @MarwanFarouq
- Inspired by real-world email service integration challenges
- Built to demonstrate design patterns in practice
- Created as a learning project for software architecture
If you have any questions or run into issues, please open an issue on GitHub.
β If you found this project helpful, please give it a star! β