0
votes

I have a set of componentes registered to StructureMap. What should be the best way to resolve a component depending on the actual Tenant?

Small example: There are two tenants, say, Yellow and Green. I have an IValidator that has two implementations: YellowValidator and GreenValidator. Say the application is MVC and that the tentant comes form the URL. So, I just need the proper IValidator to be injected depending on the tenant.

I've seen many solutions for multi-tenant applications that deals only with multitenancy of data, normaly configuring different databases depending on the tenant. That involves only parameter passing. But this is the case where variation occurs in behavior, not in data. I want the IoC container to Resolve the right instance transparently.

EDIT: more info: The IValidator interface have a simple method bool Validate(), but the implementation require some injection.

There are other custom validators, but they are used by both tenants.

There is a clear tentant strategy based on the URL. This means that each request can have a different tenant, and that a single application serves both tenants.

1
Could you give a bit more background information, such as: How does the IValidator interface actually look like? How are validators currently registered. Are YellowValidator and GreenValidator the only two validators in the system, or are there many validators that are almost all used for both tenants, and just a few that differ? How do you determine what the tenant is? Is there one tenant per web application / app domain (determined on startup, possible registered in XML) or do you have multiple tenants at the same time and can each request have a different tenant. - Steven

1 Answers

1
votes

There are many ways to skin a cat. It's hard for me to guess the design of your application, so here is an idea. Things that come in mind are to hide validators behind a composite, to allow users of the IValidator interface to know nothing about having many implementations. Such composite can look like this:

public class ValidatorComposite : IValidator
{
    private IEnumerable<IValidator> validators;

    public ValidatorComposite(
        IEnumerable<IValidator> validators)
    {
        this.validators = validators;
    }

    public bool Validate(object instance)
    {
        return this.validators.All(v => v.Validate(instance));
    }
}

You can create multiple composites and register them by key where the key is the name of the tenant (but without keyed registrations is probably just as easy). Those composites can be wrapped in yet another composite that will delegate to the proper tenant-specific composite. Such a tenant-selecting composite could look like this:

public class TenantValidatorComposite : IValidator
{
    private ITenantContext tenantContext;
    private IValidator defaultValidator;
    private IDictionary<string, IValidator> tenantValidators;

    public ValidatorComposite(
        ITenantContext tenantContext,
        IValidator defaultValidator,
        IDictionary<string, IValidator> tenantValidators)
    {
        this.tenantContext = tenantContext;
        this.defaultValidator = defaultValidator;
        this.tenantValidators = tenantValidators;
    }

    public bool Validate(object instance)
    {
        string name = this.tenantContext.CurrentTenant.Name;

        return this.defaultValidator.Validate(instance) &&
            this.tenantValidators[name].Validate(instance);
    }
}

The ITenantContext is an abstraction that allows you to get the current tenant within the current context. You probably already have something like that in place, but I imagine an implementation to look something like this:

class UrlBasedTenantContext : ITenantContext
{
    public Tenant Current
    {
        get
        {
            // Naive implementation.
            if (HttpContext.Current.Request.Url.Contains("tenant1"))
            {
                return Tenant1;
            }

            return Tenant2;
        }
    }
}

Create a TenantValidatorComposite would be easy:

var defaultValidator = CompositeValidator(
    GetAllDefaultValidators());

var tenantValidators = new Dictionary<string, IValidator>()
{
    { "tenant1", new CompositeValidator(GetValidatorsFor("tenant1")) },
    { "tenant2", new CompositeValidator(GetValidatorsFor("tenant2")) },
};

var tenantValidator = new TenantValidatorComposite(
    new UrlBasedTenantContext(),
    defaultValidator,
    tenantValidators);

I hope this helps.