Beyond Custom Metadata Types

54
Beyond Custom Metadata Types Avrom Roy-Faderman Principal Member of Techincal Staff Salesforce App Cloud [email protected] @aroyfaderman The Vision for “Platform on the Platform” in Force.com Haripriya Murthy Senior Member of Techincal Staff Salesforce App Cloud [email protected] @haripriyamurthy

Transcript of Beyond Custom Metadata Types

Page 1: Beyond Custom Metadata Types

Beyond Custom Metadata Types

Avrom Roy-Faderman

Principal Member of Techincal Staff

Salesforce App Cloud

[email protected]

@aroyfaderman

The Vision for “Platform on the Platform” in Force.com

Haripriya Murthy

Senior Member of Techincal Staff

Salesforce App Cloud

[email protected]

@haripriyamurthy

Page 2: Beyond Custom Metadata Types

Safe harbor statement under the Private Securities Litigation Reform Act of 1995:

This presentation may contain forward-looking statements that involve risks, uncertainties, and assumptions. If any such uncertainties materialize

or if any of the assumptions proves incorrect, the results of salesforce.com, inc. could differ materially from the results expressed or implied by the

forward-looking statements we make. All statements other than statements of historical fact could be deemed forward-looking, including any

projections of product or service availability, subscriber growth, earnings, revenues, or other financial items and any statements regarding

strategies or plans of management for future operations, statements of belief, any statements concerning new, planned, or upgraded services or

technology developments and customer contracts or use of our services.

The risks and uncertainties referred to above include – but are not limited to – risks associated with developing and delivering new functionality for

our service, new products and services, our new business model, our past operating losses, possible fluctuations in our operating results and rate

of growth, interruptions or delays in our Web hosting, breach of our security measures, the outcome of any litigation, risks associated with

completed and any possible mergers and acquisitions, the immature market in which we operate, our relatively limited operating history, our ability

to expand, retain, and motivate our employees and manage our growth, new releases of our service and successful customer deployment, our

limited history reselling non-salesforce.com products, and utilization and selling to larger enterprise customers. Further information on potential

factors that could affect the financial results of salesforce.com, inc. is included in our annual report on Form 10-K for the most recent fiscal year

and in our quarterly report on Form 10-Q for the most recent fiscal quarter. These documents and others containing important disclosures are

available on the SEC Filings section of the Investor Information section of our Web site.

Any unreleased services or features referenced in this or other presentations, press releases or public statements are not currently available and

may not be delivered on time or at all. Customers who purchase our services should make the purchase decisions based upon features that are

currently available. Salesforce.com, inc. assumes no obligation and does not intend to update these forward-looking statements.

Safe Harbor

Page 3: Beyond Custom Metadata Types

CRM Apps ISV Apps IT Apps

The “Standard” Force.com Platform

App Cloud

Customer

The Salesforce Advantage

Page 4: Beyond Custom Metadata Types

IT Apps

The “Platform on the Platform” Vision

App Cloud

CRM Apps ISV Apps IT Apps

Customer

IT AppsISV Apps

ISV Platform IT Platform

The <Your Company Name> Advantage

Page 5: Beyond Custom Metadata Types

RemoteSiteSetting

ValidationRule

CustomObjectTranslation

PermissionSet

<Many more examples>

Standard Metadata Types Custom Metadata Types

The Story So Far…

ApiMapping

StockPointRule

InternationalVatRule

AclRule

<Whatever you want>

Page 6: Beyond Custom Metadata Types

Instances are:

• Packageable/Upgradeable

• Deployable (change set/MD API)

• Free to access (via Describe)

• Salesforce writes the code that “makes them go”

Standard Metadata Types Custom Metadata Types

The Story So Far…

Instances are:

• Packageable/Upgradeable

• Deployable (change set/MD API)

• Free to access (via SOQL)

• You write the code that “makes them go”

Page 7: Beyond Custom Metadata Types

The Vision for the Future

Demo: Coming… Now

• For everyone: Create Custom Metadata Types and Records in the Setup UI

• For ISVs: Control Field Editability and Record Visibility

Metadata Relationships

Dynamic Triggers

• With pseudocode walkthrough

Apex DDL

• With pseudocode walkthrough

Custom Datatypes

Summary/Roadmap/Q&A

And the Agenda for Today

Page 8: Beyond Custom Metadata Types

Haripriya MurthySenior Member of the Technical Staff, Salesforce App Cloud

