we are committed to delivering innovative solutions that drive growth and add value to our clients. With a team of experienced professionals and a passion for excellence.

Follow us

Building a Metadata-Driven Sharing Engine in Salesforce Experience Cloud – Scalable Access Management Architecture

Building a Metadata-Driven Sharing Engine in Salesforce Experience Cloud – Scalable Access Management Architecture

Images
Authored by
Nitish Jadhav
Date Released
June 30, 2026
Comments
No Comments

INTRODUCTION

Record sharing in Salesforce is deceptively complex. What seems simple—”give user access to these records”—becomes intricate when you have hierarchical account structures, multiple warehouse locations, dynamic relationships, and evolving business requirements.

Building a point-solution (code a specific sharing model for Cases, another for WorkOrders, another for Contracts) works temporarily but creates technical debt. Each new object requires new code, new testing, new maintenance.

The elegant solution is a metadata-driven sharing engine—a generic framework that uses custom metadata to configure what gets shared with whom, and applies that configuration across any object without code changes.

This post walks through a real-world implementation of such an engine for Experience Cloud, covering the architecture, design patterns, handling complexity, and the extensibility benefits that make this approach worth the upfront investment.


THE BUSINESS PROBLEM

Why Standard Sharing Models Break Down

The Scenario: Multi-Warehouse Access Management

Business Context:

Company Structure:

– Multiple warehouse accounts (East, West, Central)

– Each warehouse handles Cases, WorkOrders, Shipments

– Contact users need visibility across multiple warehouses

– A single customer contact may be associated with multiple warehouses

– Access must be dynamic (change as relationships change)

Example:

Contact: John Smith

  └─ Customer Account 1 (Primary warehouse: East)

     ├─ Cases (should see)

     ├─ WorkOrders (should see)

     └─ Shipments (should see)

  └─ Customer Account 2 (Primary warehouse: West)

     ├─ Cases (should see)

     ├─ WorkOrders (should see)

     └─ Shipments (should see)

Problem: Manually managing this is not scalable

Solution: Automated, metadata-driven sharing

Why This Is Hard

Challenge 1: Complexity

Multiple dimensions of sharing:

– By account hierarchy (parent/child accounts)

– By account location (warehouse)

– By relationship type (contact, partner, vendor)

– By record type (Cases vs WorkOrders vs Shipments)

– By user role (view-only vs edit)

– Cascading (parent account → child accounts)

Managing manually:

– Creates sharing rules for each combination

– Becomes unmaintainable (hundreds of rules)

– Inflexible (can’t change logic without editing rules)

– Error-prone (easy to miss scenarios)

Challenge 2: Scalability

Large account hierarchies (100+ accounts):

– Manual sharing: 10,000+ share records

– Hard to update or revoke

– Performance issues (sharing calculations slow)

– Difficult to audit

Large user base (10,000+ users):

– Each access change = update shares

– Bulk updates risky

– Governor limits hit easily

Challenge 3: Extensibility

Adding new objects to share:

Traditional approach:

  1. Build sharing logic (Apex code)
  2. Create test suite
  3. Deploy to production
  4. Monitor and support
  5. Maintain going forward

Metadata-driven approach:

  1. Create metadata record
  2. Checkbox: “Active”
  3. Done (logic reused)

THE METADATA-DRIVEN APPROACH

Conceptual Foundation

Core Concept

What Is Metadata-Driven?

Instead of hardcoding logic:

if (isCase) { shareLogicForCases(); }

else if (isWorkOrder) { shareLogicForWorkOrders(); }

Use configuration (metadata):

  1. Define what to share (metadata record)
  2. Define who to share with (relationship record)
  3. Generic engine reads both
  4. Applies sharing dynamically

Example:

Metadata Record:

– Object: Case

– Active: true

– Access Level: Read

– Share with: Account owners, contacts

At Runtime:

– Query metadata for “Case”

– Query relationships (contact to account)

– Apply sharing automatically

– No if/else logic needed

How It Works

Simplified Flow:

  1.  Change Detected

   ├─ New contact linked to account

   ├─ Account hierarchy updated

   └─ User provisioned for Experience Cloud

  1. Check Metadata

   ├─ What objects are configured for sharing?

   ├─ Case? WorkOrder? Shipment?

   └─ Which ones are active?

  1. Calculate Access

   ├─ Get account hierarchy

   ├─ Get related records

   ├─ Determine access level

   └─ Generate sharing operations

  1. Apply Sharing

   ├─ Create AccountShare records

   ├─ Create custom object shares

   ├─ Handle unsharing (old relationships)

   └─ Update user provisioning

  1. Monitor

   ├─ Log operations

   ├─ Handle errors

   ├─ Alert on failures

   └─ Track sharing status


