Standards & Framework Metadata
Status: Final Version: 1.0 Last Updated: 2026-01-03
Purpose
Define the database schema and hierarchical structure for storing ESG reporting frameworks, standards, disclosures, and their versioning. This enables the platform to support multiple frameworks (GRI, ISSB, SASB, CSRD) and track framework evolution over time.
Scope
In Scope: - Database schema for framework hierarchy (framework → standard → disclosure → metric) - Version management for framework updates (GRI 2021 → GRI 2025) - Cross-framework mapping (metric appears in both GRI and TCFD) - Custom KPI traceability to standard disclosures
Out of Scope: - Complete GRI catalog data (see Metric Catalog) - Materiality assessment logic - Framework selection wizard (vNext)
Key Decisions
| Decision | Rationale |
|---|---|
| 4-tier hierarchy (framework → standard → disclosure → metric) | Aligns with GRI structure; flexible for other frameworks |
| Version as separate field (not separate records) | Simplifies queries; historical versions archived separately |
| Cross-framework mapping via junction table | Enables multi-framework reporting without duplication |
1. Framework Hierarchy
erDiagram
FRAMEWORKS ||--o{ GRI_STANDARDS : contains
GRI_STANDARDS ||--o{ GRI_DISCLOSURES : contains
GRI_DISCLOSURES ||--o{ METRIC_DEFINITIONS : defines
FRAMEWORKS {
id bigint PK
code string "GRI_2021, ISSB_S1, TCFD"
name string
version string
effective_date date
status enum
}
GRI_STANDARDS {
id bigint PK
framework_id bigint FK
code string "GRI_302, GRI_401"
name string
category enum
}
GRI_DISCLOSURES {
id bigint PK
standard_id bigint FK
code string "302-1, 401-1"
requirement text
guidance_url string
}
METRIC_DEFINITIONS {
id bigint PK
disclosure_id bigint FK
metric_id string
name string
}
2. Database Schemas
Frameworks Table
CREATE TABLE frameworks (
id BIGSERIAL PRIMARY KEY,
code VARCHAR(50) NOT NULL UNIQUE, -- e.g., 'GRI_2021', 'ISSB_S1_2024'
name VARCHAR(255) NOT NULL, -- e.g., 'GRI Standards 2021'
description TEXT,
version VARCHAR(50), -- e.g., '2021', 'S1-2024'
issuing_organization VARCHAR(255), -- e.g., 'Global Reporting Initiative'
effective_date DATE, -- When framework came into effect
sunset_date DATE, -- When framework deprecated (if applicable)
status VARCHAR(50) DEFAULT 'active', -- ENUM: 'draft', 'active', 'deprecated'
framework_url VARCHAR(500), -- Official documentation URL
metadata JSONB, -- Extensible
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_frameworks_code ON frameworks(code);
CREATE INDEX idx_frameworks_status ON frameworks(status);
Example Records:
INSERT INTO frameworks VALUES
(1, 'GRI_2021', 'GRI Standards 2021', 'Global Reporting Initiative Standards', '2021', 'Global Reporting Initiative', '2021-01-01', NULL, 'active', 'https://www.globalreporting.org/standards/', '{}'),
(2, 'ISSB_S1_2024', 'IFRS S1 - General Requirements', 'ISSB Sustainability Disclosure Standard S1', 'S1-2024', 'IFRS Foundation', '2024-01-01', NULL, 'active', 'https://www.ifrs.org/issued-standards/issb-standards/', '{}');
GRI Standards Table
CREATE TABLE gri_standards (
id BIGSERIAL PRIMARY KEY,
framework_id BIGINT NOT NULL REFERENCES frameworks(id),
code VARCHAR(50) NOT NULL, -- e.g., 'GRI_302', 'GRI_401'
number INT, -- e.g., 302, 401 (for sorting)
name VARCHAR(255) NOT NULL, -- e.g., 'Energy', 'Employment'
category VARCHAR(50), -- ENUM: 'universal', 'environmental', 'social', 'governance'
description TEXT,
standard_url VARCHAR(500),
metadata JSONB,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
UNIQUE (framework_id, code)
);
CREATE INDEX idx_gri_standards_framework ON gri_standards(framework_id);
CREATE INDEX idx_gri_standards_category ON gri_standards(category);
Example Records:
INSERT INTO gri_standards VALUES
(10, 1, 'GRI_302', 302, 'Energy', 'environmental', 'Energy consumption, production, and intensity', 'https://www.globalreporting.org/standards/gri-standards-download-center/gri-302-energy-2016/', '{}'),
(25, 1, 'GRI_401', 401, 'Employment', 'social', 'New employee hires and employee turnover', 'https://www.globalreporting.org/standards/gri-standards-download-center/gri-401-employment-2016/', '{}');
GRI Disclosures Table
CREATE TABLE gri_disclosures (
id BIGSERIAL PRIMARY KEY,
standard_id BIGINT NOT NULL REFERENCES gri_standards(id),
code VARCHAR(50) NOT NULL, -- e.g., '302-1', '401-1'
name VARCHAR(255) NOT NULL, -- e.g., 'Energy consumption within the organization'
disclosure_type VARCHAR(50), -- ENUM: 'management_approach', 'quantitative', 'qualitative'
requirement TEXT NOT NULL, -- Full disclosure requirement text
guidance TEXT, -- Implementation guidance
disclosure_url VARCHAR(500),
is_mandatory BOOLEAN DEFAULT false, -- Required for GRI compliance
metadata JSONB,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
UNIQUE (standard_id, code)
);
CREATE INDEX idx_gri_disclosures_standard ON gri_disclosures(standard_id);
CREATE INDEX idx_gri_disclosures_code ON gri_disclosures(code);
Example Records:
INSERT INTO gri_disclosures VALUES
(50, 10, '302-1', 'Energy consumption within the organization', 'quantitative',
'Report total fuel consumption, electricity consumption, heating consumption, cooling consumption, steam consumption, and total energy consumption within the organization.',
'Break down by renewable and non-renewable sources. Convert all energy to joules or multiples.',
'https://www.globalreporting.org/standards/gri-standards-download-center/gri-302-energy-2016/', true, '{}');
Cross-Framework Mapping Table
CREATE TABLE framework_mappings (
id BIGSERIAL PRIMARY KEY,
source_disclosure_id BIGINT NOT NULL REFERENCES gri_disclosures(id),
target_disclosure_id BIGINT NOT NULL REFERENCES gri_disclosures(id),
mapping_type VARCHAR(50), -- ENUM: 'equivalent', 'subset', 'superset', 'related'
mapping_notes TEXT,
confidence_level VARCHAR(50), -- ENUM: 'exact', 'high', 'medium', 'low'
created_at TIMESTAMP DEFAULT NOW(),
UNIQUE (source_disclosure_id, target_disclosure_id)
);
CREATE INDEX idx_framework_mappings_source ON framework_mappings(source_disclosure_id);
CREATE INDEX idx_framework_mappings_target ON framework_mappings(target_disclosure_id);
Example Use Case: Map GRI 305-1 (Scope 1 GHG) to TCFD and ISSB S2 climate disclosures.
3. Laravel Models
Framework Model
class Framework extends Model
{
protected $fillable = ['code', 'name', 'description', 'version', 'issuing_organization',
'effective_date', 'sunset_date', 'status', 'framework_url', 'metadata'];
protected $casts = [
'effective_date' => 'date',
'sunset_date' => 'date',
'metadata' => 'array',
];
public function standards() {
return $this->hasMany(GriStandard::class);
}
public function scopeActive($query) {
return $query->where('status', 'active');
}
public function isActive() {
return $this->status === 'active' &&
($this->sunset_date === null || $this->sunset_date->isFuture());
}
}
GriStandard Model
class GriStandard extends Model
{
protected $fillable = ['framework_id', 'code', 'number', 'name', 'category',
'description', 'standard_url', 'metadata'];
protected $casts = ['metadata' => 'array'];
public function framework() {
return $this->belongsTo(Framework::class);
}
public function disclosures() {
return $this->hasMany(GriDisclosure::class, 'standard_id');
}
public function scopeCategory($query, $category) {
return $query->where('category', $category);
}
}
GriDisclosure Model
class GriDisclosure extends Model
{
protected $fillable = ['standard_id', 'code', 'name', 'disclosure_type', 'requirement',
'guidance', 'disclosure_url', 'is_mandatory', 'metadata'];
protected $casts = [
'is_mandatory' => 'boolean',
'metadata' => 'array',
];
public function standard() {
return $this->belongsTo(GriStandard::class, 'standard_id');
}
public function metrics() {
return $this->hasMany(MetricDefinition::class, 'disclosure_id');
}
// Cross-framework mappings
public function mappedTo() {
return $this->belongsToMany(GriDisclosure::class, 'framework_mappings',
'source_disclosure_id', 'target_disclosure_id')
->withPivot('mapping_type', 'confidence_level');
}
}
4. Version Management
Approach: Framework Versioning
- New Major Version: Create new framework record (e.g.,
GRI_2025alongsideGRI_2021) - Metric Definitions: Link to specific framework version via
framework_id - Migration Path: Admin maps old metrics to new framework disclosures
- Historical Data: Always tied to framework version in effect during reporting period
Version Migration Workflow
class FrameworkMigrationService
{
public function migrateMetrics(Framework $oldFramework, Framework $newFramework)
{
// Get all metrics tied to old framework
$oldMetrics = MetricDefinition::where('framework_id', $oldFramework->id)->get();
foreach ($oldMetrics as $metric) {
// Check if disclosure exists in new framework
$newDisclosure = $this->findMappedDisclosure($metric->disclosure_id, $newFramework);
if ($newDisclosure) {
// Create new metric version
$newMetric = $metric->replicate();
$newMetric->framework_id = $newFramework->id;
$newMetric->disclosure_id = $newDisclosure->id;
$newMetric->version = $metric->version + 1;
$newMetric->save();
// Mark old metric as deprecated
$metric->update([
'deprecated_at' => now(),
'replaced_by_metric_id' => $newMetric->metric_id
]);
}
}
}
}
Acceptance Criteria
Done When:
- [ ] All 4 tables created (frameworks, gri_standards, gri_disclosures, framework_mappings)
- [ ] Laravel models with relationships implemented
- [ ] GRI 2021 framework seeded with Universal + Topic Standards
- [ ] Version migration service can upgrade framework versions
- [ ] Cross-framework mapping table supports GRI ↔ ISSB/TCFD mappings (vNext)
- [ ] API endpoints expose framework hierarchy (see Admin API)
Cross-References
- Related: Metric Catalog - Metric definitions
- Related: GRI/ESG Scope v1 - Framework coverage
Change Log
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.0 | 2026-01-03 | Senior Product Architect | Initial framework metadata structure |