Page 9: Beyond Custom Metadata Types

DemoComing… now (Winter 2016)

Page 10: Beyond Custom Metadata Types

Metadata RelationshipsIntegrate your types into a unified declarative platform

Page 11: Beyond Custom Metadata Types

RollupOverLookup__mdt

ParentObject__c: MD Rel

ParentField__c: MD Rel

SummarizedObject__c: MD Rel

LookupField__c: MD Rel

FieldToSummarize__c: MD Rel

RollupOperation__c: Picklist

Active__c: Checkbox

Description__c: LTA

Metadata relationships to SObjects

Triggerable SObject Types

Page 12: Beyond Custom Metadata Types

RollupOverLookup__mdt

ParentObject__c: MD Rel

ParentField__c: MD Rel

SummarizedObject__c: MD

RelLookupField__c: MD Rel

LookupField__c: MD Rel

FieldToSummarize__c: MD Rel

RollupOperation__c: Picklist

Active__c: Checkbox

Description__c: LTA

Metadata relationships to SObjects

Triggerable SObject Types

ParentObject__c ParentField__c SummarizedObject__c FieldToSummarize__c RollupOperation__c

Contact TotalOppValue__c Opportunity Amount SUM

JobListing__c TotalAppliedCandidates__c Candidate__c COUNT

Page 13: Beyond Custom Metadata Types

Metadata relationships to other types

RollupFilter__mdt

FilterField__c: MD Rel

FilterOperation__c: Picklist

Value__c: String

Summary__c: MD Rel

RollupOverLookup__mdt

ParentObject__c: MD Rel

ParentField__c: MD Rel

SummarizedObject__c: MD

RelLookupField__c: MD Rel

LookupField__c: MD Rel

FieldToSummarize__c: MD Rel

RollupOperation__c: Picklist

Active__c: Checkbox

Description__c: LTA

Page 14: Beyond Custom Metadata Types

Metadata relationships to other types

FilterField__c FilterOperation__c Value__c Summary__c

Stage NOT EQUALS Closed Lost TotalAmountByContact__md

IsEmployee__c EQUALS False TotalApplicants__md

RollupFilter__mdt

FilterField__c: MD Rel

FilterOperation__c: Picklist

Value__c: String

Summary__c: MD Rel

RollupOverLookup__mdt

ParentObject__c: MD Rel

ParentField__c: MD Rel

SummarizedObject__c: MD Rel

LookupField__c: MD Rel

FieldToSummarize__c: MD Rel

RollupOperation__c: Picklist

Active__c: Checkbox

Description__c: LTA

Page 15: Beyond Custom Metadata Types

RollupOverLookup__mdt

ParentObject__c: MD Rel

ParentField__c: MD Rel

SummarizedObject__c: MD Rel

LookupField__c: MD Rel

FieldToSummarize__c: MD Rel

RollupOperation__c: Picklist

Active__c: Checkbox

Description__c: LTA

Metadata relationships to Fields

Fields

Page 16: Beyond Custom Metadata Types

Metadata relationships to other entities

Apex Classes

Custom

metadata

type

Permsets

Static resources

Page 17: Beyond Custom Metadata Types

Creating a metadata relationship field

Page 18: Beyond Custom Metadata Types

Metadata relationship in SOQL

Access the fields as is

select ParentObject__c, ParentField__c from RollupOverLookup__mdt where developerName = ‘OppExpectedRevenue’;

Page 19: Beyond Custom Metadata Types

Metadata relationship in SOQL

Access the fields as is

select ParentObject__c, ParentField__c from RollupOverLookup__mdt where developerName = ‘OppExpectedRevenue’;

Traverse relationship from child

select ParentObject__r.Name from RollupOverLookup__mdt where developerName = ‘OppExpectedRevenue’;

Page 20: Beyond Custom Metadata Types

Metadata relationship in SOQL

Access the fields as is

select ParentObject__c, ParentField__c from RollupOverLookup__mdt where developerName = ‘OppExpectedRevenue’;

Traverse relationship from child

select ParentObject__r.Name from RollupOverLookup__mdt where developerName = ‘OppExpectedRevenue’;

Filter using relationship

select ParentField__c from RollupOverLookup__mdt where developerName = ‘OppExpectedRevenue’ and

ParentObject__r.Name = ‘Opportunity’;

Page 21: Beyond Custom Metadata Types

Dynamic TriggersCustomize the record save process

Page 22: Beyond Custom Metadata Types

CustomField is of type Number?

• Non-numeric data rejected

CustomField is of type RollupSummary?

• On save of children, parents are updated

WorkflowRule exists on object?

• Should fire when the object saves and filter is

matched

Standard Metadata Types Custom Metadata Types

Metadata Should Affect the Save Process

InternationalVatRule applies?

• VAT should get added on save

“Rollup over Lookup” exists?

• On save of children, parents are updated

Object-Object mapping exists?

• When first object is inserted, transformed second

object should be inserted too

Page 23: Beyond Custom Metadata Types

Step 1: Create a Custom Metadata TypeWith a Relationship to Triggerable SObject Types

Triggerable SObject Types

RollupFilter__mdt

FilterField__c: MD Rel

FilterOperation__c: Picklist

Value__c: String

Summary__c: MD Rel

RollupOverLookup__mdt

ParentObject__c: MD Rel

ParentField__c: MD Rel

SummarizedObject__c: MD Rel

LookupField__c: MD Rel

FieldToSummarize__c: MD Rel

RollupOperation__c: Picklist

Active__c: Checkbox

Description__c: LTA

Page 24: Beyond Custom Metadata Types

Step 2: Write a Dynamic Apex Triggerover the Relationship

dynamic trigger RecalcRollupTriggerover RollupOverLookup__mdt.SummarizedObject__c (BEFORE INSERT, BEFORE UPDATE, BEFORE DELETE)

{/* … */

}

Page 25: Beyond Custom Metadata Types

You or an Admin Can Do This

Step 3: Populate the Custom Metadata Type

ParentObject__c ParentField__c SummarizedObject__c FieldToSummarize__c RollupOperation__c

Contact TotalOppValue__c Opportunity Amount SUM

JobListing__c TotalAppliedCandidates__c Candidate__c COUNT

FilterField__c FilterOperation__c Value__c Summary__c

Stage NOT EQUALS Closed Lost TotalAmountByContact__md

IsEmployee__c EQUALS False TotalApplicants__md

Page 26: Beyond Custom Metadata Types

Runtime Example 1Opportunities

End-user inserts some opportunities

RollupOverLookup__mdt records with SummarizedObject__c = Opportunity found!

RecalcRollupTrigger executes before insert

• Trigger.sObjectType = Opportunity.sObjectType

• Trigger.customMetadata = [found records of RollupOverLookup__mdt]

Page 27: Beyond Custom Metadata Types

Runtime Example 2

End-user inserts some contacts

No RollupOverLookup__mdt records with SummarizedObject__c = Contact found!

RecalcRollupTrigger does not execute

Contacts

Page 28: Beyond Custom Metadata Types

Pseudocode Exampledynamic trigger ReclalcRollupTrigger over RollupOverLookup__mdt.SummarizedObject__c

(BEFORE INSERT, BEFORE DELETE, BEFORE UPDATE) {Schema.SObjectType thisObject = Trigger.sObjectType;RollupOverLookup__mdt[] summariesOfThisObject = Trigger.customMetadata;for (RollupOverLookup__mdt summary : summariesOfThisObject) {

RollupFilter__mdt[] filters = [SELECT FilterField__r.QualifiedApiName, Operation__c, Value__c FROM RollupFilter__mdt WHERE Summary__c = summary.Id];

if (Trigger.isInsert) {SObject[] matchingChildren = Trigger.new;for (RollupFilter__mdt oneFilter : filters) {

matchingChildren = FilterUtil.filter(matchingChildren, oneFilter);}SObject[] parents = RollupUtil.getParents(

matchingChildren, summary.ParentObject__r.QualifiedApiName,summary.LookupField__r.QualifiedApiName);

RollupUtil.adjustParents(parents, summary.ParentField__r.QualifiedApiName, summary.RollupOperation__c, matchingChildren, summary.FieldToSummarize__r.QualifiedApiName);

update parents;}

…}

}

Page 29: Beyond Custom Metadata Types

Pseudocode Exampledynamic trigger ReclalcRollupTrigger over RollupOverLookup__mdt.SummarizedObject__c

(BEFORE INSERT, BEFORE DELETE, BEFORE UPDATE) {Schema.SObjectType thisObject = Trigger.sObjectType;RollupOverLookup__mdt[] summariesOfThisObject = Trigger.customMetadata;for (RollupOverLookup__mdt summary : summariesOfThisObject) {

RollupFilter__mdt[] filters = [SELECT FilterField__r.QualifiedApiName, Operation__c, Value__c FROM RollupFilter__mdt WHERE Summary__c = summary.Id];

if (Trigger.isInsert) {SObject[] matchingChildren = Trigger.new;for (RollupFilter__mdt oneFilter : filters) {

matchingChildren = FilterUtil.filter(matchingChildren, oneFilter);}SObject[] parents = RollupUtil.getParents(

matchingChildren, summary.ParentObject__r.QualifiedApiName,summary.LookupField__r.QualifiedApiName);

RollupUtil.adjustParents(parents, summary.ParentField__r.QualifiedApiName, summary.RollupOperation__c, matchingChildren, summary.FieldToSummarize__r.QualifiedApiName);

update parents;}

…}

}

Page 30: Beyond Custom Metadata Types

Pseudocode Exampledynamic trigger ReclalcRollupTrigger over RollupOverLookup__mdt.SummarizedObject__c

(BEFORE INSERT, BEFORE DELETE, BEFORE UPDATE) {Schema.SObjectType thisObject = Trigger.sObjectType;RollupOverLookup__mdt[] summariesOfThisObject = Trigger.customMetadata;for (RollupOverLookup__mdt summary : summariesOfThisObject) {

RollupFilter__mdt[] filters = [SELECT FilterField__r.QualifiedApiName, Operation__c, Value__c FROM RollupFilter__mdt WHERE Summary__c = summary.Id];

if (Trigger.isInsert) {SObject[] matchingChildren = Trigger.new;for (RollupFilter__mdt oneFilter : filters) {

matchingChildren = FilterUtil.filter(matchingChildren, oneFilter);}SObject[] parents = RollupUtil.getParents(

matchingChildren, summary.ParentObject__r.QualifiedApiName,summary.LookupField__r.QualifiedApiName);

RollupUtil.adjustParents(parents, summary.ParentField__r.QualifiedApiName, summary.RollupOperation__c, matchingChildren, summary.FieldToSummarize__r.QualifiedApiName);

update parents;}

…}

}

Page 31: Beyond Custom Metadata Types

Pseudocode Exampledynamic trigger ReclalcRollupTrigger over RollupOverLookup__mdt.SummarizedObject__c

(BEFORE INSERT, BEFORE DELETE, BEFORE UPDATE) {Schema.SObjectType thisObject = Trigger.sObjectType;RollupOverLookup__mdt[] summariesOfThisObject = Trigger.customMetadata;for (RollupOverLookup__mdt summary : summariesOfThisObject) {

RollupFilter__mdt[] filters = [SELECT FilterField__r.QualifiedApiName, Operation__c, Value__c FROM RollupFilter__mdt WHERE Summary__c = summary.Id];

if (Trigger.isInsert) {SObject[] matchingChildren = Trigger.new;for (RollupFilter__mdt oneFilter : filters) {

matchingChildren = FilterUtil.filter(matchingChildren, oneFilter);}SObject[] parents = RollupUtil.getParents(

matchingChildren, summary.ParentObject__r.QualifiedApiName,summary.LookupField__r.QualifiedApiName);

RollupUtil.adjustParents(parents, summary.ParentField__r.QualifiedApiName, summary.RollupOperation__c, matchingChildren, summary.FieldToSummarize__r.QualifiedApiName);

update parents;}

…}

}

Page 32: Beyond Custom Metadata Types

Pseudocode Exampledynamic trigger ReclalcRollupTrigger over RollupOverLookup__mdt.SummarizedObject__c

(BEFORE INSERT, BEFORE DELETE, BEFORE UPDATE) {Schema.SObjectType thisObject = Trigger.sObjectType;RollupOverLookup__mdt[] summariesOfThisObject = Trigger.customMetadata;for (RollupOverLookup__mdt summary : summariesOfThisObject) {

RollupFilter__mdt[] filters = [SELECT FilterField__r.QualifiedApiName, Operation__c, Value__c FROM RollupFilter__mdt WHERE Summary__c = summary.Id];

if (Trigger.isInsert) {SObject[] matchingChildren = Trigger.new;for (RollupFilter__mdt oneFilter : filters) {

matchingChildren = FilterUtil.filter(matchingChildren, oneFilter);}SObject[] parents = RollupUtil.getParents(

matchingChildren, summary.ParentObject__r.QualifiedApiName,summary.LookupField__r.QualifiedApiName);

RollupUtil.adjustParents(parents, summary.ParentField__r.QualifiedApiName, summary.RollupOperation__c, matchingChildren, summary.FieldToSummarize__r.QualifiedApiName);

update parents;}

…}

}