ARCHITECTURE BREAKDOWN

Design Components

Component 1: Data Sources (Left Side of Diagram)

External Systems:

APIs / Webhooks:

├─ Customer relationship systems

├─ Partner data sources

├─ Legacy systems

└─ Real-time data feeds

Salesforce Objects:

├─ Accounts (warehouse accounts)

├─ Contacts (customer contacts)

├─ Users (Experience Cloud users)

└─ Relationships (AccountContact__c)

Purpose:
Feed data into the sharing engine about who needs access to what.

 

Component 2: Relationship Objects (WHOLESALES_C)

Custom Relationship Object:

WHOLESALES_C (Relationship Object):

– Represents connection between contact and account

– Fields:

  ├─ Contact__c (lookup)

  ├─ Account (Warehouse Account)

  ├─ Active Flag (boolean)

  ├─ Relationship Type (picklist)

  └─ Custom Fields (domain-specific)

Purpose:

– Define which contacts have access to which accounts

– Act as input to sharing engine

– Drive access decisions

 

Component 3: Metadata Configuration (Center of Diagram)

Custom Metadata Types:

Shareable_Object__mdt (Custom Metadata):

– Defines which objects to share

– Fields:

  ├─ API Name

  ├─ Active Checkbox

  ├─ Access Level (Read, Read/Write)

  ├─ Share Type

  └─ Configuration JSON

Example Records:

  1. Case Sharing Config

   – Object: Case

   – Active: true

  1. WorkOrder Sharing Config

   – Object: WorkOrder

   – Active: true

  1. Shipment Sharing Config

   – Object: Shipment__c

   – Active: false (not yet enabled)

Purpose:

– Configure what to share (without code)

– Enable/disable sharing (checkbox)

– Define access levels

– Extensible for future objects

Sharing Rules Metadata:

Sharing_Rule__mdt (Custom Metadata):

– Defines sharing rules per object

– Fields:

  ├─ Object Name

  ├─ Rule Name

  ├─ Relationship Path (SOQL syntax)

  ├─ Access Level

  └─ Description

Example:

– Object: Case

– Rule: Account Owner Access

– Path: (Contact → Account → Case)

– Access: Read

Purpose:

– Define how sharing decisions are made

– Express relationship hierarchies

– Configure access levels per rule

 

Component 4: Metadata-Driven Sharing Engine (Heart of System)

Read Shareable Objects:

Apex logic:

  1. Query all Shareable_Object__mdt records
  2. Filter where Active = true
  3. Build list of objects to share

Result: [Case, WorkOrder, Shipment]

Expand Account Hierarchy:

Apex logic:

  1. Start with contact’s accounts (from relationship object)
  2. Query parent accounts
  3. Query child accounts
  4. Build complete hierarchy

Example:

Contact linked to: Account East

Hierarchy expansion:

├─ Parent: Warehouse Global

├─ Self: Account East

└─ Children: East Region, East Central

Result: 5 accounts to share

Resolve Partial Users:

Apex logic:

  1. Get contact from relationship
  2. Create or get Experience Cloud user
  3. Handle user provisioning
  4. Return user ID for sharing

Purpose: Map contacts to users for sharing

Build Shareable Records Dynamically:

Apex logic:

  1. For each enabled object:

   – Query records linked to account(s)

   – Use dynamic SOQL

   – Handle relationships

Example for Cases:

SELECT id FROM Case WHERE AccountId IN :accountIds

Result: Case IDs to share with user

Threshold Check (Async Processing):

Apex logic:

  1. Count total sharing operations needed
  2. If threshold (e.g., 1000+):

   – Queue for Queueable processing

   – Avoid governor limits

  1. If small:

   – Process synchronously

   – Real-time sharing

Purpose: Balance speed vs governor limits

Create Sharing Records (Async):

Apex logic via Queueable:

  1. Create AccountShare records
  2. Create custom object shares
  3. Create sharing records dynamically
  4. Handle duplicates (no error)
  5. Batch insert (efficient)

Purpose: Actually grant access in database

 

Component 5: User Provisioning (Right Side)

Create Users:

Experience Cloud User Provisioning:

  1. Get contact from relationship
  2. Create Experience Cloud user
  3. Assign community
  4. Assign roles/permissions
  5. Return user ID

Challenge: Mixed DML

– User creation is separate DML context

– Can’t mix with record sharing

– Solution: Async processing separates contexts

Assign Sharing DML (Async):

Queueable handles:

  1. User creation (if needed)
  2. Sharing DML (separate context)
  3. No Mixed DML issues
  4. Reliable execution

HANDLING COMPLEXITY

