Keeping track of what you have built in your Salesforce org can be overwhelming. What was once a few simple Triggers, is now hundreds of Triggers, Apex classes, and VisualForce pages — each built to accommodate a new business unit or a regional process variation. You also have supporting logic in thousands of workflow rules, validation rules, approval workflows, etc. Maintaining control of this growing set of metadata artifacts can be daunting.
Today, we will discuss a design for a Salesforce tool that collects your metadata artifacts and put them more manageable organized groups. These groups can be business units, functional teams, a business capability, an approved customization, and more. Here are a few examples:
Below is the high-level object model for this tool. In addition to the Artifact to Artifact Group lookup, we are assuming that an artifact will be leveraged by other groups – therefore we have the Additional Usage junction object.
Consider a custom round-robin application developed to route leads. The Artifact Group “Sales Lead Routing” is created and approved by an Architecture Review Board (ARB). The following artifacts are associated to the “Sales Lead Routing” group:
- Lead Routing Trigger
- RoundRobin APEX helper class
- Routing Rule custom object
- RoundRobin Test Class
When the support team later needs to build a round-robin application for Case routing, the ARB recommends they reuse the relevant artifacts in the “Sales Lead Routing” group. A new Artifact Group “Case Routing Customization” is created and holds artifacts like Case Routing Trigger, and Test Classes.. Additional Usage records are then created for each of the artifacts that are leveraged from the “Sales Lead Routing” group.
This basic data model enables these powerful features:
- Artifact ownership is conveyed via the Artifact Group. We have a single point of reference for production errors, deployment failures and enhancement requests for these artifacts.
- We can add custom fields to Artifact and Artifact group to document the design, constraints, purpose, and roadmap for the artifacts.
- We have created a “paper trail” for governance groups to document decisions; we can take advantage of Approval Workflows, Reports, Chatter, etc. to help govern and communicate information about artifacts.
- As artifact changes are proposed, additional usage records ensure we can include all relevant stakeholders in the approval decision.
For this application to be effective, it must ensure data accuracy and completeness. If you rely on developers to report what artifacts have been added or changed, this application will not be accurate nor complete as time progresses. A solution that can ensure changes are tracked accurately and completely is to collect metadata directly from the org as the last post-deployment step. This entails querying your Salesforce org via the tooling/metadata API, mapping the different objects returned to the Metadata Artifact object model, and invoking UPSERT for all adds and changes. Here are a few examples of the SOQL statements you will likely use:
// lastUpsertDate should be stored / retrieved from Custom Metadata…
DateTime lastUpsertDate = DateTime.parse('10/14/2016 11:46 AM');
// Retrieve Apex Classes
List<ApexClass> updatedClasses = [Select Id, NamespacePrefix, Name, LengthWithoutComments, LastModifiedDate, CreatedDate, ApiVersion From ApexClass Where LastModifiedDate > :lastUpsertDate];
// Retrieve Apex Triggers
List<ApexTrigger> updatedTriggers = [Select Id, NamespacePrefix, Name, LengthWithoutComments, LastModifiedDate, CreatedDate, TableEnumOrId, UsageBeforeInsert, UsageBeforeUpdate, UsageAfterInsert, UsageAfterUpdate, ApiVersion From ApexTrigger Where LastModifiedDate > :lastUpsertDate];
// Retrieve Custom Objects
List<EntityDefinition> updatedSObjects = [Select Id, NamespacePrefix, DeveloperName From EntityDefinition Where iscustomsetting = false and LastModifiedDate > :lastUpsertDate];
Note the choice of UPSERT is generally less preferable in a high-transaction environment — but since this is expected to run only on launch night, I am not so concerned with the performance hit. I also chose the Name field as the key for UPSERT matching. This made it simple to add pre-approved metadata artifacts that are entered by an ARB early in the project (before they exist in production). Following this thought, I also recommend an “ignore until” date on the Artifact Group. If your ARB approves the customizations, it can add an “ignore until” date that is after your deployment. You can now focus post-deployment effort on chasing a smaller set of unapproved/orphaned artifacts.
There are a lot of design options in this application — I am looking forward to exploring them further. In the next post, I will look deeper at how the Strategy and Command design patterns can be used to organize the collection of metadata.