Page 33: Beyond Custom Metadata Types

• Lets the platform developer control the save process of Sobjects using your custom metadata types.

• Code written by the platform developer will be executed for entities that might not even exist at the

time the code was written.

• ISVs can package dynamic triggers and the triggers can be used to modify save process of the

customer org’s objects that use the platform built by the ISVs.

Dynamic triggers

Page 34: Beyond Custom Metadata Types

Apex DDLSet up metadata natively

Page 35: Beyond Custom Metadata Types

Create metadata in native apex

• Fewer manual install procedures

• Easier custom tooling

• No callouts required

Page 36: Beyond Custom Metadata Types

public void createRollupField(String devName, EntityDefinition parent, EntityDefinition child,FieldDefinition summarized, FieldDefinition lookup, PicklistValue op) {

MetadataContainer mdc = new MetadataContainer();insert mdc;FieldDefinition summarizingField = new FieldDefintion();summarizingField.EntityId = parent.EntityId;summarizingField.DeveloperName = devName;…mdc.addMember(summarizingField);RollupOverLookup__mdt rollup = new RollupOverLookup__mdt();rollup.ParentObject__c = parent.EntityId;rollup.SummarizedObject__c = child.EntityId;rollup.RollupOperation__c = op;rollup.parentField__r = summarizingField;…mdc.addMember(rollup);mdc.deploy();

}

Pseudocode Example

Page 37: Beyond Custom Metadata Types

Pseudocode Examplepublic void createRollupField(String devName, EntityDefinition parent, EntityDefinition child,

FieldDefinition summarized, FieldDefinition lookup, PicklistValue op) {

MetadataContainer mdc = new MetadataContainer();insert mdc;FieldDefinition summarizingField = new FieldDefintion();summarizingField.EntityId = parent.EntityId;summarizingField.DeveloperName = devName;…mdc.addMember(summarizingField);RollupOverLookup__mdt rollup = new RollupOverLookup__mdt();rollup.ParentObject__c = parent.EntityId;rollup.ChildObject__c = child.EntityId;rollup.RollupOperation__c = op;rollup.parentField__r = summarizingField;…mdc.addMember(rollup);mdc.deploy();

}

Page 38: Beyond Custom Metadata Types

public void createRollupField(String devName, EntityDefinition parent, EntityDefinition child,FieldDefinition summarized, FieldDefinition lookup, PicklistValue op) {

MetadataContainer mdc = new MetadataContainer();insert mdc;FieldDefinition summarizingField = new FieldDefintion();summarizingField.EntityId = parent.EntityId;summarizingField.DeveloperName = devName;…mdc.addMember(summarizingField);RollupOverLookup__mdt rollup = new RollupOverLookup__mdt();rollup.ParentObject__c = parent.EntityId;rollup.ChildObject__c = child.EntityId;rollup.RollupOperation__c = op;rollup.parentField__r = summarizingField;…mdc.addMember(rollup);mdc.deploy();

}

Pseudocode Example

Page 39: Beyond Custom Metadata Types

public void createRollupField(String devName, EntityDefinition parent, EntityDefinition child,FieldDefinition summarized, FieldDefinition lookup, PicklistValue op) {

MetadataContainer mdc = new MetadataContainer();insert mdc;FieldDefinition summarizingField = new FieldDefintion();summarizingField.EntityId = parent.EntityId;summarizingField.DeveloperName = devName;…mdc.addMember(summarizingField);RollupOverLookup__mdt rollup = new RollupOverLookup__mdt();rollup.ParentObject__c = parent.EntityId;rollup.ChildObject__c = child.EntityId;rollup.RollupOperation__c = op;rollup.parentField__r = summarizingField;…mdc.addMember(rollup);mdc.deploy();

}

Pseudocode Example

Page 40: Beyond Custom Metadata Types

