Page 1
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
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
CRM Apps ISV Apps IT Apps
The “Standard” Force.com Platform
App Cloud
Customer
The Salesforce Advantage
Page 4
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
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
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
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
Haripriya MurthySenior Member of the Technical Staff, Salesforce App Cloud
Page 9
DemoComing… now (Winter 2016)
Page 10
Metadata RelationshipsIntegrate your types into a unified declarative platform
Page 11
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
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
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
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
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
Metadata relationships to other entities
Apex Classes
Custom
metadata
type
Permsets
Static resources
Page 17
Creating a metadata relationship field
Page 18
Metadata relationship in SOQL
Access the fields as is
select ParentObject__c, ParentField__c from RollupOverLookup__mdt where developerName = ‘OppExpectedRevenue’;
Page 19
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
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
Dynamic TriggersCustomize the record save process
Page 22
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
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
Step 2: Write a Dynamic Apex Triggerover the Relationship
dynamic trigger RecalcRollupTriggerover RollupOverLookup__mdt.SummarizedObject__c (BEFORE INSERT, BEFORE UPDATE, BEFORE DELETE)
{/* … */
}
Page 25
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
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
Runtime Example 2
End-user inserts some contacts
No RollupOverLookup__mdt records with SummarizedObject__c = Contact found!
RecalcRollupTrigger does not execute
Contacts
Page 28
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
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
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
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
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
• 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
Apex DDLSet up metadata natively
Page 35
Create metadata in native apex
• Fewer manual install procedures
• Easier custom tooling
• No callouts required
Page 36
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
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
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
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
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
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
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
Avrom Roy-FadermanPrincipal Member of the Technical Staff, Salesforce App Cloud
Page 44
Custom DatatypesCreate your own meta-schema
Page 45
Custom DatatypesCompound and configurable
Custom datatype
Column
Column
Column
One or more columns of
standard datatypes
Page 46
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
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
RollupOverLookup__mdt
Example Custom DatatypeRollupOverLookup
RollupOverLookup
Value__s Fields of type
RollupOverLookupParentField__c
SummarizedField__c
Operation__c
…
Page 49
Creating a Field of the Custom Type
Page 50
Summary and Roadmap
Page 51
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
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
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