TransWikia.com

Custom Metadata vs. Custom Objects in Managed Package

Salesforce Asked on October 4, 2021

I worked on Managed packages for nearly 10 years now and never missed Custom Metadata Types. Not before they came and not after they were announced. I didn’t come across a single great and technical severe reason to use Custom Metadata Types over regular Custom Object.

Unlike Custom Objects they:

  1. Don’t support triggers
  2. Are hard to create in Apex Tests
  3. Have a very limited number of field types
  4. Have an inflexible UI to managed records

The only use case where CMDT are somewhat nice is, that you cannot package Custom Object records in packages. But that is also not an issue. You can perfectly store hundreds of Custom records in JSON serialized form in a single packaged Static Resource and unpackage them on install.

So tell me what your reasons are to favor them over Custom Objects?

7 Answers

Here are some "advantages" vs using a Custom Object:

  • Can be Deployed
  • Can use Field Definition or Entity Particle to create references to Custom Object Metadata
  • Retrieve doesn't cost against SOQL limits (unless it contains long text)
  • Can be referenced in formula fields
  • Unit Testing isn't too bad as well as the code is well composed. Because they are treated similar to @seeAllData, it actually makes integration testing the current configuration of the ORG much easier.
  • Can be "protected" to store secrets in managed packages

However (IMO), Salesforce missed too many features to make them the clear choice in every scenario:

  • Records cannot be statically referenced in code (this would make dependencies much more robust)
  • No direct/global access in VF or LWC
  • Objects cannot reference them. This results in a lot of "magic" relationships being setup (EG string matching).
  • No Triggers (As mentioned above)

I've created a public google sheet that attempts to compare the features of each of the configuration options (please add comments if I missed anything).

My Approach

  • I tend to favor CMDT over Custom Settings, UNLESS I need user specific configuration.
  • I'll use Custom Objects where it's critical to ensure referential integrity to data records.
  • Custom Labels were once commonly used because they could be deployed, but now with CMDT there isn't much reason to use them (for configuration).

Correct answer by NSjonas on October 4, 2021

I've always seen them as an alternative to Custom Settings as much as an alternative to Custom Objects.

However, one use case that stands out where they were really useful was in configuring mappings in a point-to-point integration inbound to Salesforce where they essentially acted as the middleware.

We could create mappings of values from the source system to values in specified fields on specified SObjects in SFDC, and kept this to be a declarative exercise. Again, nothing that couldn't be done in JSON, but this was a Professional Services engagement where the customer was extremely keen to avoid code-heavy solutions (and they'd have classed JSON manipulation as that) due to having no available in-house resources for that.

Answered by barrick on October 4, 2021

Robert - in response to your point about Custom Metadata being hard to set up in unit tests

I use the selector pattern to query all sobjects, including Custom Metadata, then, use mocking via dependency injection to return the results to the testmethod. This decouples the testmethod from any live custom metadata (and thus achieves test isolation). Here is a small fragment

Standard fflib Selector and apexmocks pattern follows; assumes an Application class as per fflib pattern.

Since you can't construct Custom Metadata label/developername, I resort to the JSON deserialize approach. Here, I'm using the SObject Fabricator repo but there are other ways to do this including maps, json strings, or other libraries. Once you go the dependency injection route to unit tests, it is inevitable you will need at some point to have to construct sobjects that aren't supported by new TheSObject(..). I liked SObject Fabricator for its expressiveness.

fflib_ApexMocks mocks = new fflib_ApexMocks();
// Given mockSelector results
MyCustomMetadata__mdt[] mockMdts = new List<MyCustomMetadata__mdt> {
                (MyCustomMetadata__mdt) new sfab_FabricatedSObject (MyCustomMetadata__mdt.class) // [
                        .setField(MyCustomMetadata__mdt.Label,'foo')
                        .setField(MyCustomMetadata__mdt.SomeField__c,true)                          
                        .toSObject(),
                (MyCustomMetadata__mdt) new sfab_FabricatedSObject (MyCustomMetadata__mdt.class)    // [1] not troubleshooter
                        .setField(MyCustomMetadata__mdt.Label,'bar')
                        .setField(MyCustomMetadata__mdt.SomeField__c,false)
                        .toSObject()
        };