public void createRollupField(String devName, EntityDefinition parent, EntityDefinition child,FieldDefinition summarized, FieldDefinition lookup, PicklistValue op) {

MetadataContainer mdc = new MetadataContainer();insert mdc;FieldDefinition summarizingField = new FieldDefintion();summarizingField.EntityId = parent.EntityId;summarizingField.DeveloperName = devName;…mdc.addMember(summarizingField);RollupOverLookup__mdt rollup = new RollupOverLookup__mdt();rollup.ParentObject__c = parent.EntityId;rollup.ChildObject__c = child.EntityId;rollup.RollupOperation__c = op;rollup.parentField__r = summarizingField;…mdc.addMember(rollup);mdc.deploy();

}

Pseudocode Example

Page 41: Beyond Custom Metadata Types

public void createRollupField(String devName, EntityDefinition parent, EntityDefinition child,FieldDefinition summarized, FieldDefinition lookup, PicklistValue op) {

MetadataContainer mdc = new MetadataContainer();insert mdc;FieldDefinition summarizingField = new FieldDefintion();summarizingField.EntityId = parent.EntityId;summarizingField.DeveloperName = devName;…mdc.addMember(summarizingField);RollupOverLookup__mdt rollup = new RollupOverLookup__mdt();rollup.ParentObject__c = parent.EntityId;rollup.ChildObject__c = child.EntityId;rollup.RollupOperation__c = op;rollup.parentField__r = summarizingField;…mdc.addMember(rollup);mdc.deploy();

}

Pseudocode Example

Page 42: Beyond Custom Metadata Types

public void createRollupField(String devName, EntityDefinition parent, EntityDefinition child,FieldDefinition summarized, FieldDefinition lookup, PicklistValue op) {

MetadataContainer mdc = new MetadataContainer();insert mdc;FieldDefinition summarizingField = new FieldDefintion();summarizingField.EntityId = parent.EntityId;summarizingField.DeveloperName = devName;…mdc.addMember(summarizingField);RollupOverLookup__mdt rollup = new RollupOverLookup__mdt();rollup.ParentObject__c = parent.EntityId;rollup.ChildObject__c = child.EntityId;rollup.RollupOperation__c = op;rollup.parentField__r = summarizingField;…mdc.addMember(rollup);mdc.deploy();

}

Pseudocode Example

Page 43: Beyond Custom Metadata Types

Avrom Roy-FadermanPrincipal Member of the Technical Staff, Salesforce App Cloud

Page 44: Beyond Custom Metadata Types

Custom DatatypesCreate your own meta-schema

Page 45: Beyond Custom Metadata Types

Custom DatatypesCompound and configurable

Custom datatype

Column

Column

Column

One or more columns of

standard datatypes

Page 46: Beyond Custom Metadata Types

Custom metadata type

Custom DatatypesCompound and configurable

Custom datatype

Column

Column

Column

Custom Field

DefinitionsMD Rel Field

Values of this field get

auto-populated with

fields of the custom

datatype

Page 47: Beyond Custom Metadata Types

Custom metadata type

Custom DatatypesCompound and configurable

Custom datatype

Column

Column

Column

MD Rel Field

Field

Field

Other fields here contain

additional metadata

relevant to fields of this

type

Page 48: Beyond Custom Metadata Types

RollupOverLookup__mdt

Example Custom DatatypeRollupOverLookup

RollupOverLookup

Value__s Fields of type

RollupOverLookupParentField__c

SummarizedField__c

Operation__c

Page 49: Beyond Custom Metadata Types

Creating a Field of the Custom Type

Page 50: Beyond Custom Metadata Types

Summary and Roadmap

Page 51: Beyond Custom Metadata Types

Our vision: Open up the platform to partners and customers the way the platform opens up apps

• Custom metadata types are a first step towards this, providing custom types of metadata with standard:

• SDLC

• Packaging

• Setup UI

• Fast runtime access

• More features on our roadmap

• Metadata relationships for type integration (plus other datatypes!)

• Customizable save process via dynamic triggers

• Custom tooling and automated setup via Apex DDL

• Custom datatypes for fields

• Even more stuff in the longer term!

Summary

Page 52: Beyond Custom Metadata Types

Available now Spring ’16 Summer ’16 Winter ’17 Beyond

Metadata

Relationships

Apex DDL

Dynamic

Triggers

Custom

Datatypes

Extra-super-special safe harbor!

Roadmap

Pilot GA

Pilot GA

GA

Pilot GA

Page 53: Beyond Custom Metadata Types

Share Your Feedback, and Win a GoPro!

3Earn a GoPro prize entry for each completed survey

Tap the bell to take a survey

2Enroll in a session1

Page 54: Beyond Custom Metadata Types

Thank you