特定于租户的业务逻辑的C#架构/模式

时间:2017-01-24 04:49:18

标签: c# design-patterns c#-6.0

假设您有一个多租户应用。 Tenant具有各种属性:

public class Tenant{
  public string TenantName {get; set;}
  public string TenantUrl {get; set;}
}

这样,当我的服务层发送电子邮件时,我可以执行以下操作:

 SendEmail(Tenant.FromEmailAddress, recipientEmailAddress)

这适用于属性。在我的业务逻辑中的许多地方,我遇到了必须考虑特定于租户的行为的情况。一个例子是检索主页的照片:

public List<string> GetPhotoUrls(){
     if(currentTenant == TenantA){
         // logic to go off to retrieve from one third party
     } else if (currentTenant == TenantB){
         // totally different logic
     } else...  // one for each tenant

     // do some stuff
     // return stuff
}

GetPhotoUrls是一个简单的例子 - 但在我的业务逻辑中的许多地方都存在类似的情况。我正在寻找一个简单模式,我可以在其中定义和实现特定于租户的逻辑。总体目标是将所有特定于租户的逻辑放在一个位置,这样租户的创建和定义就很容易。

我希望开发人员的经验可以阅读:

public List<string> GetPhotoUrls(){
     currentTenant.GetPhotoUrls(); // define this logic on the tenant object somehow
     // do some stuff
     // return stuff
}

有哪些模式/结构可用于实现此目的?

2 个答案:

答案 0 :(得分:0)

在您的案例中使用策略模式。当您看到switch语句或多个if语句以简化客户端以便将自定义实现委派给依赖接口时,最好应用该模式。您也可以结合工厂模式使用。为了说明这一点:

public interface ITenant{
   List<string> GetPhotoUrls();
}

public class TenantA:ITenant{
   public string TenantName {get; set;}
   public string TenantUrl {get; set;}
   public List<string> GetPhotoUrls(){
         //A implementation
   }
}

public class TenantB:ITenant{
   public string TenantName {get; set;}
   public string TenantUrl {get; set;}  
   public List<string> GetPhotoUrls(){
         //B implementation
   }
}

public class SomeTenantApp{
   public SomeTenantApp(ITenant tenant){
      _tenant = tenant;
   }

   public void DoSomething(){
     var urls  = _tenant.GetPhotoUrls();
     //do something
   }
}

public static class TenantFactory{
    public static ITenant Create(string id)
    {
        //logic to get concrete tenant
        return concreteTenant;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var tenant = TenantFactory.Create("A");
        var app = var SomeTenantApp(tenant);
        app.DoSomething();
    }
}

客户端(SomeTenantApp)不必更改。您将实现委托给拥有逻辑的具体类。

答案 1 :(得分:0)

如果您想构建SaaS,我强烈建议您使用ASP.NET Core和依赖注入来克服多租户问题。

您可以定义租户类:

public class AppTenant
{
    public string Name { get; set; }
    public string[] Hostnames { get; set; }
}   

接下来,您可以从当前请求中解析租户

public class AppTenantResolver : ITenantResolver<AppTenant>
{
    IEnumerable<AppTenant> tenants = new List<AppTenant>(new[]
    {
        new AppTenant {
            Name = "Tenant 1",
            Hostnames = new[] { "localhost:6000", "localhost:6001" }
        },
        new AppTenant {
            Name = "Tenant 2",
            Hostnames = new[] { "localhost:6002" }
        }
    });

    public async Task<TenantContext<AppTenant>> ResolveAsync(HttpContext context)
    {
        TenantContext<AppTenant> tenantContext = null;
        // it's just a sample...
        var tenant = tenants.FirstOrDefault(t =>
            t.Hostnames.Any(h => h.Equals(context.Request.Host.Value.ToLower())));

        if (tenant != null)
        {
            tenantContext = new TenantContext<AppTenant>(tenant);
        }

        return tenantContext;
    }
}

接线:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMultitenancy<AppTenant, AppTenantResolver>();
}  

获取当前租户(无论何时需要):

public class HomeController : Controller
{
    private AppTenant tenant;

    public HomeController(AppTenant tenant)
    {
        this.tenant = tenant;
    }
    .
    .
    .
}  

有关详细信息,请查看SaasKit
Building multi-tenant applications with ASP.NET Core (ASP.NET 5)