// Given mock selector(s)
MyCustomMetadatasSelector mockMdtsSelector = (MyCustomMetadatasSelector) mocks.mock(MyCustomMetadatasSelector.class);
mocks.startStubbing();
mocks.when(mockMdtsSelector.sObjectType()).thenReturn(MyCustomMetadata__mdt.SObjectType);
mocks.when(mockMdtsSelector.selectByLabel(new Set<String>{'foo'}))
            .thenReturn(new List< MyCustomMetadata__mdt> {mockMdts[0]});
mocks.when(mockMdtsSelector.selectByLabel(new Set<String>{'bar'}))
            .thenReturn(new List<MyCustomMetadata__mdt> {mockMdts[1]});
mocks.stopStubbing();
// Given mocks injected
Application.Selector.setMock(mockCrsdToDescriptionsSelector);

// when code-under-test invoked
Test.startTest();
new MyClass().doStuff();
Test.stopTest();

// Then verify
.. asserts and/or mocks.verify

Note that if the selector method is called with a set containing only foo, the first mocked custom metadata is retrieved by the dependency-injected selector. And, if called with a set containing bar, the second mocked custom metadata is retrieved.

Since you can't construct Custom Metadata label/developername, I resort to the JSON deserialize approach. Here, I'm using the SObject Fabricator repo but there are other ways to do this including maps, json strings, or other libraries. Once you go the dependency injection route to unit tests, it is inevitable you will need at some point to have to construct sobjects that aren't supported by new TheSObject(..). I liked SObject Fabricator for its expressiveness.

I know you use your own testobject fabricator so adapt as necessary.

Answered by cropredy on October 4, 2021

Some benefits not listed but I like are:

  • Page Layouts (custom settings can get quite messy for customers given we don't control the layout). With metadata types, we can publish layouts.
  • An indirect one seems to be for the customers: they can manage their configuration and migrate through environment more easily than custom settings.
    • This can make their lives much easier because some rely on manual steps to configure environments - including production.
  • Allow reference to object and field in a controlled way.
    • This usually looks like a string in a custom setting. Using "Metadata Relationship" with custom metadata types this is tied to real object and fields while editing, visualizing.

I still agree that a lot should be improved and not being able to fully replace hierarchical custom settings also make them a little less appealing. Plus, unit tests experience with them still more painful.

Answered by Jeferson Chaves on October 4, 2021

We've implemented them and it was one of the biggest disappointments as an ISV, here's why:

  • Doesn't support every object (surprise, Users don't work!)
  • Creating via code is absolute pain as it's either async or metadata deploy - both means thousands of lines of code (but only second one is reliable)
  • Impossible to relate to an SObject (kinda makes sense since it's deployable, but opposite should be possible)
  • Doesn't have a createdbyid, lastmodifieddate, etc

There was also some obscure SOQL limitation that I can't remember at the moment.

Basically it's only upside (deployable, wow) hardly justifies all the downsides.

Answered by dzh on October 4, 2021

A reason I haven't seen articulated yet applies specifically to the managed package context:

Packaged, protected Custom Metadata Types are an ideal solution for secret storage in a managed package context. While Protected Custom Objects and Protected Custom Settings can also offer secret storage, only Custom Metadata Types allow those secrets to be packaged and deployed, and protected from everything outside the boundary of packaged Apex.

Answered by David Reed on October 4, 2021

The big reason for me to use Custom Metadata is the logical separation between Custom Metadata and Custom Objects. In my head they serve a different purpose and that helps me better organize things in my org. I would store any configurations for my applications in Custom Metadata rather than Custom Objects. The way I see it Custom Objects should retain data about records in the org.

Additionally as NSjonas pointed out CM doesn't count against the governor limits in the org. They can be refreshed to sandboxes and are cached data which would provide better performance.

Answered by Arthlete on October 4, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP