用户帐户数据库是实体-属性-值 (EAV) 模式的完美候选者!
它通常足够小(无需担心一个 EAV 的弱点,即性能/缩放),并且用户帐户配置通常是包装和规则永无止境的变化的主题。替代方案
可以是对象数据库,由此
下面描述的模型的对象实例“休眠”到存储。后一种方法的缺点是可能更难以将帐户数据库作为一个整体来使用。当然,一个糟糕的选择将是一个真正的关系模式(如问题中所暗示的那样),其记录更不直接暴露于应用程序级别。
简而言之 EAV
数据是“垂直”存储的,而不是“水平”存储的。在问题示例中,对于客户 ID 1,我们在值表中有 4 条记录,分别用于描述、montly_price、max_users 和 max_customers。当我们需要更多属性(例如,一个旨在提供对高级/高级功能的访问的属性)时,其中一些可能具有多个值,我们只需在属性表中定义它们,并在同一个表中添加值记录。
更详细地说:
数据库结构通常由 3 个表组成:
- tblEntity(在此称为 TblAccount),每个实体(帐户)有一个恰好一行。它包含给定帐户的通用信息:AccountId、Name、可能是 Login/pawsord、可能是到期日期,但通常很少。
- tblAttribute 它保存了一个属性的定义:一个AttributeId,一个Name,一个Type
- tblValues 如前所述,每个属性值都有一个记录分配给特定实体(帐户)
与程序逻辑相结合:
通常我们引入一个对象模型,其中一个帐户只不过是几个“标题”属性(Id,名称),并且各种属性存储在哈希(字典)的键值对中。这些对象或一些实用方法提供了一个层,通过该层处理有关帐户的所有查询和操作,例如:
- bool CanUserAccess(FeatureName); // 评估与 FeatureName 关联的规则(通常是对该帐户的存在和/或属性值的一系列测试),如果帐户可以先验访问所述功能,则返回 true(另一层可能会在给定时间阻止此类访问, 因为并发用户数或其他...)
- int TakeLicenseFor(FeatureName);
- ETC。
这通常涵盖了它。许多细节仍有待深入,但上面提出的暂定架构提供了拼图各个元素的整体松散耦合:
- 数据库可以立即接受新的属性,无需更改 SQL 的架构,仅更改应用程序级架构(主要是属性表)
- 数据库用于在登录时加载代表帐户的对象
- 应用程序使用符号名称(字符串)来指定应用程序的功能(如果您愿意,可以分段)并通过业务规则层查询底层帐户的此类功能的可用性(允许帐户属性和特征:在不改变账户的情况下,可以添加或删除一些特征,如果它们可以映射到一个或多个账户属性。
有了2层绝缘,即使数据库的物理布局发生变化,或者某些业务规则来来去去,应用程序也不会受到困扰。应用程序中当然有一个关联:对这些“功能字符串”的引用,但这些引用通常可以放在脚本中的较高位置(早期),从而允许在应用程序的页面/功能之间进行非常可见且易于更改的映射以及描述的账户管理逻辑。