Skip to content

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_2025 alongside GRI_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


Change Log

Version Date Author Changes
1.0 2026-01-03 Senior Product Architect Initial framework metadata structure