Skip to main content

Aplicar os princípios de camada de serviço no Apex

Objetivos de aprendizagem

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

  • Criar uma classe Service do Apex e usá-la efetivamente em seu aplicativo.
  • Expor uma classe Service do Apex como uma API.
Nota

Nota

Deseja aprender em português (Brasil)? Nesse emblema, as validações dos desafios práticos do Trailhead funcionam em inglês. As traduções são fornecidas entre parênteses como referência No Trailhead Playground, (1) mude a localidade para Estados Unidos, (2) mude o idioma para inglês e (3) copie e cole apenas os valores em inglês. Siga as instruções aqui.

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 38:48 minutos, caso você queira retroceder e ver o início da etapa novamente.)

Como criar serviços

Vamos ver um pouco de código! Se você está usando um encapsulamento total do banco de dados e do gerenciamento de estados, uma das abordagens de implementação é usar uma classe adequadamente nomeada com métodos estáticos que representam as operações do serviço.

Os métodos na sua classe representam as operações de serviço, que acessam as informações necessárias por meio do ambiente e dos parâmetros transmitidos. A lógica no método atualiza o banco de dados ou retorna informações no tipo de retorno do método usando exceções personalizadas do Apex para indicar falha. O exemplo a seguir mostra um serviço aplicando determinado desconto a um conjunto de oportunidades (e itens de linha, se houver).

public with sharing class OpportunitiesService {
   public static void applyDiscounts(Set<Id> opportunityIds, Decimal discountPercentage) {
        // Validate parameters
        if(opportunityIds==null || opportunityIds.size()==0)
            throw new OpportunityServiceException('Opportunities not specified.');
        if(discountPercentage<0 || discountPercentage>100)
            throw new OpportunityServiceException('Invalid discount to apply.');
        // Query Opportunities and Lines (SOQL inlined for this example, see Selector pattern in later module)
        List<Opportunity> opportunities =
            [select Amount, (select UnitPrice from OpportunityLineItems)
             from Opportunity where Id in :opportunityIds];
        // Update Opportunities and Lines (if present)
        List<Opportunity> oppsToUpdate = new List<Opportunity>();
        List<OpportunityLineItem> oppLinesToUpdate = new List<OpportunityLineItem>();
        Decimal factor = 1 - (discountPercentage==null ?0 :discountPercentage / 100);
        for(Opportunity opportunity :opportunities) {
            // Apply to Opportunity Amount
            if(opportunity.OpportunityLineItems!=null && opportunity.OpportunityLineItems.size()>0) {
                for(OpportunityLineItem oppLineItem :opportunity.OpportunityLineItems) {
                    oppLineItem.UnitPrice = oppLineItem.UnitPrice * factor;
                    oppLinesToUpdate.add(oppLineItem);
                }
            } else {
                opportunity.Amount = opportunity.Amount * factor;
                oppsToUpdate.add(opportunity);
            }
        }
        // Update the database
        SavePoint sp = Database.setSavePoint();
        try {
            update oppLinesToUpdate;
            update oppsToUpdate;
        } catch (Exception e) {
            // Rollback
            Database.rollback(sp);
            // Throw exception on to caller
            throw e;
        }       
    }
    public class OpportunityServiceException extends Exception {} 
}

Se aplicarmos a consideração de design de configuração descrita anteriormente, podemos adicionar uma versão sobrecarregada do serviço acima com um parâmetro options, que permite ao chamador instruir o serviço para ignorar a confirmação do trabalho. O retorno dos valores descontados permite ao cliente implementar uma visualização dos descontos que seriam aplicados.

public static List<Decimal> applyDiscounts(
     List<Id> opportunityIds, Decimal discountPercentage, Options config)

A assinatura do método no exemplo completo acima usa uma lista de IDs, que também está de acordo com as considerações de design. No entanto, somente um parâmetro único para o desconto foi usado. O pressuposto é que o mesmo desconto se aplica a todas as oportunidades. No entanto, se for necessário permitir descontos diferentes para cada oportunidade, você pode usar uma classe parameter, como mostrado aqui.

public class OpportunityService {
    public class ApplyDiscountInfo {
        public Id OpportunityId;
        public Decimal DiscountPercentage;
    }
    public static void applyDiscounts(List<ApplyDiscountInfo> discInfos) {
      // Implementation...    }
}

Como expor serviços como APIs

Todo mundo ama APIs! É essencial expor sua lógica de serviço a terceiros por meio de uma API em seu aplicativo para desenvolver um ecossistema sólido de inovação e integrações de parceiros em torno de seus produtos.

Se você considerar sua camada de serviço como totalmente testada, sólida e aberta a qualquer cliente (e por que não estaria, já que você também está usando, não é?), a maneira mais simples de expô-la aos desenvolvedores do Apex é alterar os modificadores de método e classe de public para global. Bingo!

global class OpportunityService {
    global class ApplyDiscountInfo {
        global Id OpportunityId;
        global Decimal DiscountPercentage;
    }
    global static void applyDiscounts(List<ApplyDiscountInfo> discInfos) {
       // Implementation...    }
}

No entanto, nada na vida é tão simples quanto parece. Se você está criando um pacote do AppExchange, o uso de global tem implicações na hora de mudar assinaturas de método entre versões. Entenda bem essas implicações.

Também convém considerar expor sua API a chamadores fora da plataforma, como dispositivos móveis ou Internet das Coisas. Uma forma de fazer isso é por meio do protocolo REST. Veja uma implementação da API REST do Apex personalizada.

@RestResource(urlMapping='/opportunity/*/applydiscount')
global with sharing class OpportunityApplyDiscountResource {
    @HttpPost
    global static void applyDiscount(Decimal discountPercentage) {
        // Parse context
        RestRequest req = RestContext.request;
        String[] uriParts = req.requestURI.split('/');
        Id opportunityId = uriParts[2];
        // Call the service
        OpportunitiesService.applyDiscounts(
            new Set<Id> { opportunityId }, discountPercentage);     
    }
}

A ID da oportunidade é tirada do URI e a porcentagem de desconto é tirada da informação publicada. Assim como no exemplo de JavaScript Remoting na unidade anterior, o tratamento de exceções é deixado a cargo do chamador. A plataforma faz marshalling de exceções na resposta JSON ou XML pertinente.

Pro Tip: considere expor alguns métodos invocáveis para que os usuários do Flow Builder possam acessar sua funcionalidade da camada de serviço sem ter que escrever código. Plataformas são incríveis, não é?

Recursos

Continue a aprender de graça!
Inscreva-se em uma conta para continuar.
O que você ganha com isso?
  • Receba recomendações personalizadas para suas metas de carreira
  • Pratique suas habilidades com desafios práticos e testes
  • Monitore e compartilhe seu progresso com os empregadores
  • Conecte-se a orientação e oportunidades de carreira