fastway-app/
├── api/ # Business Logic Layer
│ ├── config.php # Configuration & utilities
│ ├── track.php # Tracking business logic
│ └── quote.php # Quote business logic
├── assets/ # Presentation Layer
│ ├── css/ # Styling
│ ├── js/ # Client-side logic
│ └── images/ # Static assets
├── includes/ # Shared UI Components
│ ├── header.php # Navigation
│ └── footer.php # Footer
├── index.php # Front Controller
├── track.php # Track view
├── quote.php # Quote view
└── logs/ # Application logs
- Separation of Concerns: API logic separate from presentation
- Modularity: Each component has single responsibility
- Scalability: Easy to add new features without affecting existing code
- Maintainability: Clear file organization for team collaboration
- Security: API keys and logic isolated from public access
// api/track.php - Pure business logic
- Validates tracking numbers
- Calls external API
- Parses responses
- Returns structured data
- No HTML/UI codeResponsibilities:
- Data validation
- API communication
- Error handling
- Data transformation
- Logging
// Centralized API configuration
- API credentials management
- HTTP client setup (cURL)
- Request/response handling
- Retry logic
- Timeout managementResponsibilities:
- External service communication
- Connection pooling
- Rate limiting
- Circuit breaker pattern (future)
// track.php - Pure presentation
- Displays forms
- Shows results
- No business logic
- Calls API layer via AJAXResponsibilities:
- User interface rendering
- Form validation (client-side)
- User feedback
- Responsive design
// Interface for all courier providers
interface CourierProviderInterface {
public function track($trackingNumber);
public function getQuote($params);
public function validateTrackingNumber($number);
}
// Fastway implementation
class FastwayProvider implements CourierProviderInterface {
public function track($trackingNumber) {
// Fastway-specific logic
}
public function getQuote($params) {
// Fastway-specific logic
}
}
// DHL implementation
class DHLProvider implements CourierProviderInterface {
public function track($trackingNumber) {
// DHL-specific logic
}
}
// Provider Factory
class CourierProviderFactory {
public static function create($provider) {
switch($provider) {
case 'fastway':
return new FastwayProvider();
case 'dhl':
return new DHLProvider();
case 'ups':
return new UPSProvider();
default:
throw new Exception("Unknown provider");
}
}
}
// Usage
$provider = CourierProviderFactory::create('fastway');
$result = $provider->track('Z60000983328');// config/providers.php
return [
'fastway' => [
'class' => FastwayProvider::class,
'api_key' => getenv('FASTWAY_API_KEY'),
'base_url' => 'https://sa.api.fastway.org',
'timeout' => 30
],
'dhl' => [
'class' => DHLProvider::class,
'api_key' => getenv('DHL_API_KEY'),
'base_url' => 'https://api.dhl.com',
'timeout' => 30
]
];// API Version Adapter
interface APIVersionInterface {
public function makeRequest($endpoint, $params);
public function parseResponse($response);
}
// V3 Adapter
class FastwayAPIv3 implements APIVersionInterface {
public function makeRequest($endpoint, $params) {
return $this->callAPI("/v3/{$endpoint}", $params);
}
public function parseResponse($response) {
// V3-specific parsing
return [
'tracking_number' => $response['result']['LabelNumber'],
'status' => $response['result']['Scans'][0]['StatusDescription']
];
}
}
// V4 Adapter (future)
class FastwayAPIv4 implements APIVersionInterface {
public function makeRequest($endpoint, $params) {
return $this->callAPI("/v4/{$endpoint}", $params);
}
public function parseResponse($response) {
// V4-specific parsing (different structure)
return [
'tracking_number' => $response['data']['label_number'],
'status' => $response['data']['current_status']
];
}
}
// Configuration-based version selection
class APIVersionManager {
private $adapter;
public function __construct() {
$version = getenv('FASTWAY_API_VERSION') ?: 'v3';
$this->adapter = $this->getAdapter($version);
}
private function getAdapter($version) {
$adapters = [
'v3' => new FastwayAPIv3(),
'v4' => new FastwayAPIv4()
];
return $adapters[$version];
}
}- Minimal Code Changes: Switch API versions via config
- Backward Compatibility: Support multiple versions simultaneously
- Easy Testing: Mock different API versions
- Gradual Migration: Phase out old versions slowly
// Used in: CourierProviderFactory
// Why: Creates provider instances without exposing instantiation logic// Used in: CourierProviderInterface implementations
// Why: Allows switching between different courier providers at runtime// Used in: API version adapters
// Why: Makes incompatible API versions work together// For: Database connections, cache instances
class Database {
private static $instance = null;
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new PDO(...);
}
return self::$instance;
}
}// For: Data access abstraction
class TrackingRepository {
public function save($tracking);
public function findByNumber($number);
public function findRecent($limit);
}// For: Testing and flexibility
class TrackingService {
private $provider;
private $cache;
public function __construct(
CourierProviderInterface $provider,
CacheInterface $cache
) {
$this->provider = $provider;
$this->cache = $cache;
}
}fastway-app/
├── app/
│ ├── Controllers/
│ │ ├── TrackingController.php
│ │ ├── QuoteController.php
│ │ └── HomeController.php
│ ├── Models/
│ │ ├── Tracking.php
│ │ ├── Quote.php
│ │ └── CourierProvider.php
│ ├── Views/
│ │ ├── layouts/
│ │ │ ├── header.php
│ │ │ └── footer.php
│ │ ├── tracking/
│ │ │ ├── index.php
│ │ │ └── results.php
│ │ └── quotes/
│ │ ├── index.php
│ │ └── results.php
│ ├── Services/
│ │ ├── CourierService.php
│ │ ├── CacheService.php
│ │ └── LoggingService.php
│ └── Repositories/
│ ├── TrackingRepository.php
│ └── QuoteRepository.php
├── config/
│ ├── app.php
│ ├── database.php
│ └── providers.php
├── public/
│ ├── index.php (Front Controller)
│ └── assets/
├── routes/
│ └── web.php
└── tests/
├── Unit/
└── Integration/
// app/Controllers/TrackingController.php
class TrackingController {
private $trackingService;
public function __construct(TrackingService $service) {
$this->trackingService = $service;
}
public function index() {
return view('tracking/index');
}
public function track(Request $request) {
$trackingNumber = $request->input('tracking_number');
try {
$result = $this->trackingService->track($trackingNumber);
return json(['success' => true, 'data' => $result]);
} catch (Exception $e) {
return json(['success' => false, 'message' => $e->getMessage()]);
}
}
}// app/Models/Tracking.php
class Tracking {
private $id;
private $trackingNumber;
private $status;
private $events;
private $createdAt;
public function save() {
// Persist to database
}
public static function findByNumber($number) {
// Retrieve from database
}
}// app/Services/TrackingService.php
class TrackingService {
private $provider;
private $cache;
private $repository;
public function track($trackingNumber) {
// Check cache first
$cached = $this->cache->get("track_{$trackingNumber}");
if ($cached) return $cached;
// Call API
$result = $this->provider->track($trackingNumber);
// Cache result
$this->cache->set("track_{$trackingNumber}", $result, 300);
// Save to database
$this->repository->save($result);
return $result;
}
}// routes/web.php
Route::get('/', 'HomeController@index');
Route::get('/track', 'TrackingController@index');
Route::post('/api/track', 'TrackingController@track');
Route::get('/quote', 'QuoteController@index');
Route::post('/api/quote', 'QuoteController@calculate');- ✅ Clear separation of concerns
- ✅ Testability - Each component can be tested independently
- ✅ Scalability - Easy to add features
- ✅ Team collaboration - Multiple developers can work simultaneously
- ✅ Code reusability - Models and services can be shared
- ✅ Maintainability - Changes isolated to specific layers
The current implementation follows good practices with clear separation between business logic, API integration, and presentation. However, it can be significantly improved by:
- Implementing design patterns (Factory, Strategy, Adapter)
- Adopting MVC architecture for better organization
- Using dependency injection for flexibility
- Creating service layer for complex business logic
- Implementing repository pattern for data access
These improvements would make the system more maintainable, testable, and scalable while preparing it for enterprise-level requirements.