Skip to main content

Aplicar princípios de Unit of Work no Apex

Objetivos de aprendizagem

Após concluir esta unidade, você estará apto a:

  • Descrever a classe Unit of Work e seus métodos.
  • Usar a classe fflib_SObjectUnitOfWork e sua API no Apex.
Nota

Nota

Deseja aprender em português (Brasil)? Comece o desafio em um Trailhead Playground de português (Brasil) e use as traduções fornecidas entre parênteses para navegar. Copie e cole somente os valores em inglês porque as validações dos desafios dependem de dados em inglês. Se você não passar no desafio em sua organização de português (Brasil), recomendamos que (1) mude o local para os Estados Unidos, (2) mude o idioma para inglês, seguindo as instruções aqui, e (3) clique novamente no botão “Validar o desafio”.

Consulte o emblema Trailhead no seu idioma para saber mais sobre como aproveitar a experiência de Trailhead em outros idiomas.

Acompanhar com o Trail Together

Deseja acompanhar um especialista enquanto trabalha nesta etapa? Veja este vídeo que faz parte da série Trail Together.

(Este clipe começa na marca dos 01:08:31 minutos, caso você queira retroceder e ver o início da etapa novamente.)

Como esticar as pernas com a Unit of Work

Já que se trata de conteúdo avançado, só faz sentido considerar um cenário mais complexo: criar uma oportunidade e todos os registros dependentes obrigatórios do zero. Isso é muito! O código de configuração de teste a seguir mostra como isso pode ser feito sem a Unit of Work. Bem detalhado, não é?

List<Opportunity> opps = new List<Opportunity>();
List<List<Product2>> productsByOpp = new List<List<Product2>>();
List<List<PricebookEntry>> pricebookEntriesByOpp = new List<List<PricebookEntry>>();
List<List<OpportunityLineItem>> oppLinesByOpp = new List<List<OpportunityLineItem>>();
for(Integer o=0; o<10; o++) {
    Opportunity opp = new Opportunity();
    opp.Name = 'Opportunity ' + o;
    opp.StageName = 'Open';
    opp.CloseDate = System.today();
    opps.add(opp);
    List<Product2> products = new List<Product2>();
    List<PricebookEntry> pricebookEntries = new List<PricebookEntry>();
    List<OpportunityLineItem> oppLineItems = new List<OpportunityLineItem>();
    for(Integer i=0; i<o+1; i++) {
        Product2 product = new Product2();
        product.Name = opp.Name + ' : Product : ' + i;
        products.add(product);
        PricebookEntry pbe = new PricebookEntry();
        pbe.UnitPrice = 10;
        pbe.IsActive = true;
        pbe.UseStandardPrice = false;
        pbe.Pricebook2Id = Test.getStandardPricebookId();
        pricebookEntries.add(pbe);
        OpportunityLineItem oppLineItem = new OpportunityLineItem();
        oppLineItem.Quantity = 1;
        oppLineItem.TotalPrice = 10;
        oppLineItems.add(oppLineItem);
    }
    productsByOpp.add(products);
    pricebookEntriesByOpp.add(pricebookEntries);
    oppLinesByOpp.add(oppLineItems);
}
// Insert Opportunities
insert opps;
// Insert Products
List<Product2> allProducts = new List<Product2>();
for(List<Product2> products : productsByOpp) {
    allProducts.addAll(products);
}
insert allProducts;
// Insert Pricebooks
Integer oppIdx = 0;
List<PricebookEntry> allPricebookEntries = new List<PricebookEntry>();
for(List<PriceBookEntry> pricebookEntries : pricebookEntriesByOpp) {
    List<Product2> products = productsByOpp[oppIdx++];
    Integer lineIdx = 0;
    for(PricebookEntry pricebookEntry : pricebookEntries) {
        pricebookEntry.Product2Id = products[lineIdx++].Id;
    }
    allPricebookEntries.addAll(pricebookEntries);
}
insert allPricebookEntries;
// Insert Opportunity Lines
oppIdx = 0;
List<OpportunityLineItem> allOppLineItems = new List<OpportunityLineItem>();
for(List<OpportunityLineItem> oppLines : oppLinesByOpp) {
    List<PricebookEntry> pricebookEntries = pricebookEntriesByOpp[oppIdx];
    Integer lineIdx = 0;
    for(OpportunityLineItem oppLine : oppLines) {
        oppLine.OpportunityId = opps[oppIdx].Id;
        oppLine.PricebookEntryId = pricebookEntries[lineIdx++].Id;
    }
    allOppLineItems.addAll(oppLines);
    oppIdx++;
}
insert allOppLineItems;

Vamos refazer o código acima usando o padrão Unit of Work. Primeiro, comece criando uma instância de Unit of Work. Lembre-se de que os objetos transmitidos ao construtor devem estar na ordem de dependência para que o método commitWork possa inseri-los na ordem correta.

