【问题标题】:Multitenant system with different customer requirement asp.net core [closed]具有不同客户要求的多租户系统asp.net核心[关闭]
【发布时间】:2022-01-20 08:59:17
【问题描述】:

我目前正在开发一个多租户系统,并且我已经实现了混合流,它允许每个客户使用单个数据库和基于请求的共享数据库,

但是,某些客户想要一些定制,因为他们的流程略有不同,但我只有一个跨系统的用户流。

如何在不影响系统上其他客户的情况下为客户实施此自定义?

请看我目前的架构

编辑:定制主要与用户旅程相关,例如 一个工资单系统,客户 A 想将工资单发送给 MD 批准,而客户 B 想在 MD 之前让会计批准。

【问题讨论】:

  • 这在很大程度上取决于定制的类型。如果只是基本逻辑,比如重定向到哪些页面,那么很容易将其存储在针对每个客户的共享数据库中。如果您开始谈论视图更改或 js/css 更改,它可能会变得相当复杂,您可能需要考虑拥有一个骨架站点,该站点会在需要定制时为个别客户更新并复制,否则每次您进行更改时一个新客户,您可能很容易破坏另一个客户的旅程。
  • 感谢您的意见,我已编辑问题以提供更好的上下文。
  • 我们不知道您的“工资”、“批准”和“角色”(MD、会计师……)是什么样的,因此我们无法对此发表评论。我将其称为“工作流程”而不是“用户旅程”,您可以将工作流程配置完美地存储在数据库表中,而无需为每个客户定制架构。

标签: c# asp.net-core .net-core multi-tenant


【解决方案1】:

如果我们谈论的是数据库。

我要做的是为他们的自定义添加一个 json 类型的对象

比如添加一个 custom_fields 列

Customer Table
//your properties
custom_fields : nvarchar(max)

带有样本值

custom_fields
NULL //for customers without custom_fields
{"CustomFieldA" : "ValueForA", "CustomFieldB" : "ValueForB"} \\for customers with specific request

然后在 c# 端,将其反序列化为动态对象或确定的类。

//if not determined
var customField = JsonSerializer.Deserialize<ExpandoObject>(jsonString);

然后访问值


foreach (var key in customField.Select(x => x.Key))
{
    //to get the custom property name
    var propertyName = key.ToString();
    //to get the value
    var value = customField.First(x => x.Key == key).Value;
}

【讨论】:

  • 这展示了“内部平台效应”或“database-in-database”,这意味着您通过引入更多问题解决了一个问题。您的 UI 或 API 如何知道如何呈现、验证和编辑这些字段?它如何知道哪些字段存在于哪些类型上?您将如何使用自定义字段 Y 查询客户 X 的所有记录,即使他们的旧记录可能设置或不设置该字段?等等。这是可维护性的噩梦。
  • 您宁愿修改每个客户的架构?这也不是很好。
  • @David 这个答案演示了实体-属性-值 (EAV) 模型的众多解决方案之一,特别是通过将 JSON 存储在 nvarchar(MAX) 列中。 如果 OP 正在寻找的“自定义”归结为 EAV(我们不知道),那么以这种方式存储 JSON 是我最不喜欢的方式之一,即使 JSON 函数的兴起也是如此近年来在 RDBMS 中的应用。答案对 JSON 的任意解析过于深入,没有解释任何考虑因素或缺点,也没有解决我在之前的评论中提到的任何其他问题。
  • @David 简而言之,我对这个答案的问题是它直接说“使用 JSON”而不知道 OP 想要什么“定制”,并且它没有正确解释哪些问题过多这个解决方案可以而且将会产生。也许答案是 JSON,也许它只是一个 EAV 表(一个描述可能属性的 EAV 类型表),也许只是一两个可为空的列。
  • 感谢大家的参与,非常感谢,我已经编辑了问题以提供更好的上下文。
【解决方案2】:

从高层次的角度来看,在基于相同架构运行的多租户解决方案中,我们可以通过对所有租户(而不仅仅是请求更改的租户)严格应用所有架构更改来解决很多问题。

如果可以避免,甚至不要考虑动态架构,而是对产品进行改进,以便所有租户都能享受到好处,并回击那些太难或与您的核心产品不相符的请求。

可以拒绝 (或索要淫秽费用以使其值得付出努力)

诀窍是以向后兼容的方式实现任何更改,如果类获得新属性但您不希望它们自动对所有租户可用,则使用继承来扩展类,如果您引入了新关系,则使它们可选的。如果您已将表示层与数据模型充分分离,那么保留不使用新属性的先前视图并有效地为需要新属性的任何视图蒙皮应该没有任何问题。

如果您的领域模型可以被抽象为有效的文档管理,主要的流程管理和配置方面将不需要了解特定的租户业务模型,只需抽象,在这种情况下 JSON 或 XML 序列化技术可以是使用as described in this response

这方面的一个很好的例子是 3rd 方物流或经纪应用程序中的 ConsignmentsManifestsPurchase Orders,领域模型仅受约束通过一组通用标识符和一些通用描述性字段松散地跟踪寄售和行项目,但原始内容可以序列化并存储到数据库中的单个字段中,因为大多数数据和处理操作不需要了解具体内容或其结构。

这在 3PL 中经常出现,因为使用不同数据平台和软件的不同运营商在发送端和接收端都与客户端进行交互,多个实体之间可能涉及到,但这些实体没有任何 数据的所有权或在飞行过程中修改数据的权利。

数据的生命周期涉及摄取、多阶段处理、通知和某种形式的出口。在摄取时,我们应用特定的转换来解释标准模式需要从数据中知道的内容,但对于其他所有内容,通常可以接受以原生形式查看序列化内容,这意味着每个自定义数据类型会有对应的转换到标准模型和可以呈现原生表单的视图。

如果唯一的变化是数据处理的编排,那么除了流程步骤的配置和一些状态管理之外,实际上不需要太多的架构变化来实现。

想到的可能的编排解决方案是 Azure Functions、Azure Durable Functions、逻辑应用或其他工作流引擎。即使您已经拥有可以执行各种任务和操作的核心 API 或代码库,您仍然可以使用工作流技术根据配置的触发器执行这些端点。

您还可以研究一种更微服务风格的架构来处理数据、网络挂钩或域事件从模式的角度来看可能会有所帮助,请阅读Domain Events vs. Integration Events in Domain-Driven Design and microservices architectures

【讨论】:

    猜你喜欢
    • 2021-03-11
    • 1970-01-01
    • 1970-01-01
    • 2013-09-06
    • 1970-01-01
    • 2016-07-21
    • 1970-01-01
    • 1970-01-01
    • 2014-02-10
    相关资源
    最近更新 更多