Factory Pattern in a Spring Boot Project

📘 Premium Read: Access my best content on Medium member-only articles — deep dives into Java, Spring Boot, Microservices, backend architecture, interview preparation, career advice, and industry-standard best practices.

🎓 Top 15 Udemy Courses (80-90% Discount): My Udemy Courses - Ramesh Fadatare — All my Udemy courses are real-time and project oriented courses.

▶️ Subscribe to My YouTube Channel (176K+ subscribers): Java Guides on YouTube

▶️ For AI, ChatGPT, Web, Tech, and Generative AI, subscribe to another channel: Ramesh Fadatare on YouTube

In real project work, you often have situations where your code needs to create different objects based on input. You might start with a few if-else statements or a switch, but soon it grows and gets hard to maintain.

This is where the Factory Pattern becomes useful.

In this article, we’ll walk through:

  • What Factory Pattern is
  • Why we use it in backend development
  • How to implement it in a Spring Boot project
  • A real-world e-commerce example: choosing a payment method
  • Best practices to follow

Let’s get started.


What Is the Factory Pattern?

The Factory Pattern is a way to create objects without writing a bunch of if-else or switch code every time.

It means:

Let a separate class (the factory) decide which object to return, based on some input.

This keeps your business logic clean, and your code easier to manage.


Real Project Example: Payment Service in E-Commerce

Use Case:

You have an OrderService. Users can choose different payment types:

  • Credit Card
  • PayPal
  • UPI

Each one has different logic for processing payments.

Let’s implement this step-by-step.


✅ Step 1: Define a Common Interface

public interface PaymentProcessor {
    void pay(String orderId, double amount);
}

This is the contract every payment method will follow.


✅ Step 2: Create Implementations

Credit Card:

@Component("creditCard")
public class CreditCardProcessor implements PaymentProcessor {
    @Override
    public void pay(String orderId, double amount) {
        System.out.println("Paid " + amount + " using Credit Card for Order " + orderId);
    }
}

PayPal:

@Component("paypal")
public class PayPalProcessor implements PaymentProcessor {
    @Override
    public void pay(String orderId, double amount) {
        System.out.println("Paid " + amount + " using PayPal for Order " + orderId);
    }
}

UPI:

@Component("upi")
public class UpiProcessor implements PaymentProcessor {
    @Override
    public void pay(String orderId, double amount) {
        System.out.println("Paid " + amount + " using UPI for Order " + orderId);
    }
}

Notice the @Component("beanName") — we’ll use this in the factory.


✅ Step 3: Build the Factory

@Component
public class PaymentFactory {

    private final Map<String, PaymentProcessor> paymentMap;

    @Autowired
    public PaymentFactory(Map<String, PaymentProcessor> paymentMap) {
        this.paymentMap = paymentMap;
    }

    public PaymentProcessor getProcessor(String method) {
        PaymentProcessor processor = paymentMap.get(method.toLowerCase());
        if (processor == null) {
            throw new IllegalArgumentException("Unknown payment method: " + method);
        }
        return processor;
    }
}

How it works:

  • Spring injects all PaymentProcessor beans into the map
  • The bean name (from @Component("paypal")) becomes the key
  • You fetch the correct implementation based on the method input

✅ Step 4: Use the Factory in a Service

@Service
public class OrderService {

    private final PaymentFactory paymentFactory;

    @Autowired
    public OrderService(PaymentFactory paymentFactory) {
        this.paymentFactory = paymentFactory;
    }

    public void placeOrder(String orderId, double amount, String paymentMethod) {
        // Other order logic here

        PaymentProcessor processor = paymentFactory.getProcessor(paymentMethod);
        processor.pay(orderId, amount);
    }
}

Simple. Clean. No long if-else chains.


✅ Step 5: Create a REST Controller

@RestController
@RequestMapping("/api/orders")
public class OrderController {

    private final OrderService orderService;

    @Autowired
    public OrderController(OrderService orderService) {
        this.orderService = orderService;
    }

    @PostMapping("/place")
    public ResponseEntity<String> placeOrder(@RequestParam String orderId,
                                             @RequestParam double amount,
                                             @RequestParam String method) {
        orderService.placeOrder(orderId, amount, method);
        return ResponseEntity.ok("Order placed using " + method);
    }
}

Test it using curl or Postman:

POST http://localhost:8080/api/orders/place?orderId=ORD123&amount=1000&method=paypal

Why This Pattern Works Well in Spring Boot

  • Decoupling: Each payment method is in its own class
  • Scalability: Want to add Apple Pay tomorrow? Just add a new class and register the bean
  • Clarity: No messy logic in the service
  • Spring does the wiring: You don’t need to manually manage dependencies

✅ Best Practices

  • Use lowercase or enums for method names to avoid key mismatches
  • Catch IllegalArgumentException at controller level and return proper HTTP error
  • Don’t put business logic inside the factory — keep it just for creating the right object
  • Add logs when a payment processor is selected (useful for debugging)

⚠️ What to Avoid

  • Don’t use if-else blocks to select processors — that’s what the factory is for
  • Don’t pass around too many dependencies — keep each processor focused
  • Don’t hardcode the logic inside the controller — let the service layer handle it

Summary

What We Did Why It Matters
Defined a common interface Keeps all processors consistent
Created separate implementations Keeps each payment method in its own class
Used Spring’s DI with map injection Clean and dynamic lookup of beans
Built a factory to return the right bean No more long if-else chains
Called factory from service Keeps controller and service layers clean

Final Thoughts

In real Spring Boot projects, your service classes can easily become bloated with decision-making code. The Factory Pattern helps you manage this by putting the creation logic in one place.

With Spring’s help, you can implement factories easily using dependency injection. It’s a clean, testable, and maintainable solution — especially when you deal with multiple strategies like payment gateways, notification channels, file parsers, etc.

If your service has a long switch or if chain based on type — it’s time to build a factory.

Comments

Spring Boot 3 Paid Course Published for Free
on my Java Guides YouTube Channel

Subscribe to my YouTube Channel (165K+ subscribers):
Java Guides Channel

Top 10 My Udemy Courses with Huge Discount:
Udemy Courses - Ramesh Fadatare