SEFAZ-BA SGF DTI GEDES Secretaria da Fazenda do Estado da Bahia Superintendência da Gestão Fazendária Diretoria de Tecnologia da Informação Gerência de Administração de Dados e Desenvolvimento de Sistemas BOAS PRÁTICAS DE PROGRAMAÇÃO WEB Versão 01.00.00 Salvador (Ba), maio de 2017 Secretaria da Fazenda do Estado da Bahia 29/05/17 DTI - Diretoria de Tecnologia da Informação CONTROLE DE VERSÃO Versão Data 01.00.00 01/10/2009 Responsável Equipe AD (Padrões) Página 2 Histórico 1. Versão inicial; 582807064 Secretaria da Fazenda do Estado da Bahia 29/05/17 DTI - Diretoria de Tecnologia da Informação ÍNDICE 1. INTRODUÇÃO ................................................................................................................................. 4 2. CONSIDERAÇÕES GERAIS .......................................................................................................... 5 2.1. 3. BOAS PRÁTICAS .NET ................................................................................................................... 6 3.1. 3.2. 3.3. 3.4. 3.5. 3.6. 3.7. 3.8. 3.9. 3.10. 3.11. 4. ORGANIZAÇÃO DE CÓDIGO ................................................................................................. 5 CONSTANTES .......................................................................................................................... 6 STRINGS .................................................................................................................................... 6 VARIÁVEIS DE SESSÃO ......................................................................................................... 6 PASSAGEM DE PARÂMETROS.............................................................................................. 6 CAMADA DE DADOS .............................................................................................................. 7 DISPOSE .................................................................................................................................... 9 POSTBACK.............................................................................................................................. 10 MAPEANDO CAMINHO ABSOLUTO APLICAÇÃO ........................................................... 10 DATASET E DATAREADER ................................................................................................. 10 NOTHING ................................................................................................................................ 11 XML ......................................................................................................................................... 12 BOAS PRÁTICAS ASP .................................................................................................................. 13 4.1. 4.2. 4.3. 4.4. 4.5. UTILIZAÇÃO DE CSS ............................................................................................................ 13 CÓDIGO HTML LIMPO .......................................................................................................... 13 IMAGENS DE CABEÇALHOS E RODAPÉS DA SEFAZ ..................................................... 13 BARRA DE ROLAGEM .......................................................................................................... 13 COMPATIBILIDADE ENTRE IE E FIREFOX ....................................................................... 13 Página 3 582807064 Secretaria da Fazenda do Estado da Bahia 29/05/17 DTI - Diretoria de Tecnologia da Informação 1. INTRODUÇÃO O objetivo deste documento é fornecer algumas orientações referentes a boas práticas de programação para nortear a manutenção e o desenvolvimento de aplicações da SEFAZ baseadas na arquitetura Web e contribuir para a melhoria de sua qualidade. Ele aborda as tecnologias ASP, VB.NET e JavaScript. Página 4 582807064 Secretaria da Fazenda do Estado da Bahia 29/05/17 DTI - Diretoria de Tecnologia da Informação 2. CONSIDERAÇÕES GERAIS 2.1. ORGANIZAÇÃO DE CÓDIGO A organização de código é um fator muito importante para garantir uma boa legibilidade e entendimento do que foi implementado, facilitando, assim, a sua manutenção. Para tanto, alguns fatores devem ser considerados: A utilização de comentários explicativos facilita o entendimento do código, documentando o que foi feito. Eles devem ser claros e objetivos, de forma que o código não fique poluído. Comentários de documentação: têm por objetivo descrever a funcionalidade. Deve-se utilizar este tipo de comentário nos trechos de código mais importantes para a regra de negócio. Comentários de implementação: fornecem uma explicação sobre a forma de implementação em relação aos controles de fluxo, iterações, recursos da linguagem utilizados e trechos de código que não foram especificados e não fazem parte do negócio, mas sim da solução implementada. Qualquer tipo de código desnecessário, como blocos comentados e variáveis não utilizadas, deve ser retirado para não dificultar o entendimento e a manutenção. Cada comando deve estar disposto separadamente em uma ou mais linhas. A utilização de vários comandos em uma mesma linha compromete a legibilidade do código, tornando-o mais complexo. O recurso Region (#Region em VB .NET), fornecido pelo Visual Studio .Net, permite que os métodos sejam agrupados de acordo com a similaridade de função, o que viabiliza o uso do recurso de recolher e expandir visualmente conjuntos de métodos. Indentação e organização do código em blocos lógicos facilitam a legibilidade e manutenção. Variáveis, constantes, métodos e objetos utilizados na implementação devem ter nomenclatura clara, sempre tentando representar de forma mais intuitiva seu significado de acordo com o negócio ou utilização. A adoção de padrões de nomenclatura para variáveis, constantes, métodos e objetos é muito importante para a organização e entendimento do código. Página 5 582807064 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 3. BOAS PRÁTICAS .NET 3.1. CONSTANTES O uso de constantes é indicado na representação de dados que não sofrem alteração durante a execução do código, evitando valores fixos espalhados, que dificultam muito a manutenção. Uma boa prática é centralizar suas definições e atribuições numa classe separada. Este conceito de constantes globais aplica-se no caso daquelas que são utilizadas por várias classes do projeto e devem ser declaradas como Friend , nunca como Public. 3.2. STRINGS Ao concatenar strings repetidas vezes, deve-se usar o objeto StringBuilder (System.Text.StringBuilder). Isso evitará que, a cada alteração da string, seja alocado um novo bloco de memória. Para máxima performance, deve-se instanciar o StringBuilder com tamanho igual ou maior que a quantidade máxima de caracteres a ser armazenada e nunca reaproveitar o objeto após a extração da string com o método ToString. Se a concatenação foi restrita a até 3 vezes, pode-se usar concatenação de strings com o operador +=, caso contrário se recomenda o uso de StringBuilder. 3.3. VARIÁVEIS DE SESSÃO O programador deve ter bastante cautela quando decidir usar variáveis de sessão na sua implementação, pois elas são armazenadas na memória e o desempenho da aplicação pode ser comprometido, dependendo do número de usuários da aplicação, quantidade e complexidade dos objetos armazenados em sessão. Se a solução realmente precisar deste recurso, deve-se limpar as sessões que não estão mais sendo utilizadas para liberar espaço de memória. Deve-se atentar para o nome dado às variáveis de sessão para que não haja sobreposição de outra variável em outro contexto. 3.4. PASSAGEM DE PARÂMETROS É muito importante indicar, na declaração dos métodos, o tipo de passagem dos seus parâmetros. Existem duas formas de um método receber os parâmetros: por valor (ByVal em VB .NET) e por referência (ByRef em VB .NET). Em ambientes centralizados, todos os objetos são passados por referência, mesmo que na declaração do método esteja explícito que o argumento é ByVal. Em ambientes distribuídos, a passagem de parâmetros ocorre tal como é definida no método. Deve-se ter muita atenção quando os parâmetros forem objetos porque o custo relacionado à passagem por valor é muito alto. Página 6 Boas_Praticas_de_Programacao_Net Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 3.5. CAMADA DE DADOS As classes deste grupo isolam o resto da aplicação de tudo que esteja relacionado à manipulação dos dados. Elas fornecem informações para as regras de negócio de forma mais simplificada e modificam o conteúdo do banco de dados sob a orientação dessas regras, isolando as funcionalidades e os detalhes da implementação física. Em .NET, pode-se usar o ADO.NET, que é um conjunto de assemblies contidos no .NET Framework que tem por objetivo prover a comunicação com os bancos de dados. Como boa prática em termos de arquitetura de aplicação .NET, sugere-se a divisão em camadas funcionais. A utilização de uma camada de dados encapsulando o ADO.NET, separada da camada de regra de negócios, garante a padronização deste componente para as funcionalidades mais comuns, promovendo facilidade de manutenção, extensão e produtividade. É importante, portanto, identificar padrões que resolvam os tipos de problemas mais freqüentes. A depender do banco de dados acessado pela aplicação, o ADO.NET disponibiliza vários “Clientes” de fonte de dados. Para alcançar um melhor desempenho, deve-se escolher o provedor mais adequado à fonte de dados que será utilizada. Abaixo estão algumas orientações sobre os provedores: SQL Server .NET Data Provider: Localizado no namespace System.Data.SqlClient, é recomendado para aplicações que utilizam banco de dados SQL Server 7.0 ou superior. NET Data Provider for Oracle: Localizado no namespace System.Data.OracleClient, é recomendado para aplicações que utilizam banco de dados Oracle. OLE DB .NET Data Provider: Localizado no namespace System.Data.OleDb, é recomendado para aplicações que utilizam banco de dados Microsoft Access ou SQL Server 6.5. ODBC .NET Data Provider: Localizado no namespace Microsoft.Data.Odbc, é recomendado para aplicações que utilizam fonte de dados conectadas via driver ODBC. Esta forma de acesso a dados só deve ser utilizada em último caso. Abaixo estão algumas orientações que devem ser seguidas como boas práticas para acesso a dados com ADO.NET: As conexões devem sempre ser abertas o mais tarde possível e fechadas o mais cedo possível; Sempre utilizar transações em operações que envolvam alterações de dados (UPDATE, INSERT, DELETE) em mais de uma tabela simultaneamente; Sempre que for necessário, atribuir valores numa instrução SQL, seja numa cláusula WHERE seja na atualização de um campo através de um INSERT ou UPDATE, deve-se utilizar parâmetros em vez da concatenação de strings. Desta forma, evita-se erros e ataques de injeção SQL, já que os parâmetros são valores com tipos definidos. Tal prática permite que o SGBD compile o código SQL apenas uma vez, melhorando a performance, já que, após a primeira execução, a instrução estará pré-compilada no banco de dados; Deve-se construir filtros e ordenações diretamente na instrução SQL, através das cláusulas WHERE, ORDER e GROUP, e não via código. As orientações a seguir são para aplicações que utilizam o Framework UNIFW.NET. Página 7 Boas_Praticas_de_Programacao_Net Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 O UniFW auxilia na implementação de componentes de acesso à base de dados de diversos tipos. Componentes como as classes Unitech.Component.UniComponent e Unitech.Data fornecem funcionalidades prontas de abertura de conexão, conversão de resultados de Stored Procedures em DataSets, formatação de dados SQL. As classes da aplicação que necessitam fazer acesso ao banco de dados devem herdar de Unitech.Component.UniComponent: Utilizando este componente, a classe não precisa se acoplar a uma implementação específica do ADO.NET e a string de conexão com o banco de dados não precisa ser mantida e gerenciada em código. O UniComponent utiliza o conceito de origem de dados para identificar o tipo de banco de dados e sua string de conexão através de uma simples sigla. No exemplo a seguir, vê-se o uso do método CreateDataBaseInstance, que recebe um parâmetro denominado “minhaorigemdados”. Dim SGBD As Unitech.Data.IDatabase = CreateDataBaseInstance(“minhaorigemdados”) Esta forma de utilização do método CreateDataBaseInstance se aplica quando os métodos da classe acessam diferentes origens de dados. Esta definição é feita dentro da implementação do próprio método. Quando os métodos da classe acessam a mesma origem de dados, sua chamada dentro dos métodos é feita sem passagem de nenhum parâmetro conforme exemplo abaixo: Dim SGBD As Unitech.Data.IDatabase = CreateDataBaseInstance() Se nenhum parâmetro for passado para o método, será utilizado como origem de dados um valor padrão definido na propriedade “SiglaDataSource” existente no UniComponent. Esta definição deve ser feita no construtor da classe conforme exemplo abaixo. Public Sub New() SiglaDataSource = "minhaorigemdados" End Sub O framework UniFW.Net oferece alguns mecanismos para controle de transações. Através do pacote DAC, é possível controlar o funcionamento das transações (Supported, Required, etc) e também o nível de isolamento entre elas (ReadCommitted, Serializable, etc.). Desta forma, todo método que efetua operações de consulta em banco de dados deve possuir a seguinte configuração antes da sua assinatura: <TransactionContext(Transaction.Supported,Isolation:=Isolation.ReadCommitted)> _ Os métodos que efetuam operações de atualização em banco de dados devem possuir a seguinte configuração antes da sua assinatura: <TransactionContext(Transaction.Required,Isolation:=Isolation.ReadCommitted)> _ Estas configurações fornecem o comportamento que, normalmente, os bancos de dados oferecem como padrão. Se a especificação do software contiver tais informações, deve-se, obrigatoriamente, utilizar as configurações determinadas. O contexto da transação possui quatro possíveis comportamentos: Disabled: Suporte a transação está desabilitada. NotSupported: O objeto nunca participa da transação. Página 8 Boas_Praticas_de_Programacao_Net Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 Supported: O objeto participa da transação, se existir. Required: Se o objeto existir, participa da transação, caso contrário cria uma nova transação. RequiredNew: O objeto sempre cria uma nova transação. O UniComponent fornece aos componentes de negócio/dados a funcionalidade de geração de números seqüenciais, baseada no recurso existente no UniFW.Net, com configuração através de sua interface de administração. Tais seqüenciais únicos podem servir, por exemplo, como chaves primárias. O método GetNewNumber do UniComponent recebe como parâmetro a sigla da seqüência desejada. Segue um exemplo: Dim X as Integer = GetNewNumber(“minhasequencia”) 3.6. DISPOSE Uma característica muito interessante da plataforma .NET é o gerenciamento automático de memória, feito pelo Garbage Collector, que é responsável por liberar os recursos da memória de objetos gerenciados quando eles não estão mais em uso. Apesar do .NET oferecer esta solução, deve-se atentar para o uso eficiente da memória, de forma que o desempenho da aplicação não seja comprometido. Nunca se sabe quando o Garbage Collector vai liberar os recursos que não estão mais sendo utilizados pela aplicação. Além disso, ele não reconhece recursos não gerenciados. Por conta disso, existem no .NET objetos que implementam a interface IDisposable através do método Dispose, que permite ao programador liberar os recursos no momento que precisar, sem depender do finalizador acionado em um momento desconhecido, o que pode comprometer o sistema como um todo por acúmulo de recursos inativos em memória. Recomenda-se que os objetos que implementam a interface IDisposable tenham obrigatoriamente a chamada do método Dispose explicitada. Esta chamada deve ser feita, preferencialmente, na zona Finally de um bloco Try/Catch/Finally, já que todo código presente nesta zona será executado, independentemente da ocorrência ou não de erros. Outro ponto a ser observado é que a chamada do Dispose deve ser feita após a verificação de não nulidade do objeto, já que algum erro pode ter ocorrido no momento da criação da sua instância. Segue abaixo exemplo: Private Sub CarregaDadosTela() Try // Código Imaginário de manipulação do DataSet Finally If Not LdstDados is Nothing then LdstDados.Dispose() End If End Try End Sub Página 9 Boas_Praticas_de_Programacao_Net Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 3.7. POSTBACK O recurso de ViewState está presente na maioria dos controles Web e serve para que estes possam manter, automaticamente, seus valores em caso de atualização da página. Antes da atribuição de valores em controles que possuam este recurso, o programador deve verificar se ocorreu uma atualização na página ou não, para evitar processamento desnecessário por conta de obtenção de valores no banco de dados ou sincronização de valores com controles da tela. O recurso de BookMark do UniFW.NET, por exemplo, mantém seus valores em caso de atualização, devendo estar dentro do teste de PostBack. Recomenda-se utilizá-lo sempre que possível. Abaixo segue exemplo: Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load If Not IsPostBack Then ' Adiciona página ao bookmark Unitech.Web.Util.BookMark.Marcar() Dim LobjClasseBD As ClasseBD = New ClasseBD InnerDataSet = LobjClasseBD.ObterTodos() SyncForm() End If End Sub 3.8. MAPEANDO CAMINHO ABSOLUTO APLICAÇÃO Se, durante o desenvolvimento, houver a necessidade de se mapear o caminho absoluto referente à pasta onde a aplicação Web está localizada, não é indicado o mapeamento fixo no código, porque este caminho pode variar dependendo da estrutura de pastas do ambiente em que a aplicação está rodando. Recomenda-se o uso do método AppDomain.CurrentDomain.BaseDirectory(), existente no FrameWork .NET, para obter esta informação. Este método irá retornar o caminho físico da aplicação que está sendo executada conforme configurado no IIS. 3.9. DATASET E DATAREADER Até a versão 1.1, o ADO.NET fornecia dois objetos para retornar e armazenar dados em memória. Um deles é o DataReader, que fornece um acesso conectado somente-leitura e somente-parafrente a uma fonte de dados. A utilização deste objeto é recomendada quando: A cache de dados não é necessária; Só é necessário trabalhar com uma tabela de dados por vez; Quando os dados são acessados de forma rápida, somente-leitura e somente-para-frente. Ao final da leitura, deve-se fechar o DataReader. Se a conexão existir apenas para retornar o DataReader, deve ser fechada imediatamente depois deste. O DataReader não pode ser trocado entre camadas, somente um pode ser aberto por vez. Página 10 Boas_Praticas_de_Programacao_Net Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 Quando há a necessidade de uso prolongado do objeto DataReader, aconselha-se a opção dos Datasets, que são sempre desconectados (isso aumenta a escalabilidade). O objeto DataSet é uma representação relacional dos dados em memória, incluindo tabelas que contém dados, restrições de dados e relacionamentos entre as tabelas. Os DataSets são estruturas complexas similares a um banco de dados relacional, que provêem um conjunto de objetos dispostos de maneira hierárquica (tabelas, linhas e colunas). Sua utilização é recomendada quando: É necessário manipular dados de Banco de Dados, XML, Planilhas Eletrônicas, etc; A cache de dados é necessária; É necessária a navegação entre múltiplas tabelas de resultados; A aplicação precisa trocar dados entre diferentes camadas. As tabelas de um DataSet e as linhas e colunas de uma tabela podem ser acessadas de duas formas: pelo índice do elemento ou pelo nome do elemento. O acesso por índices não é o recomendado, pois eventuais mudanças na estrutura do DataSet implicam um re-trabalho de correção dos índices, além do custo de processamento no processo de identificação dos locais onde são realizados acessos por índices. Desta forma, recomenda-se que o acesso aos elementos de DataSets seja feito por meio dos seus nomes, conforme exemplo abaixo. LdstExemplo.Tables(NOME_TABELA).Rows(0)("Dominio") = "SEFAZ" LdstExemplo.Tables(NOME_TABELA).Rows(0)("Login") = "almamorim" LdstExemplo.Tables(NOME_TABELA).Rows(0)("Nome") = "Augusto Amorim" Quando for necessário testar a nulidade de campos de DataSets, deve-se utilizar DBNull.Value. Nas aplicações desenvolvidas com o framework UniFW.NET, existe a possibilidade de se utilizar um DataSet chamado InnerDataSet, que existe para armazenamento dos dados obtidos por algum método ou passados a algum método de uma classe de negócio. Ele pode ser utilizado em conjunto com os métodos SyncData e SyncForm. O método SyncData preenche os campos do InnerDataSet com os dados da interface Web; de maneira inversa, o método SyncForm preenche os campos da interface Web com os dados armazenados no InnerDataSet. Os campos na interface devem ter o mesmo nome das colunas no InnerDataSet. Não é preciso passar parâmetros aos métodos e estes devem ser chamados dentro de um IF Not IsPostBack, por questões de performance. 3.10. NOTHING Para verificar se um objeto não foi inicializado (não teve sua instância criada), o VB.NET oferece a palavra reservada Nothing. A comparação com “Is Nothing” só é verdadeira quando o objeto não está inicializado ou quando se trata de um parâmetro para o qual foi passado Nothing. Nothing também representa um valor numérico nulo (zero). A comparação de valores numéricos nulos, entretanto, não deve ser realizada por meio da expressão “= Nothing”, mas sim “= 0”, porque Nothing representa uma referência, e não um valor. Quando se trata de comparações com strings, a expressão “= Nothing” é verdadeira para strings vazias, mas recomenda-se a comparação através da expressão “= String.Empty”. Página 11 Boas_Praticas_de_Programacao_Net Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 3.11. XML Extensible Markup Language é um padrão consolidado no mundo da tecnologia para organizar e descrever informações. Devido à sua essência auto-descritiva (Dados + Metadados), a XML é usada em todas as camadas lógicas e físicas de uma aplicação como meio para a comunicação e a transferência de dados. Em .NET, existe um namespace específico para a rápida manipulação e criação de informação no formato XML: System.Xml Este namespace oferece classes com métodos que executam as principais tarefas relacionadas à manipulação de dados em XML, tais como: 1. Carregar dados XML numa estrutura hierárquica em memória (System.Xml.Serialize). Exemplo: XmlDocument myDoc = new XmlDocument(); myDoc.Load ("c:\\samples\\arquivo.xml"); 2. Pesquisar, no documento, valores ou atributos de um determinado TAG ou característica. Exemplo: XmlNodeList myList; myList = myDoc.SelectNodes ("Book[Author/@lastname='Smith']"); 3. Criar nodes XML e permitir sua inclusão em documentos de maior hierarquia. Exemplo: XmlNode newNode = myDoc.CreateElement ("Book"); myDoc.DocumentElement.AppendChild (newNode); 4. Alterar valores ou atributos. Exemplo: // Incluindo um novo atributo XmlAttribute newAttr = myDoc.CreateAttribute ("Title"); newNode.Attributes.Append (newAttr) // Alterando o valor de um atributo newNode.Attributes["Title"].Value = "Visual Studio.Net"; Página 12 Boas_Praticas_de_Programacao_Net Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 4. BOAS PRÁTICAS ASP 4.1. UTILIZAÇÃO DE CSS Os arquivos de estilo (.CSS) contêm todas as definições de formato necessárias ao desenvolvimento dos protótipos e das aplicações (cores, tamanho e tipo de fontes, título, tamanhos de divs, estilos de botões e componentes, etc.). O ideal é que as páginas contenham somente tags de código html (html, body, div, table, p, br, etc.). Qualquer definição de estilo deve ser importada dos arquivos css e definida em html como classe ou ID. Para maiores informações, consultar o documento Manual de Padrões - Interface WEB. 4.2. CÓDIGO HTML LIMPO O reaproveitamento de protótipos já montados para criação de novos, normalmente, implica sujeira no código, tornando o html pesado, com renderização lenta e suscetível à incompatibilidade entre browsers (IE, Mozila). O código não deve conter excessos (ex: divs dentro de tabela apenas para centralização do conteúdo) e sua correção precisa ser garantida (ex: toda tag aberta deve ser fechada). 4.3. IMAGENS DE CABEÇALHOS E RODAPÉS DA SEFAZ É preciso atentar para a especificação correta do caminho dos diretórios das imagens. Para maiores informações, consultar o documento Manual de Padrões - Interface WEB. 4.4. BARRA DE ROLAGEM Para evitar a barra de rolagem horizontal, deve-se utilizar a classe <div id="conteudo">, que engloba as tabelas, as abas etc. Para maiores informações, consultar o documento Manual de Padrões - Interface WEB. 4.5. COMPATIBILIDADE ENTRE IE E FIREFOX Além de utilizar arquivos de estilo e manter o código limpo, deve-se evitar o uso de componentes JavaScript obsoletos criados antes do surgimento do Firefox. Página 13 Boas_Praticas_de_Programacao_Net