Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to choose spring configuration in runtime based on a tenant?

I would like to be able to choose specific Spring (or Grails) context configuration based on the tenant that user belongs to in runtime. Let's say I use Spring Security and I retrieve tenantId during login. Imagine now I have a two tenants and they pay different commission. How to inject specific service into a controller without too much plumbing? Here are two different contexts. So, I should inject different ExchangeService based on tenant.

@Configuration
public class FooTenant{
@Bean
public ExchangeService bar() {
  return new ZeroCommisionExchangeService ();
  }
}


@Configuration
public class BarTenant{
@Bean
public ExchangeService bar() {
  return new StandardCommisionExchangeService ();
  }
}

Edit: I am aware I can obtain reference to Spring context and ask for service "manually", but I am looking for a more generic solution where this problematic is solved by IoC framework.

like image 600
Dan Avatar asked Nov 26 '13 03:11

Dan


People also ask

What is the use of @configuration in spring boot?

Spring @Configuration annotation is part of the spring core framework. Spring Configuration annotation indicates that the class has @Bean definition methods. So Spring container can process the class and generate Spring Beans to be used in the application.

What is multi tenant in spring?

Multi-tenancy refers to an architecture in which a single instance of a software application serves multiple tenants or customers. It enables the required degree of isolation between tenants so that the data and resources used by tenants are separated from the others.

How do I apply different application properties in spring boot?

Properties files are used to keep 'N' number of properties in a single file to run the application in a different environment. In Spring Boot, properties are kept in the application. properties file under the classpath. Note that in the code shown above the Spring Boot application demoservice starts on the port 9090.


1 Answers

While it is possible to swap beans instantiated in a spring context at runtime (HotswappableTargetSource), it is not meant for use cases such as yours.

Remember there is one Spring Context for your application, all threads use the same instances (in most cases), this implies when you swap out a bean implementation, you are affectively doing this for all your application's users. To prevent this, you run into issues of ensuring Thread Safety, employing Thread Locals, as listed in another answer.

While it is possible to continue this approach and arrive at an implementation that gets the job done, it would definitely be a very contrived way of solving this problem.

You should take a step back and look at your problem in a more wholesome, system wide design point of view. Bust out your patterns books and look at how this can be resolved, regardless of whether you use Spring or an other framework. Service Locator, Factory bean etc described in some of the answers above is a step in the correct direction.

Your Use Case is pretty common for multi-tenant applications. You need to narrow down things that are likely to change based on a tenantId versus things that are constant across.

For instance as mentioned in the question, each Tenant might have a different commission amount or even different algorithm for commission calculation. A simple solution to this would be to implement a CommissionCalculationService which accepts a tenantId, and any other domain object based on which commission is to be calculated, I would imagine this would be something like Order or Sale, whatever makes sense in your application.

You now need a CommissionServiceFactory or a ServiceLocator which will contain tenant specific implementations of the CommissionCalculationService. The Service Locator is instantiated when the Spring context loads, and is injected with implementation classes also at application startup.

When you want to calculate commission for a tenant, you basically obtain the tenantId from the user's login, pass the tenant id to your service locator, based on the tenantId passed, the service locator returns the appropriate instance of a Service Implementation. In your calling class, use this instance to calculate the commission for the tenant.

Another pattern to consider is the Strategy Pattern, or even Template Pattern.

Bottom line, even if you want tenant specific logic implemented cleanly, don't thing about changing the beans loaded in the context. Have classes in your context that can handle all your tenant specific logic. Rely on design patterns to use the correct bean from the context based on the tenant id.

I apologize if the answer was a little verbose, I felt it was needed to explain why I think updating beans in a loaded Spring Context is not the appropriate solution.

like image 145
Akshay Avatar answered Nov 07 '22 19:11

Akshay