Receber por email
← Voltar para o catálogo

salesforce-development

Padrões especializados para o desenvolvimento na plataforma Salesforce, incluindo

41.2kestrelas
Atualizado há 4 meses

Ver no GitHub ↗ Licença: MIT

Como adicionar

/plugin marketplace add sickn33/antigravity-awesome-skills

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/salesforce-development-sickn33/svg)](https://www.skillteca.com.br/skills/salesforce-development-sickn33)

Skills relacionadas

Ver todas de Desenvolvimento →

Alerta por categoria

Receba novas skills de Desenvolvimento toda segunda

1 email curto, só com as skills novas de Desenvolvimento. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

Salesforce Development

Expert patterns for Salesforce platform development including Lightning Web Components (LWC), Apex triggers and classes, REST/Bulk APIs, Connected Apps, and Salesforce DX with scratch orgs and 2nd generation packages (2GP).

Patterns

Lightning Web Component with Wire Service

Use @wire decorator for reactive data binding with Lightning Data Service or Apex methods. @wire fits LWC's reactive architecture and enables Salesforce performance optimizations.

// myComponent.js import { LightningElement, wire, api } from 'lwc'; import { getRecord, getFieldValue } from 'lightning/uiRecordApi'; import getRelatedRecords from '@salesforce/apex/MyController.getRelatedRecords'; import ACCOUNT_NAME from '@salesforce/schema/Account.Name'; import ACCOUNT_INDUSTRY from '@salesforce/schema/Account.Industry';

const FIELDS = [ACCOUNT_NAME, ACCOUNT_INDUSTRY];

export default class MyComponent extends LightningElement { @api recordId; // Passed from parent or record page

// Wire to Lightning Data Service (preferred for single records) @wire(getRecord, { recordId: '$recordId', fields: FIELDS }) account;

// Wire to Apex method (for complex queries) @wire(getRelatedRecords, { accountId: '$recordId' }) wiredRecords({ error, data }) { if (data) { this.relatedRecords = data; this.error = undefined; } else if (error) { this.error = error; this.relatedRecords = undefined; } }

get accountName() { return getFieldValue(this.account.data, ACCOUNT_NAME); }

get isLoading() { return !this.account.data && !this.account.error; }

// Reactive: changing recordId automatically re-fetches }

// myComponent.html <template> <lightning-card title={accountName}> <template if:true={isLoading}> <lightning-spinner alternative-text="Loading"></lightning-spinner> </template>

<template if:true={account.data}> <p>Industry: {industry}</p> </template>

<template if:true={error}> <p class="slds-text-color_error">{error.body.message}</p> </template> </lightning-card> </template>

// MyController.cls public with sharing class MyController { @AuraEnabled(cacheable=true) public static List<Contact> getRelatedRecords(Id accountId) { return [ SELECT Id, Name, Email, Phone FROM Contact WHERE AccountId = :accountId WITH SECURITY_ENFORCED LIMIT 100 ]; } }

Context

  • building LWC components
  • fetching Salesforce data
  • reactive UI

Bulkified Apex Trigger with Handler Pattern

Apex triggers must be bulkified to handle 200+ records per transaction. Use handler pattern for separation of concerns, testability, and recursion prevention.

// AccountTrigger.trigger trigger AccountTrigger on Account ( before insert, before update, before delete, after insert, after update, after delete, after undelete ) { new AccountTriggerHandler().run(); }

// TriggerHandler.cls (base class) public virtual class TriggerHandler { // Recursion prevention private static Set<String> executedHandlers = new Set<String>();

public void run() { String handlerName = String.valueOf(this).split(':')[0];

// Prevent recursion String contextKey = handlerName + '_' + Trigger.operationType; if (executedHandlers.contains(contextKey)) { return; } executedHandlers.add(contextKey);

switch on Trigger.operationType { when BEFORE_INSERT { this.beforeInsert(); } when BEFORE_UPDATE { this.beforeUpdate(); } when BEFORE_DELETE { this.beforeDelete(); } when AFTER_INSERT { this.afterInsert(); } when AFTER_UPDATE { this.afterUpdate(); } when AFTER_DELETE { this.afterDelete(); } when AFTER_UNDELETE { this.afterUndelete(); } } }

// Override in child classes protected virtual void beforeInsert() {} protected virtual void beforeUpdate() {} protected virtual void beforeDelete() {} protected virtual void afterInsert() {} protected virtual void afterUpdate() {} protected virtual void afterDelete() {} protected virtual void afterUndelete() {} }

// AccountTriggerHandler.cls public class AccountTriggerHandler extends TriggerHandler { private List<Account> newAccounts; private List<Account> oldAccounts; private Map<Id, Account> newMap; private Map<Id, Account> oldMap;

public AccountTriggerHandler() { this.newAccounts = (List<Account>) Trigger.new; this.oldAccounts = (List<Account>) Trigger.old; this.newMap = (Map<Id, Account>) Trigger.newMap; this.oldMap = (Map<Id, Account>) Trigger.oldMap; }

protected override void afterInsert() { createDefaultContacts(); notifySlack(); }

protected override void afterUpdate() { handleIndustryChange(); }

// BULKIFIED: Query once, update once private void createDefaultContacts() { List<Contact> contactsToInsert = new List<Contact>();

for (Account acc : newAccounts) { if (acc.Type == 'Prospect') { contactsToInsert.add(new Contact( AccountId = acc.Id, LastName = 'Primary Contact', Email = 'contact@' + acc.Website )); } }

if (!contactsToInsert.isEmpty()) { insert contactsToInsert; // Single DML for all } }

private void handleIndustryChange() { Set<Id> changedAccountIds = new Set<Id>();

for (Account acc : newAccounts) { Account oldAcc = oldMap.get(acc.Id); if (acc.Industry != oldAcc.Industry) { changedAccountIds.add(acc.Id); } }

if (!changedAccountIds.isEmpty()) { // Queue async processing for heavy work System.enqueueJob(new IndustryChangeQueueable(changedAccountIds)); } }

private void notifySlack() { // Offload callouts to async List<Id> accountIds = new List<Id>(newMap.keySet()); System.enqueueJob(new SlackNotificationQueueable(accountIds)); } }

Context

  • apex triggers
  • data operations
  • automation

Queueable Apex for Async Processing

Use Queueable Apex for async processing with support for non-primitive types, monitoring via AsyncApexJob, and job chaining. Limit: 50 jobs per transaction, 1 child job when chaining.

// IndustryChangeQueueable.cls public class IndustryChangeQueueable implements Queueable, Database.AllowsCallouts { private Set<Id> accountIds; private Integer retryCount;

public IndustryChangeQueueable(Set<Id> accountIds) { this(accountIds, 0); }

public IndustryChangeQueueable(Set<Id> accountIds, Integer retryCount) { this.accountIds = accountIds; this.retryCount = retryCount; }

public void execute(QueueableContext context) { try { // Query with fresh data List<Account> accounts = [ SELECT Id, Name, Industry, OwnerId FROM Account WHERE Id IN :accountIds WITH SECURITY_ENFORCED ];

// Process and make callout for (Account acc : accounts) { syncToExternalSystem(acc); }

// Update records updateRelatedOpportunities(accountIds);

} catch (Exception e) { handleError(e); } }

private void syncToExternalSystem(Account acc) { HttpRequest req = new HttpRequest(); req.setEndpoint('callout:ExternalCRM/accounts'); req.setMethod('POST'); req.setHeader('Content-Type', 'application/json'); req.setBody(JSON.serialize(new Map<String, Object>{ 'salesforceId' => acc.Id, 'name' => acc.Name, 'industry' => acc.Industry }));

Http http = new Http(); HttpResponse res = http.send(req);

if (res.getStatusCode() != 200 && res.getStatusCode() != 201) { throw new CalloutException('Sync failed: ' + res.getBody()); } }

private void updateRelatedOpportunities(Set<Id> accIds) { List<Opportunity> oppsToUpdate = [ SELECT Id, Industry__c, AccountId FROM Opportunity WHERE AccountId IN :accIds WITH SECURITY_ENFORCED ];

Map<Id, Account> accountMap = new Map<Id, Account>([ S

Compartilhar X LinkedIn