10 KiB
SMTP Client Refactoring Plan
Problem Statement
Following the successful SMTP server refactoring, the SMTP client implementation in classes.smtp.client.ts
has grown to be too large and complex, with over 1,421 lines of code. This monolithic structure makes it difficult to maintain, test, and extend. We need to refactor it into multiple smaller, focused files to improve maintainability and achieve consistency with the server architecture.
Refactoring Goals
- Improve code organization by splitting the SmtpClient class into multiple focused modules
- Enhance testability by making components more isolated and easier to mock
- Improve maintainability by reducing file sizes and complexity
- Achieve architectural consistency with the refactored SMTP server structure
- Preserve existing functionality and behavior while improving the architecture
Proposed File Structure
mail/
└── delivery/
├── smtpclient/
│ ├── index.ts - Main export file
│ ├── interfaces.ts - All SMTP client interfaces
│ ├── constants.ts - Constants and error codes
│ ├── smtp-client.ts - Main client class (core functionality)
│ ├── connection-manager.ts - Connection pooling and lifecycle
│ ├── command-handler.ts - SMTP command sending and parsing
│ ├── auth-handler.ts - Authentication mechanisms
│ ├── tls-handler.ts - TLS and STARTTLS client functionality
│ ├── error-handler.ts - Error classification and recovery
│ ├── create-client.ts - Factory function for client creation
│ └── utils/
│ ├── validation.ts - Input validation utilities
│ ├── logging.ts - Client-side logging utilities
│ └── helpers.ts - Protocol helper functions
├── classes.smtp.client.ts - Legacy file (will be deprecated)
└── interfaces.ts - Main delivery interfaces
Module Responsibilities
1. smtp-client.ts (150-200 lines)
- Core client initialization and lifecycle management
- Client configuration and options handling
- Connection coordination and delegation
- High-level send operations
- Delegates to other modules for specific functionality
2. connection-manager.ts (150-200 lines)
- Connection pooling and reuse
- Socket lifecycle management
- Connection timeout handling
- Connection health monitoring
- Connection error recovery
3. command-handler.ts (200-250 lines)
- SMTP command formatting and sending
- Server response parsing and validation
- Command pipeline management
- Response code interpretation
- Protocol state tracking
4. auth-handler.ts (150-200 lines)
- Authentication mechanism selection
- PLAIN, LOGIN, OAUTH2 implementation
- Credential management
- Authentication challenge handling
- Authentication error handling
5. tls-handler.ts (100-150 lines)
- TLS connection establishment
- STARTTLS client implementation
- Certificate validation
- Secure socket creation and management
- TLS error handling
6. error-handler.ts (150-200 lines)
- SMTP error code classification
- Error recovery strategies
- Retry logic implementation
- Error logging and reporting
- Connection failure handling
7. create-client.ts (50-100 lines)
- Factory function for client creation
- Dependency injection setup
- Configuration validation
- Component initialization
- Default option handling
8. interfaces.ts (100-150 lines)
- All client interface definitions
- Type aliases for client operations
- Error type definitions
- Configuration interfaces
- Result type definitions
Implementation Strategy
Phase 1: Initial Structure and Scaffolding (Days 1-2) ✅
-
✅ Create the folder structure and empty files
- ✅ Create
mail/delivery/smtpclient
directory - ✅ Create all module files with basic exports
- ✅ Set up barrel file (index.ts)
- ✅ Create
-
✅ Move interfaces to the new interfaces.ts file
- ✅ Extract all interfaces from current client implementation
- ✅ Add proper documentation for client-specific interfaces
- ✅ Add any missing interface properties
-
✅ Extract constants and enums to constants.ts
- ✅ Move all SMTP client constants and error codes
- ✅ Ensure proper typing and documentation
- ✅ Replace magic numbers with named constants
-
✅ Set up the basic structure for each module
- ✅ Define basic class skeletons for each handler
- ✅ Set up dependency injection structure
- ✅ Document interfaces for each module
Phase 2: Gradual Implementation Transfer (Days 3-7) ✅
-
✅ Start with utility modules
- ✅ Implement validation.ts with input validation functions
- ✅ Create logging.ts with client-side logging utilities
- ✅ Build helpers.ts with protocol helper functions
-
✅ Implement error-handler.ts
- ✅ Extract error classification logic
- ✅ Implement retry strategies
- ✅ Add error logging and reporting
-
✅ Implement connection-manager.ts
- ✅ Extract connection pooling code
- ✅ Implement connection lifecycle management
- ✅ Add connection health monitoring
-
✅ Implement command-handler.ts
- ✅ Extract SMTP command formatting and sending
- ✅ Split response parsing into separate methods
- ✅ Implement command pipeline management
-
✅ Implement auth-handler.ts
- ✅ Extract authentication mechanism logic
- ✅ Implement PLAIN, LOGIN, OAUTH2 handlers
- ✅ Add credential management
-
✅ Implement tls-handler.ts
- ✅ Extract TLS client connection handling
- ✅ Implement STARTTLS client functionality
- ✅ Add certificate validation
-
✅ Implement create-client.ts
- ✅ Create factory function for client creation
- ✅ Implement dependency injection setup
- ✅ Add configuration validation
Phase 3: Core Client Refactoring (Days 8-10) ✅
-
✅ Refactor the main SmtpClient class
- ✅ Update constructor to create and initialize components
- ✅ Implement dependency injection for all modules
- ✅ Delegate functionality to appropriate modules
- ✅ Reduce core class to client lifecycle management
-
✅ Update method delegation
- ✅ Ensure proper method delegation to handlers
- ✅ Implement proper error propagation
- ✅ Add missing method implementations
-
✅ Implement cross-module communication
- ✅ Define clear interfaces for module interaction
- ✅ Ensure proper data flow between components
- ✅ Avoid circular dependencies
-
✅ Verify functionality preservation
- ✅ Ensure all methods have equivalent implementations
- ✅ Add logging to trace execution flow
- ✅ Create test cases for edge conditions
Phase 4: Legacy Compatibility and Testing (Days 11-12) ✅
-
✅ Create facade in classes.smtp.client.ts
- ✅ Keep original class signature
- ✅ Delegate to new implementation internally
- ✅ Ensure backward compatibility
-
✅ Add deprecation notices
- ✅ Mark legacy file with deprecation comments
- ✅ Add migration guide in comments
- ✅ Document breaking changes if any
-
✅ Update documentation
- ✅ Create detailed documentation for new architecture
- ✅ Add examples of how to use the new modules
- ✅ Document extension points
-
✅ Create comprehensive tests
- ✅ Ensure all modules have proper unit tests
- ✅ Add integration tests between modules
- ✅ Verify backward compatibility with existing code
Testing Strategy
-
Unit Testing
- Create unit tests for each client module in isolation
- Mock socket connections and server responses
- Test authentication mechanisms independently
- Test error handling and recovery scenarios
-
Integration Testing
- Test interactions between client modules
- Verify proper command flow and response handling
- Test full SMTP client communication flow
- Test TLS/STARTTLS integration
-
Production Testing
- Create comprehensive client production test suite (similar to server tests)
- Test against real SMTP servers with various configurations
- Test authentication with different providers
- Performance and reliability testing
-
Compatibility Testing
- Test with existing code that uses the legacy SmtpClient class
- Verify backward compatibility with EmailSendJob integration
- Document any necessary migration steps
Timeline Estimate
- Phase 1: 1-2 days
- Phase 2: 3-5 days
- Phase 3: 2-3 days
- Phase 4: 1-2 days
Total: 7-12 days of development time
Success Criteria
- All existing tests pass with the new implementation
- No regression in functionality or performance
- Each file is less than 300 lines of code
- Improved code organization and documentation
- Better separation of concerns between modules
- Easier maintenance and extension of SMTP client functionality
- Enhanced testability with modular components
- Comprehensive production test suite for SMTP client
Risks and Mitigations
Risk: Breaking existing EmailSendJob integration
Mitigation: Comprehensive test coverage and backward compatibility facade with existing interface
Risk: Performance degradation due to additional indirection
Mitigation: Performance benchmarking before and after refactoring, optimize hot paths
Risk: Increased complexity due to distributed client code
Mitigation: Clear documentation and proper module interfaces, follow server refactoring patterns
Risk: Connection pooling complexity in modular architecture
Mitigation: Careful design of connection-manager interface, thorough testing of connection lifecycle
Risk: Time overrun due to authentication complexity
Mitigation: Incremental approach with working checkpoints, start with simpler auth mechanisms
Future Extensions
Once this refactoring is complete, we can more easily:
- Add support for additional SMTP authentication mechanisms (OAUTH2, SCRAM-SHA, etc.)
- Implement advanced connection pooling strategies
- Add comprehensive production test suite matching server coverage
- Improve performance with targeted optimizations (pipelining, concurrent connections)
- Create specialized client versions for different use cases (bulk sending, transactional)
- Add client-side security features (connection validation, certificate pinning)
- Implement advanced retry and fallback strategies
- Add comprehensive monitoring and metrics collection