私は現在、共有DB/共有スキーマアプローチを採用するマルチテナントアプリケーションに取り組んでいます。IOW、すべてのテーブルにTenantID列を定義することにより、テナントデータの分離を実施します。慣例により、すべてのSQL読み取り/書き込みには、Where TenantID ='?'を含める必要があります。句。理想的な解決策ではありませんが、後知恵は20/20です。
とにかく、私たちのアプリのほぼすべてのページ/ワークフローはテナント固有のデータを表示する必要があるため、プロジェクトの最初に、現在のユーザー資格情報(つまり、TenantIDとUserID)をカプセル化するためにシングルトンを採用するという(悪い)決定をしました。当時の私の考えは、データレイヤーのすべてのメソッドシグネチャにTenantIDパラメーターを追加したくないというものでした。
基本的な擬似コードは次のようになります。
public class UserIdentity
{
public UserIdentity(int tenantID, int userID)
{
TenantID = tenantID;
UserID = userID;
}
public int TenantID { get; private set; }
public int UserID { get; private set; }
}
public class AuthenticationModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.AuthenticateRequest +=
new EventHandler(context_AuthenticateRequest);
}
private void context_AuthenticateRequest(object sender, EventArgs e)
{
var userIdentity = _authenticationService.AuthenticateUser(sender);
if (userIdentity == null)
{
//authentication failed, so redirect to login page, etc
}
else
{
//put the userIdentity into the HttpContext object so that
//its only valid for the lifetime of a single request
HttpContext.Current.Items["UserIdentity"] = userIdentity;
}
}
}
public static class CurrentUser
{
public static UserIdentity Instance
{
get { return HttpContext.Current.Items["UserIdentity"]; }
}
}
public class WidgetRepository: IWidgetRepository{
public IEnumerable<Widget> ListWidgets(){
var tenantId = CurrentUser.Instance.TenantID;
//call sproc with tenantId parameter
}
}
ご覧のとおり、ここにはいくつかのコードの臭いがあります。これはシングルトンであるため、ユニットテストには対応していません。その上、CurrentUserとHttpContextオブジェクトの間には非常に緊密な結合があります。ひいては、これは、データレイヤー(shudder)にSystem.Webへの参照があることも意味します。
上記の理由でこのシングルトンを取り除くことによって、このスプリントの技術的負債を返済したいと思います。私は、より良い実装が何であるかについていくつか考えていますが、誰かが共有できるガイダンスや教訓を持っている場合、私は非常に義務付けられます。