Careeroza
Real World Example
Swapping Payment and SMS Providers
Indian payment and notification providers change often (Razorpay vs PayU vs Cashfree, MSG91 vs Twilio for SMS). Apps like Swiggy keep business code independent of the provider by depending on interfaces and letting Spring inject the right implementation.
CheckoutService --needs--> PaymentGateway (interface)
^ ^
| |
RazorpayGateway FakeGateway
(prod profile) (local / tests)ShopEase Checkout Wiring
public interface SmsSender {
void send(String phone, String text);
}
@Component
@Profile("prod") // only in production (profiles: see Configuration)
class Msg91SmsSender implements SmsSender {
public void send(String phone, String text) { /* call MSG91 HTTP API */ }
}
@Component
@Profile("!prod") // everywhere else: just log it
class LoggingSmsSender implements SmsSender {
private static final Logger log = LoggerFactory.getLogger(LoggingSmsSender.class);
public void send(String phone, String text) { log.info("SMS to {}: {}", phone, text); }
}
@Service
public class CheckoutService {
private final PaymentGateway gateway;
private final SmsSender sms;
public CheckoutService(PaymentGateway gateway, SmsSender sms) {
this.gateway = gateway;
this.sms = sms;
}
public void checkout(long orderId, int amount, String phone) {
gateway.charge(orderId, amount);
sms.send(phone, "ShopEase: order #" + orderId + " confirmed. Amount Rs." + amount);
}
}Example
Running locally (no prod profile) uses the logging sender:
./mvnw spring-boot:runExample output
... INFO c.s.notify.LoggingSmsSender : SMS to 98XXXXXX10: ShopEase: order #101 confirmed. Amount Rs.2998
Key Takeaways
- Depend on interfaces, inject implementations through the constructor.
- Use stereotype annotations for your classes and
@Beanfor library classes. - Keep singleton beans stateless.
- Changing a provider means adding one class, not editing every service.