如何创建数据库不匹配的多租户应用程序
How to create a multi-tenant application where the databases dont match
我正在尝试想出一种方法来创建一个应用程序作为多个客户端的 CMS,每个客户端都有自己的数据库,但据我所知我们 return 的大部分内容前端应该是一样的。
到目前为止,我已经决定使用 Reactjs 作为前端,因为它可以让我在需要的地方创造灵活性,同时还能够创建可重用的组件。
我遇到的问题是想出一种后端方法,因为没有一个客户端共享一个精确的模式匹配,所以在搭建 dbcontexts 时,每个客户端都必须创建新的才能工作。
我最初的想法只是 运行 并在需要时根据 client/database 创建一个 dbcontext,并提出一些路由逻辑来帮助我找到上下文每个请求的基础。然而,这可能会让我在每次获得新客户端时都编写一个新的数据层,这将使该应用程序变得庞大而笨拙,并且需要一些凌乱的控制器或服务来管理它。
我的另一个想法是为每个客户端创建一个 API,并从前端创建一个可配置的调用以获得正确的 API,但我不确定这有多安全而且它不看起来不太明智,但会让我的后端留在小的可维护块中。
对这些方法或其他可能更好的方法有什么建议吗?
我知道这匹马已经狂奔(至少现在是这样),但避免使用不同的模式会让你免于痛苦。下一个最好的办法是为 80% 的解决方案使用一个通用模式,并仔细管理您处理可变部分的方式。
就您的实际问题而言,孤立地...听起来 依赖注入 在这种情况下可能是您的朋友。
总体架构看起来像这样。发生了一些不同的事情,让我依次解释一下。
- 每个客户端都有自己的数据库和相应的数据访问代码;数据访问代码的每个实例都实现相同的接口:
IClientDataProvider
.
- 数据访问代码程序以北的所有代码都针对
IClientDataProvider
。我假设您有单独的业务逻辑和 UI 层 - 但这与您的问题无关。
- DI(依赖注入)sub-system 决定在 运行 加载哪个实际数据访问代码(以及哪个数据库)。
这里的假设是,无论模式如何,这些差异都可以由数据访问代码处理,并适用于单个一致的接口。
接口和数据提供程序设计 - 该图描绘了最基本的解决方案,但它很容易扩展。例如,假设您有两个不同的概念:
- 客户资料数据(姓名、地址、喜欢的颜色等)。
- 客户端传输网络数据(不同客户端不同)。
创建两种不同类型的接口,一种用于通用内容(配置文件数据),另一种用于处理不同内容(传输网络数据)。普通的只需要你自己开发一个实现,所以你只需要在需要的地方为接口写重复的代码。
专业提示 - 如果您还没有听说过它,一定要熟悉 Interface Segregation Principle (ISP),以便在您开始设计界面时使用。
你可能会问,如果通用的东西只有一种实现,你需要使用接口吗?你不需要,你可以直接使用 objects - 但是:
- 全面使用接口意味着体系结构和代码更加一致,随着时间的推移更易于维护。
- 具有接口 in-place 使解决方案更容易在长 运行 中更改。例如。假设您想更改数据提供程序技术?或者客户的需求要求你在以前相同的地方有所不同。
DTOs - 要获取周围的数据,请创建一套完全愚蠢的 objects 来保存信息;系统使用这些来移动数据。例如:
PizzaInfo info = IClientDataProvider.GetClientPizzaData(guid clientId)
IClientDataProvider.Save(KittenInfo kitten);
最简单的解决方案是制作一组所有层都使用的那些。如果您愿意,您显然可以选择对其进行更改。
在运行时间选择哪个数据提供者——我想取决于用户如何访问租户。例如。有一些东西(可能在 DI sub-system 中)传递给 URL,并调用加载哪个提供者。
hogwarts.holdencms.com = CMS.DataAccess.Clients.HogwartsDataProvider
starwars.holdencms.com = CMS.DataAccess.Clients.StarWarsDataProvider
thunderbirds.holdencms.com = CMS.DataAccess.Clients.ThunderBirdsDataProvider
如果客户端之间的差异不能包含在一组接口中怎么办? 该答案的两个部分:在 high-level re-use相同的方法/原则,但使用它来加载不同的业务逻辑 类 and/or 不同的 UI 小部件。第二部分,这不是我有 hands-on 经验的领域,但我很确定大多数体面的 UI 框架都会有一些东西来帮助你做那种事情。
我正在尝试想出一种方法来创建一个应用程序作为多个客户端的 CMS,每个客户端都有自己的数据库,但据我所知我们 return 的大部分内容前端应该是一样的。
到目前为止,我已经决定使用 Reactjs 作为前端,因为它可以让我在需要的地方创造灵活性,同时还能够创建可重用的组件。
我遇到的问题是想出一种后端方法,因为没有一个客户端共享一个精确的模式匹配,所以在搭建 dbcontexts 时,每个客户端都必须创建新的才能工作。
我最初的想法只是 运行 并在需要时根据 client/database 创建一个 dbcontext,并提出一些路由逻辑来帮助我找到上下文每个请求的基础。然而,这可能会让我在每次获得新客户端时都编写一个新的数据层,这将使该应用程序变得庞大而笨拙,并且需要一些凌乱的控制器或服务来管理它。
我的另一个想法是为每个客户端创建一个 API,并从前端创建一个可配置的调用以获得正确的 API,但我不确定这有多安全而且它不看起来不太明智,但会让我的后端留在小的可维护块中。
对这些方法或其他可能更好的方法有什么建议吗?
我知道这匹马已经狂奔(至少现在是这样),但避免使用不同的模式会让你免于痛苦。下一个最好的办法是为 80% 的解决方案使用一个通用模式,并仔细管理您处理可变部分的方式。
就您的实际问题而言,孤立地...听起来 依赖注入 在这种情况下可能是您的朋友。
总体架构看起来像这样。发生了一些不同的事情,让我依次解释一下。
- 每个客户端都有自己的数据库和相应的数据访问代码;数据访问代码的每个实例都实现相同的接口:
IClientDataProvider
. - 数据访问代码程序以北的所有代码都针对
IClientDataProvider
。我假设您有单独的业务逻辑和 UI 层 - 但这与您的问题无关。 - DI(依赖注入)sub-system 决定在 运行 加载哪个实际数据访问代码(以及哪个数据库)。
这里的假设是,无论模式如何,这些差异都可以由数据访问代码处理,并适用于单个一致的接口。
接口和数据提供程序设计 - 该图描绘了最基本的解决方案,但它很容易扩展。例如,假设您有两个不同的概念:
- 客户资料数据(姓名、地址、喜欢的颜色等)。
- 客户端传输网络数据(不同客户端不同)。
创建两种不同类型的接口,一种用于通用内容(配置文件数据),另一种用于处理不同内容(传输网络数据)。普通的只需要你自己开发一个实现,所以你只需要在需要的地方为接口写重复的代码。
专业提示 - 如果您还没有听说过它,一定要熟悉 Interface Segregation Principle (ISP),以便在您开始设计界面时使用。
你可能会问,如果通用的东西只有一种实现,你需要使用接口吗?你不需要,你可以直接使用 objects - 但是:
- 全面使用接口意味着体系结构和代码更加一致,随着时间的推移更易于维护。
- 具有接口 in-place 使解决方案更容易在长 运行 中更改。例如。假设您想更改数据提供程序技术?或者客户的需求要求你在以前相同的地方有所不同。
DTOs - 要获取周围的数据,请创建一套完全愚蠢的 objects 来保存信息;系统使用这些来移动数据。例如:
PizzaInfo info = IClientDataProvider.GetClientPizzaData(guid clientId)
IClientDataProvider.Save(KittenInfo kitten);
最简单的解决方案是制作一组所有层都使用的那些。如果您愿意,您显然可以选择对其进行更改。
在运行时间选择哪个数据提供者——我想取决于用户如何访问租户。例如。有一些东西(可能在 DI sub-system 中)传递给 URL,并调用加载哪个提供者。
hogwarts.holdencms.com = CMS.DataAccess.Clients.HogwartsDataProvider
starwars.holdencms.com = CMS.DataAccess.Clients.StarWarsDataProvider
thunderbirds.holdencms.com = CMS.DataAccess.Clients.ThunderBirdsDataProvider
如果客户端之间的差异不能包含在一组接口中怎么办? 该答案的两个部分:在 high-level re-use相同的方法/原则,但使用它来加载不同的业务逻辑 类 and/or 不同的 UI 小部件。第二部分,这不是我有 hands-on 经验的领域,但我很确定大多数体面的 UI 框架都会有一些东西来帮助你做那种事情。