Solving Hard Problems

Challenge 1: Account Hierarchy Expansion

The Problem:

Contact linked to Account: East

But we need sharing for:

– East (direct)

– Warehouse Global (parent)

– East Region (child)

– East Central (child)

– East South (child)

 

Without hierarchy expansion: Contact only gets East

With expansion: Contact gets all 5 accounts

Result:

  • One contact → multiple accounts
  • Multiple accounts → multiple shared records
  • All expanded automatically

 

Challenge 2: Duplicate Access Prevention

The Problem:

Contact linked to Account A (direct)

Contact linked to Account B (parent of A)

 

Both need access to A’s Cases

– Direct link would create share

– Parent link would create share

– Result: Duplicate shares

Salesforce allows one share per user per object

Second share creation: Error

Benefits:

  • No errors on duplicates
  • Continues processing
  • Idempotent (safe to retry)

 

Challenge 3: Mixed DML Restrictions

The Problem:

Setup Objects:

– User creation

– User provisioning

 

Data Objects:

– AccountShare

– CaseShare

– Custom shares

 

Can’t mix in same transaction:

User userCreated = new User(…);

insert userCreated; // Setup object

AccountShare share = new AccountShare(…);

insert share; // Data object

// Error: Mixed DML Operation

Benefits:

  • User creation works
  • Sharing works
  • No Mixed DML errors
  • Reliable execution

EXTENSIBILITY BENEFITS

Why This Design Wins Long-Term

Adding a New Object to Share

Without Metadata-Driven Design (Old Way):

  1. Write Apex code

   – New sharing handler class

   – Custom SOQL queries

   – Error handling

   – Logging

  1. Write test code

   – Unit tests for handler

   – Integration tests

   – Edge cases

  1. Deploy to production

   – Code review

   – Testing

   – Deployment risk

  1. Support

   – Maintenance

   – Bug fixes

   – Future changes

Timeline: 2-3 weeks

Complexity: High

Risk: Medium

Cost: Significant

With Metadata-Driven Design (New Way):

  1. Create Metadata Record

   – Object: WorkOrder

   – Active: true

   – Access Level: Read

   – Done!

  1. Deploy metadata

   – No code review needed

   – No tests needed

   – No compilation

   – Instant

  1. Live

   – Engine auto-discovers

   – Auto-applies sharing

   – Works immediately

Timeline: 5 minutes

Complexity: Low

Risk: Minimal

Cost: Almost nothing

Example: Adding WorkOrder Sharing

Timeline:

  1. Identify need: “We need to share WorkOrders”
  1. Create metadata record:

Shareable_Object__mdt:

  • MasterLabel: WorkOrder Sharing
  • DeveloperName: WorkOrder_Sharing
  • API_Name__c: WorkOrder
  • Active__c: true
  • Access_Level__c: Read
  1. Save & deploy
  1. Engine detects:

   – Reads Shareable_Object__mdt

   – Finds: WorkOrder (active)

   – Queries WorkOrders

   – Applies sharing

  1. Done – WorkOrders now shared automatically

Cost: Zero code changes

Timeline: Deploy immediately

Risk: None

Flexible Configuration Examples

Example 1: Read-Only Access

Metadata:

– Object: Case

– Access: Read

– Result: Users can view, not edit

Example 2: Read/Write Access

Metadata:

– Object: WorkOrder

– Access: ReadWrite

– Result: Users can view and edit

Example 3: Enable/Disable Without Removing

Before:

Metadata: Active = true

Result: Sharing active

Change:

Metadata: Active = false

Result: Sharing inactive (no code change)

Change back:

Metadata: Active = true

Result: Sharing active again


FINAL THOUGHTS

Building a metadata-driven sharing engine represents a shift from point-solution coding to platform architecture. Instead of building one-off features, you build infrastructure that enables future features without code.

The benefits compound over time:

  • First object (Case): 80/20 on engine setup
  • Second object (WorkOrder): 5 minutes metadata
  • Third object (Shipment): 5 minutes metadata
  • Tenth object: Still 5 minutes metadata

The ROI is significant for organizations that need flexible, scalable record sharing.

The design patterns—Factory, Strategy, Dynamic SOQL, Async Processing—are fundamental to enterprise Salesforce development. Learning them through a real-world problem makes them stick.

For any organization managing complex access control on Experience Cloud or multi-tenant deployments, this pattern should be considered. It trades upfront architectural complexity for long-term flexibility and maintenance savings.

The hardest part isn’t the code—it’s thinking in abstractions rather than point solutions. But once you shift that mindset, you unlock the ability to build truly scalable, maintainable systems.

Leave a Reply

Your email address will not be published. Required fields are marked *