複合キーを使用してデータベース層でテナントの分離を強制する(したがって、不適切な3次の関係を防止する)マルチテナントアプリケーション用の単一インスタンスデータベースを設計しました。
したがって、スキーマは次のようになります。
TABLE Tenants (
TenantId int NOT NULL IDENTITY(1,1)
Name nvarchar(100) NOT NULL
IsAdmin bit NOT NULL
PRIMARY KEY( TenantId )
)
TABLE Users (
TenantId int NOT NULL
UserId int NOT NULL IDENTITY(1,1)
UserName nvarchar(100) NOT NULL
PRIMARY KEY( TenantId, UserId )
CONSTRAINT FK_Tenants_Users FOREIGN KEY ( TenantId ) REFERENCES Tenants ( TenantId )
)
TABLE Documents (
TenantId int NOT NULL
DocumentId int NOT NULL IDENTITY(1,1)
CreatedByUserId int NOT NULL
ModifiedByUserId int NOT NULL
PRIMARY KEY ( TenantId, DocumentId )
CONSTRAINT FK_Tenants_Documents FOREIGN KEY ( TenantId ) REFERENCES Tenants ( TenantId )
CONSTRAINT FK_Documents_Creators FOREIGN KEY ( TenantId, CreatedBy ) REFERENCES Users ( TenantId, CreatedBy )
CONSTRAINT FK_Documents_Modifiers FOREIGN KEY ( TenantId, ModifiedBy ) REFERENCES Users ( TenantId, ModifiedBy )
)
システムには他にも多くのテーブルがありますが、エンティティがテナントに「属している」場合、そのエンティティの主キーは複合であり、TenantId
が含まれるという同じ概念を共有します。
...これは、Document
のModifiedByUserId
が別のテナントのUserId
を参照できる状況を防ぐのに役立ちます。テナントは完全に隔離されることを意図しているため、この施行は理想的です。
例外...管理上のアクションはどのように行われるべきですか?このシステムでは、テナントをIsAdmin
としてマークできることに注意してください。これにより、すべてのテナントのリソースにアクセスできるだけでなく、リソースを編集および作成することもできます。これは、管理者のTenantId
は異なります-したがって、管理ユーザーは、そのユーザーからのものとしてマークされた別のテナンシーでリソースを作成できません。
...少なくとも管理者が既存のリソースを変更するシナリオでは、ModifiedByUserId
の値は変更されないままになる可能性があります。
したがって、このシナリオを有効にする(別のテナントで新しいリソースを作成する)には、いくつかのオプションがあります。
Users
エンティティセットを混乱させることです。オプション2(下記)と比較した場合のこのアプローチのわずかな利点は、管理テナントでもテナントの完全な分離を維持できるため、IDの競合や参照の破損なしにテナンシーを別のDBインスタンスにシャードオフできることです。TenantId
をUser
の複合主キーから削除します-欠点は、テナントの分離を弱めることです。この状況に対処するための他のオプションはありますか?
私はオプション1(ゴーストユーザー)に傾いていますが、それは正しくありません。また、特殊なケースのロジックでは外部キーの値を現在のユーザーではなくゴーストユーザーに設定する必要があるため、複雑さが増します。現在のユーザーが管理テナントに属している場合:
resource.CreatedByUserId = currentUser.TenantId == resource.TenantId ? currentUser.UserId ? getGhostUserId( resource.TenantId )
マルチテナントアプリケーションでは、ユーザーを他のテナントベースのものから分離し、代わりにテナントベースの個別の権限管理を追加しました。これにより、複数のテナントへのアクセスを1人のユーザー(またはユーザーグループ)に許可できます。これは、すべてのSEサイトで機能する1つのアカウントがある場合と同じです。
この設定では、「管理」テナントではなく、各テナントの管理ロールがあり、誰がこのロールのメンバーであるかを選択できます。グループサポートと、すべてのテナントの管理役割の暗黙的なメンバーである管理者グループを追加することにより、グローバル管理者を実装することもできます。
非常に異なって行ったもう1つのことは、複合キーではなくGUIDを主キーとして選択したことです。賛否両論については常に議論がありますが、衝突がないこと(テナントデータのマージ、同期、転送など)と、キーが煩雑にならないことが重要でした。これは、私が言及した権限と一緒に、必要に応じて(または必要に応じて)テナント間で共有されるデータなどを実装することもできます。
テナントの分離では、実際にはテナントごとに個別のデータベースがあるため、一度に1つのテナントのみをバックアップ、復元、または転送することもできます。
複合キーを保持し、テナントを同じデータベースに保持することを想定している場合、質問が示唆しているように思われる範囲内に、次のような他のオプションがあります...
1。管理者テナントの概念を排除し、各管理者ユーザーにユーザーテーブル内の実際のユーザーIDを付与します。
つまり、管理者ユーザーは、Usersテーブルに複数のユーザー行を持つことになります。管理ユーザーとしてのIDは別のテーブルにある可能性があります。たとえば、AdminUserのように...
create table AdminUser(AdminUserID int, etc...)
データベースとポリシーで許可されている場合、Usersテーブルには、AdminUserを参照するnull可能な外部キーを設定できます。
2。または...のような別のテーブルを作成します
create table TenantUser (TenantID int not null, UserID int not null);
外部キーがTenantUserテーブルを参照するようにします。このテーブルには、有効なユーザーのみをリストする必要があります。
したがって、各ユーザー所属は、Users-> Tenantsの関係を介してテナントに送信されます。管理テナントはまだ存在しており、管理ユーザーを所有しています。
ただし、TenantUserを使用して、各ユーザーに複数のテナントへの権限を付与できます。
どちらのオプションでも、監査証跡などの目的で実際のユーザーIDを記録し、Ghostユーザーなしでコードをかなり標準化しておくことができます。
あなたは代替案を求めました、そして私はこれを根本的に異ならない代替案を意味すると解釈します。私はこれらの代替案を推奨するつもりはありません。
もちろん、各顧客に個別のデータベースを提供するなど、根本的に異なる代替案もいくつかあります。
正しい答えは本当にあなたのビジネスに依存します。