私はSAASアプリケーションを開発しています。単一のWebアプリケーションがIISでホストされ、複数の企業にサービスを提供し、各企業が独自のデータベースを持ちます。Webアプリケーションとデータベースはで実行されます。同じサーバー。
私の質問は、URLに基づいてデータベース接続文字列を変更するためのベストプラクティスは何ですか?サービスレイヤーの各関数は、db接続文字列を識別するためにdbName/companynameを渡す必要がありますか?
現在、データレイヤーの各関数でdbnameを渡しており、接続文字列を返しています。次に、関数はdbに接続し、必要な操作を実行します。
オンラインでいくつかのマルチテナントデータベースクエリを確認しましたが、クエリに関連する回答が見つかりませんでした。実装の例はどれも大きな助けになります。
私のやり方は次のとおりです:
function Send()
{
_emailService.SendEmail(emailId, dbName);
}
public class EmailService()
{
Public SendEmail(string emailId, string dbName)
{
var connStr = SQLHelper.GetConnectionStr(dbName);
...
...
...
connect to db to fetch email details and then send email
}
}
個別のインスタンスの実行
論理的に分離された複数のサービスインスタンスを運用しているようです(会社ごとに1つ)。基本的に、あなたの質問は、この隔離が維持されることを確実にするための最善の方法についてです。
現在、分離はアプリケーション内で実現されています。 @MetaFightが指摘したように、これにはコードのバグがコードを壊す可能性があるというリスクがあります。明らかな候補とは、リクエスト間でメモリに保持されるものです。キャッシュデータ。
サービスのさまざまな(論理)インスタンスは相互作用する必要がないように見えるため(つまり、A社のデータはB社のデータに影響を与えない)、アプリケーションから分離を移動して個別に処理することを検討してください。 (繰り返しますが、@ MetaFightのコメントを参照してください)。
これにより、アプリケーションの複雑さが軽減されますが、展開の複雑さが増します。このシナリオでも、状態がリークする可能性があります。構成を間違えた場合は、A社のサービスをB社のデータベースに接続します。ただし、これでこの問題をアプリケーションのロジックから分離し、利用可能なすべてのDevOpsツールを使用してこの(一般的な)問題を個別に処理できるようになりました。
現在の状態から開始
ただし、アプリケーションはすでに実装されていると思います。そのため、アプローチを切り替えることは(すぐに)オプションではない可能性があります。その場合、少なくともドメインロジックは、複数の会社が存在するという事実を認識していない必要があります。また、要求間で状態を維持するべきではありません。おそらく、次のようにアプリケーションをモジュール化できます。
データベースセレクター
URLをデータベースアクセサオブジェクトに変換します。このオブジェクトを介してすべてのリクエストを行います。会社に依存している他のすべてについても同じことを行います。
ビジネス機能
データの計算、電子メールの送信など。必要に応じてデータベースアクセサーオブジェクトを使用します。このモジュールは、複数の会社があることを認識していません。さらに良いことに、DBアクセスを独自のレイヤーに配置し、データのみをビジネスロジックに提供します。
リクエストハンドラ
要求データを受信し、正しいデータベースアクセサーを取得して、ビジネス層に提供します。
このアーキテクチャは大幅に改善できますが、それは問題の範囲外です。 (たとえば、 ここ を見てください)。
アプリケーションをこの形にすると、次のステップに進むのがはるかに簡単になります。データベースセレクターに常に同じデータベース接続を提供させ、構成ファイルから読み取ることができます。そして、この設定を必要な回数だけ複製します。
実装例
これは非常に基本的な例です。繰り返しますが、それを改善する方法はたくさんありますが、それは特に質問とは関係ありません。ここでは、DBをビジネスロジック(メールの送信)から除外しました。最初のステップが大きすぎる場合は、IDatabase
インスタンスをWelcomeMailSender
に渡して、そこから移動できます。
interface IDatabase
{
MailData GetMailDataForUser(int userId);
}
interface IDatabaseSelector
{
IDatabase GetFromUrl(Url url);
}
class SendWelcomeMailRequestHandler
{
private readonly IDatabaseSelector dbSelector; // inject via constructor
private readonly WelcomeMailSender sender; // inject via constructor
ResponseData HandleRequest(RequestData req)
{
var db = dbSelector.GetFromUrl(req.RequestUrl);
var data = db.GetMailDataForUser(req.UserId);
var success = sender.SendWelcomeMail(data);
return new ResponseData() { Success = success };
}
}
class WelcomeMailSender
{
bool SendWelcomeMail(MailData data)
{
// do all the mail-sending stuff
}
}
テナンシーは、認証と承認に結び付ける必要があります。 URLは安全ではありません。
接続文字列ファクトリ(SQLHelper)があるのが好きです。依存性注入を介してそれを注入することができます。これにより、コードの重複やコードの重複によるミスを回避できます。これをさらに進めるために、一部のDIフレームワークでは、リクエストごと(=ユーザーごと)のインジェクションが許可されています。 AddScoped here に関する説明を参照してください。