Implementing Strategy Pattern with Spring Plugin: A Payment Processing Example

Implementing Strategy Pattern with Spring Plugin: A Payment Processing Example


Introduction

In the realm of microservices, handling varying business logic elegantly is crucial for maintaining a clean and extensible codebase. The Strategy Design Pattern is a behavioral design pattern that facilitates the selection of an algorithm’s implementation at runtime from a family of similar algorithms. In a Spring Boot environment, this pattern becomes even more powerful with the integration of Spring Plugin, a library that simplifies implementing the Strategy Pattern. This article delves into an example of processing payments using different payment strategies, illustrating how Spring Plugin eases the implementation of the Strategy Pattern in a Spring Boot application.


The Strategy?Pattern

The essence of the Strategy Pattern lies in creating a family of interchangeable algorithms that can be selected and executed at runtime. This is achieved by defining a common interface for a set of classes representing different algorithms or strategies. The client code can then choose the appropriate implementation based on the context, without being tightly coupled to a particular algorithm.


Implementing Payment Strategies with Spring?Plugin

We’ll explore a practical example based on a payment processing scenario, where different payment strategies like Credit Card and Debit are required. The example is retrieved from a GitHub repository which demonstrates the implementation of the Strategy Pattern using Spring Plugin in a Spring Boot application.

Dependencies

<dependency>
   <groupId>org.springframework.plugin</groupId>
   <artifactId>spring-plugin-core</artifactId>
   <version>3.0.0</version>
</dependency>
<dependency>
   <groupId>org.springframework.plugin</groupId>
   <artifactId>spring-plugin-metadata</artifactId>
   <version>3.0.0</version>
</dependency>        

Strategy Interface and implementations from?it

First, we define a common interface PaymentStrategy with a method performPayment which all payment strategy implementations will adhere to. Next, we create separate classes for each payment strategy implementing the PaymentStrategy.

interface PaymentStrategy : Plugin<TicketPayment> {
    fun performPayment(value: TicketPayment)
}

class CreditPaymentStrategy : PaymentStrategy {
    override fun performPayment(value: TicketPayment) {
        println("Executing credit payment for [ $value ]!")
    }

    override fun supports(ticketPayment: TicketPayment): Boolean =
        ticketPayment.type == PaymentType.CREDIT
}

class DebitPaymentStrategy : PaymentStrategy {
    override fun performPayment(value: TicketPayment) {
        println("Executing debit payment for [ $value ]!")
    }

    override fun supports(ticketPayment: TicketPayment): Boolean =
        ticketPayment.type == PaymentType.DEBIT
}        

Injecting Strategies with Spring?Plugin

With Spring Plugin, we can easily inject all implementations of PaymentStrategy through Plugin Registry.

class PaymentService(private val pluginRegistry: PluginRegistry<PaymentStrategy, TicketPayment>) {

    fun processPayment(payment: TicketPayment) {
        pluginRegistry.getPluginFor(payment).ifPresent { strategy ->
            strategy.performPayment(payment)
        }
    }
}        

In this setup, Spring Plugin handles the injection of all PaymentStrategy implementations into the PaymentService class. The processPayment method finds the correct strategy based on the payment type, and delegates the payment processing to the selected strategy.

Strategies and Service Configurations

@Configuration
class PaymentStrategyConfig {
    @Bean
    @Qualifier("creditPaymentStrategy")
    fun creditPaymentStrategy() = CreditPaymentStrategy()

    @Bean
    @Qualifier("debitPaymentStrategy")
    fun debitPaymentStrategy() = DebitPaymentStrategy()
}

@Configuration
class PaymentServiceConfig {
    @Bean
    fun paymentService(pluginRegistry: PluginRegistry<PaymentStrategy, TicketPayment>) =
        PaymentService(pluginRegistry)
}        

Enabling Spring Plugin?Registry

@SpringBootApplication
@EnablePluginRegistries(
 value = [
  PaymentStrategy::class
 ]
)
class DemoStrategyApplication

fun main(args: Array<String>) {
 runApplication<DemoStrategyApplication>(*args)
}        

Injecting PaymentService into the Controller

@Validated
@RestController
@RequestMapping("/v1/api/tickets")
internal class DemoStrategyController(private val paymentService: PaymentService) {
    @PostMapping("/pay")
    fun pay(@RequestBody ticketPaymentRequest: TicketPaymentRequest): ResponseEntity<TicketPaymentResponse> =
        ticketPaymentRequest.to().let { payment ->
            paymentService.processPayment(payment).let {
                ResponseEntity
                    .accepted()
                    .body(TicketPaymentResponse.from(payment))
            }
        }
}        

For executing some tests, just execute the command below using the options CREDIT and DEBIT for the paymentType property.

curl --location 'https://localhost:9090/v1/api/tickets/pay' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json'
--data '{
    "amount": 100.00,
    "paymentType": "DEBIT"
}'        

The source code presented in this article: https://github.com/jether2011/demo-spring-plugin-strategy


Conclusion

The combination of the Strategy Design Pattern with Spring Plugin provides a clean and efficient way to handle varying business logic in a Spring Boot application. Through a practical payment processing example, we have seen how Spring Plugin simplifies the management and injection of different strategy implementations, promoting a clean and modular code structure.


要查看或添加评论,请登录

Jether Rodrigues的更多文章

社区洞察

其他会员也浏览了