// Create a Unit Of Work
fflib_SObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(
    new Schema.SObjectType[] {
        Product2.SObjectType,
        PricebookEntry.SObjectType,
        Opportunity.SObjectType,
        OpportunityLineItem.SObjectType
    }
);

Na unidade anterior, o método de serviço applyDiscount usou o método registerDirty a fim de registrar os registros Oportunidade e Linha de oportunidade para atualização. O método registerNew insere novos registros. Ao inserir os registros filhos, esse método também faz com que a ID de pai correta seja aplicada antes de inserir os registros filhos. Você pode ver isso na prática na linha 22. Se você tem outros relacionamentos a criar, pode chamar o método registerRelationship (linha 21). Agora, vamos colocar nossa Unit of Work para funcionar! .

// Do some work!
for(Integer o=0; o<10; o++) {
    Opportunity opp = new Opportunity();
    opp.Name = 'UoW Test Name ' + o;
    opp.StageName = 'Open';
    opp.CloseDate = System.today();
    uow.registerNew(opp);
    for(Integer i=0; i<o+1; i++) {
      Product2 product = new Product2();
      product.Name = opp.Name + ' : Product : ' + i;
      uow.registerNew(product);
      PricebookEntry pbe = new PricebookEntry();
      pbe.UnitPrice = 10;
      pbe.IsActive = true;
      pbe.UseStandardPrice = false;
      pbe.Pricebook2Id = Test.getStandardPricebookId();
      uow.registerNew(pbe, PricebookEntry.Product2Id, product);
      OpportunityLineItem oppLineItem = new OpportunityLineItem();
      oppLineItem.Quantity = 1;
      oppLineItem.TotalPrice = 10;
      uow.registerRelationship(oppLineItem, OpportunityLineItem.PricebookEntryId, pbe);
      uow.registerNew(oppLineItem, OpportunityLineItem.OpportunityId, opp);
    }
}

Conforme a unidade anterior, esses métodos não executam operações de bancos de dados. Para isso, você precisa confirmar o trabalho usando o método commitWork, que chama as instruções DML corretas com os registros que foram registrados na ordem correta de filho e, depois, pai. Além disso, no caso de relacionamentos, ele atribui a ID de pai aos registros filhos, costurando seu modelo de objeto conforme vai sendo inserido no banco de dados.

// Commit the work to the database!
uow.commitWork();

Observe que o código não só é mais curto, mas bem mais fácil de ler em termos de lógica, sem todas as listas em volta! Vejamos mais exemplos dos métodos register que você pode usar.

 // Inserts new Opportunity when committing
uow.registerNew(opp);
// Inserts new Opportunity Line Item and associates it with the given Opportunity record when committing
uow.registerNew(oppLineItem, OpportunityLineItem.OpportunityId, opp);
// Relates the given Opportunity Line Item to the given Price Book Entry when committing
uow.registerRelationship(oppLineItem, OpportunityLineItem.PricebookEntryId, pbe);

Resumo

Neste módulo, você aprendeu sobre os benefícios de exibir sua implementação de aplicativo em camadas que têm suas próprias considerações e questões. Assim como qualquer organismo, cada uma tem seu papel em tornar seu aplicativo mais sólido e duradouro.

Você deu o primeiro passo isolando o coração do seu aplicativo, a lógica de negócios. Você pode parar por aqui ou continuar a aplicar questões de separação ao comportamento do objeto (código acionador) e a consultar informações necessárias ao aplicativo. Não deixe de conferir o módulo Padrões corporativos do Apex: camadas de domínio e seletor, que continua com as camadas de aplicativo de domínio e seletor. Por enquanto, divirta-se criando seus serviços!

Preparação para os desafios

Para concluir os desafios, você precisará usar algumas bibliotecas de código aberto. A classe fflib_SObjectUnitOfWork é parte da biblioteca de código aberto Apex Common, que é dependente da biblioteca de código aberto ApexMocks Framework. Você precisará instalar o ApexMocks primeiro e, depois, o Apex Commons. Leia mais sobre essas bibliotecas e respectivos contratos de licença de código aberto em seus repositórios.

Para instalar as bibliotecas em sua organização, basta usar os botões “Implantar” abaixo.

Implantar a biblioteca de código aberto ApexMocks.

Implantar para Salesforce

Implantar a biblioteca de código aberto Apex Common.

Implantar para Salesforce

Recursos

Compartilhe seu feedback do Trailhead usando a Ajuda do Salesforce.

Queremos saber sobre sua experiência com o Trailhead. Agora você pode acessar o novo formulário de feedback, a qualquer momento, no site Ajuda do Salesforce.

Saiba mais Continue compartilhando feedback