I'm struggeling with a piece of code, and could use some pointers.
I'm creating an API for a frontend website. This API can call various backend systems based on the tenant. Which system to call is saved in a tenant entry in the database.
My API project consists of a couple of different projects:
- API
- BusinessLogic
- Data
- Dto
My API calls my logic layer, for example to retrieve a customer object. In this call a tenantId is passed (which is taken from the authorized identity claim). My logic does some checks and then wants to call the tenant specific backend system. To do this, I need to do the following:
- Retrieve Tenant Settings from DB
- Figure out what backend is configured, and create an instance of that data class
- Call this data class, and retrieve a customer
The data class implements an interface IBackendConnector.
Because all my logic classes could potentially call the tenant specific data, i want to create a single factory that accepts a tenantId, and returns a class that implements IBackendConnector. The call to the database to retrieve the tenant is async, and the API call itself is async.
I created an abstract class called 'DataFactory' that has the (public and private) property IBackendConnector, lets name it Connector. It also has a constructor that expects a tenantid, which it saves as a private property. Every logic class implements this DataFactory. The moment i need to call the backend, I can simple call some nice and clean code:
Connector.MethodIWhishToCall();
In the getter of the public property I check if there is a value in the private property, and if not, it needs to retrieve the tenant settings, and create the correct data class, so we only create the class when we need it. Here is where i failed, because i can't call the async method in this property getter (without deadlocking).
I changed this code a lot, and i'm not happy with the current solution i'm using, which is an abstract class that is implemented in every logic class (that needs it), with a static async method in it.
public abstract class ExternalDataFactory
{
public static async Task<IBackendDataConnector> GetDataHandler(string tenantId)
{
var logic = new TenantLogic();
var tenant = await logic.GetTenantById(tenantId);
var settings = tenant.Settings;
if (settings == null)
throw new ArgumentNullException("Settings");
switch (settings.EnvironmentType)
{
case Constants.EnvironmentType.ExampleA:
return new DataHandlerClass(settings);
case Constants.EnvironmentType.ExampleB:
throw new NotImplementedException();
default:
throw new Exception("Invalid EnvironmentType");
};
}
}
And every method that uses this handler calls the following code:
var handler = await GetDataHandler(tenantId);
return await handler.DoSomething();
Or in a single line:
return await (await GetDataHandler(tenantId)).DoSomething();
The ultimate goal: All logic classes must be able to call the correct data class without having to worry which data class, or having to manually retrieve the settings first and pass it to the factory. The factory should be able to retrieve the settings (if the settings are null) and return the correct IBackendConnector implementation. What is best-practice / best pattern to do this?
TL/DR: Trying to implement a factory pattern that needs to call an async method to decide which concrete class to return. What is the best way to go about this?
Aucun commentaire:
Enregistrer un commentaire