Data Models
Overview
Here we explore data models used by Flagbase on a technical level. If you wish to get a higher-level overview, please read the guides in the user docs. Below is an ER diagram, illustrating the relationships between different resources.
If you can't see the diagram above, your browser may not support iframes. You can use this link to view original diagram.
Here's a brief explanation of each table and their relationships:
- access: This table stores access keys for different services or users. Each record contains a unique key and an encrypted secret, along with an expiration timestamp and a description.
- workspace: The workspace table represents a workspace that groups related projects together. Each workspace has a unique key, name, and description.
- project: Projects belong to a workspace and group related feature flags. Each project record is associated with a workspace through the workspace_id foreign key.
- environment: Environments represent different deployment environments, like development, staging, or production, and belong to a project. The project_id foreign key links an environment to its project.
- flag: Feature flags are stored in this table, with each flag belonging to a project. The project_id foreign key associates a flag with its project.
- variation: Variations are the different versions of a feature flag. Each variation is linked to a flag through the flag_id foreign key.
- segment: Segments define groups of users based on traits or other conditions. Segments are associated with a project through the project_id foreign key.
- sdk_key: SDK keys are used for authenticating clients and servers when interacting with the feature flag management system. Each key belongs to an environment, and the environment_id foreign key establishes this relationship.
- segment_rule: Segment rules define the conditions for including users in a segment. These rules can reference both environments and segments through the environment_id and segment_id foreign keys, respectively.
- targeting: The targeting table defines which flag variation should be served to users in a specific environment. The flag_id and environment_id foreign keys link a targeting record to a flag and an environment, respectively.
- targeting_rule: Targeting rules determine the conditions for serving a specific flag variation. A targeting rule can reference an identity, segment, and targeting through the identity_id, segment_id, and targeting_id foreign keys, respectively.
- targeting_fallthrough_variation: This table defines the default variation served when no targeting rules match. It associates a variation with a targeting through the variation_id and targeting_id foreign keys.
- targeting_rule_variation: This table maps variations to targeting rules, specifying the variation that should be served if the rule's conditions are met. The variation_id and targeting_rule_id foreign keys link a targeting rule variation to a variation and targeting rule, respectively.
In summary, this schema represents a feature flag management system that includes workspaces, projects, environments, feature flags, segments, identities, and various rules for targeting and evaluating flag variations.
Resources
The diagram below illustrates the Flagbase's resource hierarchy. To simplify the diagram, identity and targeting have not been included in this diagram.
If you can't see the diagram above, your browser may not support iframes. You can use this link to view original diagram.
We will go into more depth about the purpose of each resource and their relationship with other resources, using examples to help understand key concepts. If you wish to get a higher-level overview of these resources, please read the guides in the user docs.
Instance
An "instance" refers to a Flagbase core installation, running on a single VPS or clustered in a data center accessible via a load balancer. Flagbase uses postgres as the main datastore used to store resources. Everything in a single postgres database represents a single Flagbase instance. You can have multiple hosts running Flagbase core, but if they all share the same database, we refer to that as a "single instance".
Access
Access is a key/secret pair used to restrict operations on a particular resource via the policy enforcer. You can create and attach access to workspace, project and environment resources.
Flagbase uses casbin to authorize operations on resources, based on the Flagbase policy model. Upon creating an access key/secret pair, the secret is encrypted before being stored in the datastore. Access can expire after a certain time. The expiry time (i.e. expires_at
) is stored as a unix timestamp. You can add additional meta-data to an access resource including a name
, description
and tags
.
Workspace
A workspace is the top-level resource which is used to group projects. A workspace can only be created by a root user, which you only have access to if you own a particular instance. You can have multiple workspaces per instance.
Every workspace has a unique key
, per instance. You can add additional meta-data to an workspace resource including a name
, description
and tags
.
Project
A project represents a collection of flags and segments. In the real-world, a project can be mapped to a particular codebase for a service or package.
Every project has a unique key
, per workspace. Every project has to belong to a particular workspace. You can add additional meta-data to an project resource including a name
, description
and tags
.
Environment
A project can have multiple environments (e.g. staging, production) which correspond to different targeting states. This means if you modify a flag's targeting or a segment's rules in one environment, your changes will be scoped to that particular environment. This allows you to have different targeting rules for flags and segments in each environment.
Every environment has a unique key
, per project. A project must contain at least one environment. When you create a project, it also creates two environments for you (i.e. staging
and production
). You can add additional meta-data to an environment resource including a name
, description
and tags
.
Flag
A flag (aka feature flag, toggle, switch etc) represents a particular point in code which when evaluated determines the state of a feature. Flags hold different variations (i.e. on/off, true/false, A/B/C), which are only revealed upon evaluation.
Every flag has a unique key
, per project. You can add additional meta-data to an flag resource including a name
, description
and tags
. A flag must have two or more variations. When you create a flag, two variations are automatically created and assigned to that particular flag (i.e. on
, off
). You can add additional meta-data to an variation resource including a name
, description
and tags
.
Identity
An identity (aka user) refers to a flag observer/consumer who requests to evaluate a flag. An identity consists of a set of traits. These traits are used as the context which is used during evaluation.
Every identity has a unique key, per project environment. This key is automatically generated using trait information provided by the clients. For anonymous identities, a random hash will be used using meta-data from the context provided by clients.
Segment
A segment is used to group users based on their characteristics (i.e. traits). Segments are made up of one or more rules that is used to filter out a portion of your users. Segments provide a method to capture common targeting rules - allowing you to reuse these rules across different flags.
Every segment has a unique key, per project. You can add additional meta-data to an segment resource including a name
, description
and tags
. A segment consists rules (i.e. segment_rules
). These rules also contain a key
which is unique the segment which is used to identify that particular rule. Segment rules contain an operator (i.e. equal
, greater_than
, greater_than_or_equal
, contain
, regex
) which combined with the negate
field will allow you to represent any condition on a particular user trait. You can add additional meta-data to an segment rules including a name
, description
and tags
.
Targeting
Targeting is a spec used to determine a flag's variation. This spec consists of a set of rules which when evaluated using a user's context (i.e. traits), determines which variation of a flag that particular user will receive. So essentially you could say targeting rules are conditions mapped to a variation. A flag's targeting rules are scoped to a particular project environment.
Targeting for a particular flag consists of a fallthrough variation (i.e. the default variation users will receive when no targeting rules are satisfied or when targeting is not enabled). Targeting rules contain a key
which is unique the targeting object which is used to identify that particular rule. Targeting rules contain an operator (similar to the one used by segment rules ~ i.e. equal
, greater_than
, greater_than_or_equal
, contain
, regex
) which combined with the negate
field will allow you to represent any condition on a particular user trait. You can add additional meta-data to an targeting rules including a name
, description
and tags
. Targeting rules can also be weighted (0 ... 100), allowing you to randomly rollout to certain percentage of users who match a particular rule. The fallthrough variation can also be weighted.
Design Strategy
It is important to balance the extend to which data models are normalized to optimise for both storage and query performance. Flagbase's data models are in 2NF, ensuring key entities are split into different tables. However, entity-specific meta data (i.e. name
, description
, tags
etc) have not been split into a different table and belong to the original resource table. This is to avoid additional joins on queries and mutations.
Triggers and procedures are used to enforce validation of resource characteristics. Data validation is done in the database layer, to ensure additional safety, which will help catch errors that are not handled properly in the application layer. However, there's a performance cost of adding these validators in the database layer, so high-traffic resource paths should be handled in the application layer.