Israel Aece

Software Developer

Event Sourcing - Parte 1

Todos temos uma conta bancária. Além das informações cadastrais, muito provavelmente ela também possui o saldo atual, que representa o quanto temos naquele exato momento, podendo ser positivo, negativo ou simplesmente zero.

O saldo, subindo ou descendo, queremos, obrigatoriamente, saber como ele chegou naquele valor. É importante saber o saldo que tínhamos em uma determinada data (retroativa) e o que aconteceu para ele atingir o saldo atual.

Felizmente todos os bancos dão aos seus correntistas o extrato de sua respectiva conta. É com ele que vamos conseguir enxergar o aumento ou diminuição do nosso dinheiro. Posicionado em uma data, identificando o saldo daquele dia, se viermos somando os valores (lembrando que pode ser positivo ou negativo), chegaremos ao saldo atual da conta. Claro, se nada estiver errado.

Tecnicamente falando, é provável que no banco de dados tenhamos uma estrutura de duas tabelas, onde uma teria os dados cadastrais da conta e a outra os lançamentos. A tabela pai provavelmente também irá armazenar o saldo atual da conta e outras características.



A medida em que os lançamentos são inseridos na segunda tabela, a coluna saldo da primeira vai sendo sensibilizado para refletir o saldo atual da conta. O valor atual do saldo vai sendo sobrescrito com o novo valor.

Já está claro que o importante para o dia a dia é o valor que temos disponível, ou seja, o saldo atual. Os lançamentos são necessários, até então, apenas para histórico; na maioria das vezes, eles são dispensáveis para operar a conta corrente (pagar contas, realizar saques, sacar dinheiro, etc.). Agora, considere o exemplo abaixo:



Repare que o saldo atual que temos não corresponde a soma dos lançamentos da segunda tabela. É provável que nosso sistema possua um bug, que afeta diretamente a atualização do saldo atual.

Apesar de, teoricamente, os lançamentos estarem corretos, é difícil diagnosticar o problema, já que não é possível, de uma forma fácil, reproduzir os mesmos eventos, exatamente da forma em que eles aconteceram, para só assim identificar onde e quando o problema de fato ocorreu.

Eis que entra em cena o Event Sourcing. Ao invés de armazenar o estado atual da entidade em colunas e linhas, o uso desta técnica determina que se armazene uma sequência (stream) de eventos, onde cada um deles informa a ação que foi executada sobre a entidade. Quando esses eventos são recarregados, eles são reaplicados contra a entidade, reconstruindo a mesma. Depois de todos eventos processados, a entidade terá, novamente, o seu estado (versão) atual. Neste modelo não fazemos a persistência física das propriedades na base de dados.

Versionamento

Uma das preocupações neste modelo é com o versionamento da entidade. É através de versão que vamos controlar o processamento e aplicação dos eventos. A versão irá garantir que um evento não seja aplicado à entidade depois que outro evento tenha se antecipado e alterado o estado da mesma em relação ao momento em que ela foi construída, ou melhor, carregada.

Isso é conhecido como concorrência otimista. É algo que também já fazemos com o banco de dados relacional, comparando o valor anterior com o atual que está na tabela antes de proceder com o comando de UPDATE ou de DELETE. O controle pode ser feito de várias maneiras, mas para ser simplista, podemos nos ater ao uso de um número inteiro, que vai sendo incrementado a medida em que um novo evento é aplicado.

Snapshots

Ao longo do tempo, mais e mais eventos vão sendo adicionados e, eventualmente, a performance pode ser degradada. Neste caso, uma possível solução é a criação de snapshots, que depois de carregado os eventos, reaplicados na entidade, ela pode disponibilizar métodos para expor o seu estado atual.

Os snapshots também são armazenados e quando a entidade for recarregada, primeiramente devemos avaliar a existência dele antes de reaplicar os eventos. Se houver um snapshot, restauramos o estado da entidade e reaplicaremos os eventos que aconteceram do momento do snapshot para frente, não havendo a necessidade de percorrer toda a sequência de eventos, ganhando assim em performance. Tão importante quanto o estado atual, o snapshot também deve armazenar a versão atual da entidade, pois esta informação é essencial para o correto funcionamento dos snapshots. Essa rotina de criação de snapshots pode ser feita em background, em uma janela de baixa atividade do sistema, podendo ser implementado através de uma tarefa do Windows que roda periodicamente ou até mesmo com um Windows Service. Para a criação de snapshots, podemos recorrer ao padrão Memento, que nos permite expor e restaurar o estado sem violar o encapsulamento do objeto.

Naturalmente o cenário de conta corrente que vimos acima quase se encaixa perfeitamente neste modelo, exceto pelo fato de termos o saldo atual sumarizado e armazenado fisicamente. Vamos então transformar uma simples classe com uma coleção de lançamentos em um modelo que suporte a técnica descrita pelo Event Sourcing. Vale lembrar que isso é "apenas" mais uma forma de persistência. Você pode ter situações que talvez esse modelo seja útil e necessário, já para outros ambientes, pode ser considerado overkill.

MessageQueue e HTTP

Quando trabalhamos com o Microsoft Message Queue, serviço de mensagens fornecido pelo Windows, ele nos permite expor as filas através do protocolo HTTP. Isso permitirá apenas o envio de mensagens através deste protocolo (podendo também ser via HTTPS), e com isso, clientes que estiverem além dos limites da empresa, poderão enfileirar mensagens para o processamento interno de alguma rotina que rodará periodicamente.

Apenas o envio é permitido via HTTP, pois somente a porta 80 é necessária para executar esta ação; para recepcionar a mensagem, é exigido que outras portas também estejam abertas no firewall, e devido a isso, a Microsoft optou por não permitir o recebimento de mensagens sobre estes protocolos.

Para fazer uso deste recurso, ao instalar o Message Queue no computador que receberá as mensagens, você deve marcar a opção de suporte ao protocolo HTTP, conforme é mostrado na imagem abaixo. Vale lembrar que esta opção exige que o IIS (Internet Information Services) também esteja instalado nesta mesma máquina.


Depois de instalado, o Message Queue cria uma diretório virtual dentro do IIS, que servirá o ponto de acesso (HTTP/S) para as filas criadas nesta máquina. Abaixo vemos a fila chamada "Exemplo" criada e o diretório virtual chamado "MSMQ".


Por fim, ao fazer uso da API System.Messaging, podemos apontar para o endereço HTTP, combinando o diretório virtual criado com o nome da fila para qual queremos enviar a mensagem.

using (var mq = new MessageQueue("FormatName:DIRECT=http://localhost/msmq/private$/exemplo"))
    mq.Send("Algo Aqui", MessageQueueTransactionType.Single);

Apesar disso ser possível, considere o uso do WCF com MessageQueue.

Desenvolvimento de UIs

Por mais simples e fina que ela seja, uma das partes mais importantes de um sistema deskop, web ou mobile, é a interface gráfica. Por mais complexo e tecnológico que seja as camadas mais baixas da aplicação, independentemente dos padrões de arquitetura e projeto que utilize, se é uma base de dados relacional ou No-SQL, etc., é a interface com o usuário que receberá a maior parte das interações e é ela que inicialmente é apresentada quando se propõe ou vendo uma solução.

Além da interface ser a principal responsável por capturar informações e devolver respostas (de sucesso ou falha), é ela também quem exibe os dados para que os usuários realizem a análise gerencial e operacional da empresa para qual o sistema foi desenvolvido.

Apesar de existir uma porção de artigos e documentações que descrevem boas práticas para a construção de interfaces, o bom entendimento do negócio garantirá aos desenvolvedores a criação e otimização de telas para que em pouco espaço, sejamos capazes de apresentar poucos dados, mas com muitas informações.

A ideia é repensar a forma como as construímos, tendo em mente a atividade (ou tarefa) que o usuário irá desempenhar naquela tela. Entender a essência do problema é crucial para o sucesso da criação das telas, e o usuário que é o responsável por executar a tal atividade dentro da empresa é a melhor pessoa com quem se pode falar para extrair as dificuldades/exigências. Em seguida, temos que tentar abstrair tudo de importante que foi colhido, e por fim, implementar.

Abaixo estão algumas técnicas que utilizamos na construção de aplicações para a empresa em que trabalho. A ideia aqui é exibir alguns conceitos, dificuldades e soluções que foram encontradas e implementadas. Apesar dos exemplos serem aplicações desktop (WPF) e web, eles não se limitam a estes cenários; nada impede de utilizar estes conceitos na construção de um aplicativo móvel.

Observação: clique nas imagens para visualiza-las em um tamanho maior.

Dashboards

Todos os funcionários da empresa têm atividades diárias. Algumas pessoas estão condicionadas a executar sempre as mesmas tarefas, analisar os mesmos dados. Se conseguirmos mapear tudo o que ele deve olhar e monitorar, podemos criar um painel que concentra todas as informações que coordenarão o seu dia.

Bem-vindo aos dashboards. Assim como o painel de um carro, que centraliza todas as informações relevantes para a condução e monitoramento do mesmo, os dashboards em uma aplicação tem o mesmo sentido. Se começar a setorizar a sua empresa, irá perceber que cada departamento terá ações distintas (muitas vezes, sobre os mesmos dados), e antes dele sair analisando um a um para saber se há problemas, porque já não apontar as divergências para que ele resolva?

Além disso, os dashboards muitas vezes exibem informações e índices sobre a saúde de algum recurso da empresa, sobre dados financeiros, etc., para que pessoas de nível gerencial possam acompanhar e tomar as decisões cabíveis para cada situação. Reportar periodicamente tais informações garante que ele possa reagir assim que detecta algo suspeito.



Análises

Além dos dashboards que dão uma visão mais panorâmica sobre certos aspectos, há telas que podem concentrar informações mais detalhadas a respeito de algo ou alguém. Por exemplo, qual o risco atual do cliente XYZ? Qual a posição do meu estoque?

Quando colocamos várias informações lado a lado, temos que de alguma forma evidenciar algumas delas, separando muitas vezes por severidade. Existem índices que servem como histórico, enquanto outros indicam a quantidade e total de títulos vencidos que a empresa possui. Saber diferenciar entre essas duas informações garantirá o sucesso da tela, pois é com essa segmentação do que é ou não crítico que vamos utilizar para desenhar e destinar os espaços necessários para cada seção.

O tamanho da fonte é um importante aliado no momento em que precisamos destacar certos valores. A parte superior da tela sempre me pareceu o primeiro ponto em que olhamos quando a mesma é aberta. E aproveitando este instinto, podemos concentrar em uma linha horizontal as informações mais críticas do cliente, enquanto o restante da tela exibe informações de menor severidade, mas não tão menos importantes.



É claro que isso não é uma regra. Você pode optar por utilizar o canto direito, canto esquerdo, etc., para concentrar informações de análise acerca do que está olhando naquele momento. Abaixo está um outro cenário, agora web, que apresenta a situação atual de uma empresa na mesma tela em que exibe os seus títulos em aberto e algumas outras informações de histórico.



Por fim, é importante dizer que nada é estático. A maioria dos índices podem ser (e na maioria das vezes serão) clicáveis. O que quero dizer é que ao clicar no total de vencidos, será apresentado uma tela contendo os títulos que compõem aquele valor.

Repare que essa forma muda como o usuário pensa. Algumas vezes, tudo o que o usuário quer saber é quanto a empresa XYZ está devendo; não importa quantos títulos são. Em geral, o que se apresenta é uma tela contendo vários tipos de filtros, onde um deles é o código/CNPJ da empresa em questão, que ao clicar em filtrar, os 4822 títulos são listados.

Neste outro modelo proposto, basta ele chegar até a empresa e já terá uma sumarização, as pendências não concluídas, sendo possível apontar os títulos que estão vencidos (que ele precisa atuar).

Cores

O tamanho da fonte não é o seu único aliado no momento de destacar informações. As cores também podem representar muita coisa. Além do verde (positivo), vermelho (negativo), azul (indiferente) e amarelo (alerta), podemos abusar das mais diversas cores para indicar algum comportamento, evolução ou tendência de alguma informação.

As cores não se restringem apenas aos índices. Você pode optar por colorir diversas partes do sistema, como por exemplo, o contorno de uma janela que exibe a nota daquele cliente. Repare que a nota, o número propriamente dito, começa a ser irrelevante, já que todo o contexto gráfico indica que aquele é um cliente ruim, independente da escala utilizada (1 a 5, 1 a 10, 10 a 100, etc.).





Gráficos

Gráficos são recursos que cabem na maioria das aplicações. O importante é sempre ter um bom componente à mão para que seja fácil a criação, já que gráficos podem ser criados aos montes, cruzando diversos tipos de informações.



O maior desafio não é a parte técnica, mas sim as informações que compõem o gráfico. Com o conhecimento do negócio é possível que você sugira algumas opções predefinidas, mas é o usuário, com o conhecimento e experiência, que pode indicar quais os melhores gráficos para enriquecer a aplicação.

Sempre há uma discussão em torno disso, que é: criar manualmente cada gráfico ou disponibilizar os dados para que alguma ferramenta externa (Excel, Power BI, etc.)? É uma decisão difícil, pois a construção de gráficos é estática, ou seja, se quiser um novo gráfico, precisamos criar uma nova tela, extrair os dados e exibir, compilar e redistribuir, ao contrário dessas ferramentas específicas, que recebem um conjunto de dados e você molda como preferir.

Ao meu ver, a vantagem de ter o gráfico dentro da aplicação é a possibilidade de inserir o mesmo em um contexto específico, como por exemplo, colocar lado a lado com outras informações que estão sendo analisadas no momento da aprovação de alguma operação.



Contexto

Informações de contexto são sempre bem-vindas quando está analisando ou tomando alguma decisão. São informações que estão conectadas de alguma forma com o que está sendo analisado, e não estou me referindo à banco de dados aqui. Situações onde você está avaliando o crédito para uma empresa e vê que ela está devendo R$ 200.000,00 no mercado, e com apenas um clique, você consegue chegar rapidamente a lista de credores e seus respectivos valores.

Elas podem estar na mesma tela de análise, mas mesmo que haja espaço suficiente para ela, sacar a informação de algum outro lugar pode ser mais interessante, pois evita o risco de poluir demasiadamente a tela. Temos que ponderar entre a discrição e a facilidade de acesso à informação. Considere o exemplo abaixo, onde há uma operação de crédito sendo analisada, qual já possui diversos parâmetros, mas se o usuário desejar, pode acessar as últimas operações realizadas para definir a taxa que será aplicada à operação corrente. A janela que aparece é apenas uma tooltip.



Interação

Nem sempre as aplicações têm uma área de ajuda, pois não é algo fácil de fazer e, principalmente, de manter. Muitas vezes ela nunca é utilizada, pois é mais fácil ligar para alguém e perguntar sobre a dificuldade em fazer alguma atividade dentro do sistema.

Ao invés de criar uma área de ajuda, que concentra 100% das informações e detalhamento de como utilizar seções do sistema, a forma mais simples e intuitiva seria espalhar pelas telas da aplicação informações que ajudem o usuário a esclarecer possíveis dúvidas que ele tenha no momento da realização de uma determinada tarefa.



Repare que com poucas palavras é possível esclarecer um termo que à primeira vista parece ser complexo e confunde o usuário na escolha. A mesma regra pode se aplicada na descrição de um link que ele pode estar na dúvida em qual deles escolher:



Feedback

Reportar falha de execução de alguma atividade (seja de infraestrutura ou de negócio) é fácil, pois basta indicar o motivo do erro e pedir para o usuário tentar novamente. Mas o grande desafio é notificar, de forma simples e objetiva, a conclusão da mesma.

Não estou falando aqui de "cadastro realizado com sucesso", "operação enviada para análise", etc., mas sim de exibir detalhes precisos da atividade que o usuário acabou de realizar. Quando você dá entrada em algum órgão, é comum eles darem um protocolo indicando o número, a data, o prazo de conclusão, o nome, etc. Poderíamos utilizar a mesma regra aqui, ou seja, quando a atividade for concluída, podemos exibir na tela detalhes que o deixem seguro de que a mesma foi executada com sucesso.



É claro que isso nem sempre é possível ou viável. Processos simples também demandam mensagens mais simples. Mas considere mesmo estes casos simples para ser mais esclarecedor com o usuário, indicando com mais ênfase o que acabou de ser realizado. Opte por "A empresa teve a sua razão social alterada de Israel Aece-ME para Israel Aece Ltda." ao invés de "Alteração realizada com sucesso.".

Foco em Tarefas

No nível mais baixo, sabemos que tudo o que um sistema faz em uma base de dados é inserir, atualizar, excluir e ler os dados(CRUD). Só que se ficarmos preso a este modelo de trabalho, vamos construir telas que simplesmente farão estas quatro operações básicas, orientando elas a serem voltadas para a estrutura do nosso banco de dados.

Só que as telas podem dizer muita coisa sobre o comportamento e as atividades que devemos fazer com os dados, que nem sempre se referem à apenas uma única entidade. Muitas vezes a atividade é composta de diversas outras subatividades, que contribuem para a totalidade de uma tarefa a ser executada.

Imagine que você tem um cliente e ele está bloqueado de operar. A implementação mais trivial possível no banco de dados é ter uma tabela chamada "Cliente" com um flag chamado "Bloqueado". Como seria a construção da tela que desbloqueia o cliente?

Geralmente temos um formulário que você edita todos os dados, e nele está incluído um checkbox que está associado à coluna "Bloqueado". Ao clicar no botão atualizar, os dados são persistidos na base de dados e o cliente está apto a operar novamente. Mas essencialmente será que o desbloqueio seria apenas isso? Será que o desbloqueio não seria algo muito maior, que depende que alguém solicite e outra pessoa seja responsável por analisar o motivo e autorizar o não o desbloqueio? Será que não precisamos de histórico de alteração? Qual a justificativa para o desbloqueio? Qual a razão de aceitar o desbloqueio?



Isso é um exemplo de uma tarefa que você executa contra o seu cliente, ou seja, solicitar o seu desbloqueio. Não é um simples formulário de edição. Se você pensar orientando à tarefas que podem ser executadas naquela entidade, é provável que nem precise de um formulário para isso. Aos poucos você começa a perceber que as telas da aplicação desencadeiam ações.



Suas telas deixam de serem formulários baseados na base de dados. Elas possam a ser desenhadas de acordo com a atividade que aquele usuário deve desempenhar. Se ele é responsável por aprovar a alteração de vencimento de boletos, então podemos relacionar os que estão atualmente na sua alçada e permitir com que ele possa selecionar os que deseja aprovar e escolher a opção de conclusão.



Relatórios

É uma tentação por parte dos usuários. Sempre quando eles querem visualizar alguma massa de dados, eles falam em "relatórios". Para mim, relatório é o que se imprime, ou seja, aquilo que se manda para a impressora. Mesmo que relatórios sejam indispensáveis para aplicações, não quer dizer que eles devem ser utilizados como forma de exibir um conjunto de dados em tela.

Algumas aplicações, por questões de simplicidade, utilizam relatórios para exibição, por exemplo: listar todos os títulos vencidos do Israel, clientes com divergências cadastrais, empresas localizadas em São Paulo, etc. Apesar de isso ser possível, torna a relação estática, ou seja, não é possível clicar no cliente que está sem endereço e ajustar. Teria que imprimir, abrir a tela de cadastro de clientes, localizar o mesmo, e em seguida, fazer a alteração.

Como provavelmente existe uma pessoa responsável pelo cadastro dos clientes, então no dashboard dela poderia existir um índice que indica a quantidade de clientes com divergências, e ao clicar, uma tela seria apresentada com esta relação; bastaria dar um clique e já ajustar o endereço e ao sair, a relação já seria sensibilizada e o cliente recém alterado iria desaparecer. As telas tem um grande poder, pois pequenas sumarizações servem também de filtro para uma listagem, sem a necessidade de ter um formulário de pesquisa para isso.



Outro ponto importante a ser considerado é segurança. A impressão de relatórios permite com que algum funcionário mal-intencionado imprima e entregue a relação de clientes para um concorrente. Não que com a tela seja impossível de acontecer, mas chama mais atenção alguém fotografando a tela do computador.

Os relatórios precisam ser pensados com cautela, pois como disse acima, é uma tentação para os usuários. É importante entender a essência do problema, focando na atividade que vem após a impressão. Isso nos dará a oportunidade de construir uma tela que atenda a necessidade deles sem a impressão física da relação de dados.

E claro, não há como escapar de desenvolver relatórios. E sem querer se contraditório, eles são sempre bem-vindos.



A ideia deste artigo foi apenas mostrar algumas técnicas que utilizamos no desenvolvimento das aplicações para empresa onde trabalho. Não há nenhuma regra que deve ser assegurada aqui. São apenas experiências que foram vivenciadas e que a escolha, no meu ambiente, se fez válida. Use como quiser.

Utilizando o HSTS

Quando falamos em proteger a comunicação entre a aplicação web e os clientes que a consomem, logo pensamos em HTTPS. Este protocolo tem a finalidade de criar um túnel entre as partes, garantindo com que as informações trafeguem entre os dois pontos mantendo a confidencialidade (ninguém além dos dois consegue visualizar) e a integridade (ninguém consegue alterar).

Quando habilitamos este protocolo na nossa aplicação e também no hosting que hospeda a mesma, não é 100% garantido que a aplicação será acessada através de HTTPS, colocando em risco a segurança, pois alguém no meio do caminho pode interceptar a requisição e explorar todo o seu conteúdo, e o pior, começar a direcionar o usuário para sites maliciosos.

Para um teste simples, considere o site do Banco Itaú. Muitas vezes as pessoas não se atentam em acessar o site digitando o https:// no navegador. Se monitorarmos a requisição para o endereço http://www.itau.com.br, nós veremos que o navegador recebe como resposta o código 301 (Moved Permanently), obrigando o navegador a encaminhar o cliente para o endereço seguro, que é o https://www.itau.com.br.



Ao receber este tipo de resposta e houver alguém interceptando a comunicação, ele pode substituir o endereço do redirecionamento e, consequentemente, nos mandar para um site malicioso, que simula a aparência do banco, e rouba todos os nossos dados. Para evitar isso, um novo recurso chamado HTTP Strict Transport Security (ou apenas HSTS) foi criado e permite à uma aplicação web indicar que ela somente pode ser acessada através do protocolo HTTPS, e em conjunto com o navegador, assegurarão que independentemente do que se tente fazer, a requisição sempre será feita (e as vezes até modificada para realizar) através do protocolo seguro. Para o servidor indicar ao navegador a obrigatoriedade da utilização do HTTPS, ele deve incluir no cabeçalho da resposta o item (chave) Strict-Transport-Security, indicando o tempo (em segundos) em que o cabeçalho será mantido do lado do cliente.

A especificação também contempla um parâmetro neste cabeçalho chamado includeSubDomains, que como o próprio nome indica, subdomínios deste site deverão ter a garantia de que também serão acessíveis somente através do HTTPS.

No ASP.NET 5, que é independente de hosting, podemos incluir este cabeçalho através de um middleware. É importante dizer que segundo a especificação, este cabeçalho só deve ser enviado ao cliente quando ele já estiver dentro de uma conexão protegida. E esta validação pode ser feita através da propriedade IsHttps que é exposta pelo objeto que representa a requisição.

public class HstsMiddleware
{
    private readonly RequestDelegate next;
    private readonly TimeSpan maxAge;

    public HstsMiddleware(RequestDelegate next, TimeSpan maxAge)
    {
        this.next = next;
        this.maxAge = maxAge;
    }

    public async Task Invoke(HttpContext context)
    {
        if (maxAge > TimeSpan.Zero && context.Request.IsHttps)
            context.Response.Headers.Append(
                "Strict-Transport-Security", $"max-age={maxAge.TotalSeconds}");

        await next(context);
    }
}

public static class HstsMiddlewareExtensions
{
    public static IApplicationBuilder UseHsts(this IApplicationBuilder builder, TimeSpan maxAge) => 
        builder.UseMiddleware<HstsMiddleware>(maxAge);
}


Alternativamente você também pode recorrer ao IIS para realizar a configuração do HSTS, e permitir com que os recursos que são disponibilizados por ele também sejam protegidos por HTTPS. A solução foi proposta pelo Doug Wilson e consiste em criar uma regra para sobrescrever o protocolo HTTP para HTTPS utilizando o arquivo de configuração da aplicação ou do servidor.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name="HTTP to HTTPS redirect" stopProcessing="true">
                    <match url="(.*)" />
                    <conditions>
                        <add input="{HTTPS}" pattern="off" ignoreCase="true" />
                    </conditions>
                    <action type="Redirect" url="https://{HTTP_HOST}/{R:1}"
                        redirectType="Permanent" />
                </rule>
            </rules>
            <outboundRules>
                <rule name="Add Strict-Transport-Security when HTTPS" enabled="true">
                    <match serverVariable="RESPONSE_Strict_Transport_Security"
                        pattern=".*" />
                    <conditions>
                        <add input="{HTTPS}" pattern="on" ignoreCase="true" />
                    </conditions>
                    <action type="Rewrite" value="max-age=31536000" />
                </rule>
            </outboundRules>
        </rewrite>
    </system.webServer>
</configuration>

Preload List

Apesar de garantir que todos os recursos da aplicação serão acessados através do HTTPS, o primeiro problema ainda persiste, ou seja, a requisição inicial para o site do banco pode continuar sendo feita através de HTTP, e a possibilidade de interceptação continua. Para evitar isso, o Google mantém um site (HSTS Preload List) em que podemos adicionar URLs que emitem este cabeçalho, e obviamente, possuem um certificado válido. Isso permitirá ao navegador antes de fazer a requisição para o site avaliar se ele está contido na lista, e se estiver, já transformará a primeira requisição em HTTPS.

Depois da primeira requisição feita, o navegador já é capaz de detectar a necessidade de acessar a aplicação através de HTTPS, e ao acessa-lo via HTTP, o próprio navegador faz um redirecionamento interno (307 - Internal Redirect) e já faz a requisição com HTTPS. É possível ver esse comportamento na imagem abaixo.

Documentação de APIs com Swagger

Há algum tempo eu escrevi aqui sobre o ApiExplorer, que é uma funcionalidade que nos permite expor uma documentação sobre a nossa API. Basicamente ela consiste em nos fornecer metadados dos controllers, ações, parâmetros e resultados para que assim possamos criar uma interface amigável com o usuário (leia-se outro desenvolvedor).

Apesar de termos certa flexibilidade na criação e exibição da documentação, o nosso foco sempre é desenvolvermos a funcionalidade proposta pela API, enquanto a documentação sempre fica em segundo plano, e que na maioria das vezes, nem sempre ganha a atenção que merece. Vale lembrar que é isso que o mundo externo irá utilizar para estudar antes de consumir.

Existe uma especificação chamada Swagger, que tenta padronizar as informações extraídas da APIs Web e, consequentemente, expor de uma forma intuitiva e com bastante riqueza ao mundo. Além do padrão, ainda existe um conjunto de ferramentas (consulte o site oficial), e entre elas, tenho a Swagger UI, que consiste em um conjunto de documentos CSS, HTML e Javascript, e que dinamicamente geram uma interface rica com todas as informações extraídas da API, incluindo o formato das mensagens de requisição e resposta, e ainda, uma opção para já invocar as ações que estão disponíveis, sem precisar recorrer à outras ferramentas, tais como Fiddler. Como um diferencial, os parâmetros já são mapeados e consistidos pela própria interface, garantido que os tipos estejam de acordo com o esperado pela API.

Vale lembrar que o Swagger é completamente independente de plataforma, e não é algo exclusivo do .NET ou da Microsoft. Felizmente existe uma pacote chamado Swashbuckle, que nada mais é que uma implementação desta especificação para Web APIs escritas com o ASP.NET, e que nos bastidores, recorre ao ApiExplorer para gerar a documentação neste - novo - padrão. Por fim, com uma simples linha de código, é possível fazer com que a nossa API tenha uma documentação extremamente rica. Para utiliza-la, basta adicionar o pacote através do Nuget, conforme vemos no comando abaixo:

PM> Install-Package Swashbuckle

Depois de instalado, podemos perceber a criação de uma arquivo chamado SwaggerConfig.cs, que já habilita as configurações mais básicas para o seu funcionamento. Abaixo temos a versão simplificada dela, mas se reparar na classe gerada pela instalação do pacote, há uma série de comentários que são bastante autoexplicativos em relação a cada funcionalidade que ela expõe.

public class SwaggerConfig
{
    public static void Register()
    {
        var thisAssembly = typeof(SwaggerConfig).Assembly;

        GlobalConfiguration.Configuration
            .EnableSwagger(c => c.SingleApiVersion("v1", "Minha API"))
            .EnableSwaggerUi();
    }
}

Com essa configuração realizada, ao rodar a aplicação e colocar /swagger depois do endereço (url) onde está rodando o serviço, teremos a interface com os métodos da nossa API sendo exibidos.



E ao clicar em um dos botões "Try It Out", mais detalhes da requisição e da resposta serão apresentados, e também a ação será invocada na API. Note que se estivéssemos optando por testar uma ação que estivesse sendo exposta através do método HTTP POST, um textarea seria exibido para que você colocasse o corpo da requisição (em JSON ou XML, você escolhe) para assim enviar ao serviço.



Guardando-se as devidas proporções, para quem trabalha ou já trabalhou com SOAP, pode notar certa semelhança com o WSDL (Swagger) e com o SOAP UI (Swagger UI), onde o que vimos aqui, é voltando para o ambiente REST.

Integração do IIS com o DNX.exe

Como sabemos, o ASP.NET 5 é desenvolvido para ser independente de hosting, ou seja, podemos hospedar as aplicações no IIS, via self-hosting (WebListener) e com o, novo hosting, Kestrel, que é multi-plataforma. Em uma nova atualização que a Microsoft liberou do ASP.NET 5 (beta8), foi realizada uma mudança na forma como as aplicações ASP.NET 5 rodam no IIS.

Até então, a Microsoft utilizava um componente chamado "Helios" que servia como ponte entre a estrutura existente do .NET dentro IIS com este novo modelo do ASP.NET 5. Esse componente tinha uma série de responsabilidades, entre elas, fazer a configuração e gestão do runtime (DNX).

A partir da beta8, a Microsoft passou a utilizar o HttpPlataformHandler, que é um módulo nativo (código não gerenciado) que quando instalado no IIS, permite que as requisições HTTP que chegam para ele sejam encaminhadas para um outro processo. E, no caso do ASP.NET 5, esse processo é o dnx.exe (ambiente de execução do ASP.NET 5). Isso garantirá à Microsoft e ao time do projeto, o controle do hosting e a possibilidade de integrá-lo facilmente em ambientes Windows que já utilizam o IIS, tirando o proveito das funcionalidades que ele já disponibiliza. Depois de instalado, já é possível visualizarmos este módulo no IIS:



Quando criamos um novo projeto no Visual Studio, podemos perceber que dentro da pasta wwwroot haverá um arquivo web.config. Vale lembrar que o ASP.NET não utiliza mais este tipo de arquivo para centralizar as configurações; web.config é também o arquivo de configuração do IIS. Dentro deste arquivo temos a configuração do HttpPlataformHandler, que é onde informamos o processo para qual a requisição será encaminhada. Note que o arquivo referencia variáveis de ambiente, mas quando o deployment é feito, ele subistitui pelo endereço até o respectivo local.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <handlers>
      <add name="httpPlatformHandler"
           path="*"
           verb="*"
           modules="httpPlatformHandler"
           resourceType="Unspecified"/>
    </handlers>
    <httpPlatform processPath="%DNX_PATH%"
                  arguments="%DNX_ARGS%"
                  stdoutLogEnabled="false"
                  startupTimeLimit="3600"/>
  </system.webServer>
</configuration>

É importante dizer que o comando web continua apontando para o hosting Kestrel. Por padrão, existe um pacote já adicionado chamado Microsoft.AspNet.IISPlatformHandler. Ele é responsável por fornecer um middleware que captura os headers customizados que o IIS passa para o DNX e inclui na coleção de headers da requisição que foi criada dentro deste ambiente.

{
  "dependencies": {
    "Microsoft.AspNet.IISPlatformHandler": "1.0.0-beta8"
  },

  "commands": {
    "web": "Microsoft.AspNet.Server.Kestrel"
  }
}

Esse middleware é adicionado através do método UseIISPlatformHandler, e faz a cópia de alguns cabeçalhos (X-Forwarded-Proto, X-Original-Proto, etc.) utilizados para acompanha-la durante o repasse entre os ambientes. Ele também já faz a mescla da identidade (ClaimsPrincipal) repassada do IIS com a identidade da aplicação, caso o usuário já esteja autenticado deste lado.

Quando a aplicação é instalada no IIS, como falando acima, o web.config sobre algumas mudanças e ajusta os caminhos para o endereço de onde está o executável do DNX:

<httpPlatform processPath="..\approot\web.cmd" ... />

Finalmente, ao rodar a aplicação, é possível percebermos que o dnx.exe está abaixo do w3wp.exe, que é o processo que o IIS utiliza para rodar cada pool de aplicação. As funcionalidades fornecidas pelo IIS continuam disponíveis, como reciclagem do processo, inatividade, etc. O interessante também é que o pool não precisa mais rodar sobre código gerenciado, e ele também não será mais o responsável por fazer o carregando do runtime (CLR). Isso passa a ser responsabilidade do processo que está abaixo dele. Nem mesmo as funcionalidades do ASP.NET fornecidas pelo Windows precisam ser mais instaladas; a aplicação já é toda "auto-contida" (código, pacotes e runtime).


Request Features no ASP.NET

O ASP.NET 5 está sendo construído para ser completamente independente do hosting em que ele está sendo executado, permitindo assim, que este tipo de aplicação seja também levado para outras plataformas (Linux e Mac). Atualmente o ASP.NET 5 pode ser hospedado no IIS (incluindo a versão Express), via self-host (WebListener) ou através do Kestrel (que é um servidor multi-plataforma baseado no libuv.

Mesmo que seja independente do hosting, o ASP.NET fornece um conjunto de tipos que permite interagir, de forma genérica e abstrata, com as funcionalidades que são expostas pelo HTTP, e que na maioria das vezes, são criadas e abastecidas pelo receptor da requisição, ou seja, o próprio hosting. Esses tipos são chamados de request features, e estão incluídas no pacote chamado Microsoft.AspNet.Http.Features. Entre os tipos (funcionalidades) disponíveis temos as interfaces IHttpRequestFeatureIHttpResponseFeature, que representam o básico para o funcionamento de qualquer servidor web, que é receber a requisição, processar e devolver o resultado. Um outro exemplo é a interface ITlsConnectionFeature, que pode ser utilizada para interagir com os certificados enviados pelo cliente. Para uma relação completa, consulte a documentação.

Cada um dos servidores disponíveis já implementam as interfaces definidas (desde que eles suportem), e nos permite acessar essas funcionalidades durante a execução da aplicação, incluindo a possibilidade de acesso através de qualquer middleware. A classe HttpContext expõe uma propriedade chamada Features, que nada mais é que uma coleção das funcionalidades que são incluídas (e, novamente, suportadas) pelo hosting onde a aplicação está rodando. Essa coleção fornece um método genérico chamado Get<TFeature>, onde podemos definir a funcionalidade que queremos acessar. Abaixo um trecho do código de exemplo:

public Task Invoke(HttpContext httpContext)
{
    var clientCertificate =
        httpContext.Features.Get<ITlsConnectionFeature>()?.ClientCertificate;

    if (clientCertificate != null)
    {
        //...
    }

    return _next(httpContext);
}

Além da possibilidade de extrair alguma funcionalidade, é possível também incluir funcionalidade durante a execução, e como já era de se esperar, basta utilizar o método, também genérico, Set<TFeature>. E o mais interessante é que não estamos restritos a utilizar uma das interfaces expostas pelo ASP.NET; podemos criar novas funcionalidades e incorporá-las na execução.

Considere o exemplo abaixo, onde criamos uma funcionalidade para simular a proteção da requisição por uma transação. Basta utilizarmos uma interface para descrever a funcionalidade e criar uma classe que a implemente.

public interface ITransactionFeature
{
    string TransactionId { get; }
    void Commit();
    void Abort();
}

public class TransactionFeatureContext : ITransactionFeature
{
    public string TransactionId { get; } = Guid.NewGuid().ToString();

    public void Commit()
    {
        //...
    }

    public void Abort()
    {
       //...
    }
}

A classe TransactionFeatureContext faz toda a gestão da transação, desde a sua criação (identificando-a) e expõe métodos para conclusão com sucesso (Commit) ou falha (Abort). Repare que tanto a interface quanto a classe não referenciam nenhum tipo do ASP.NET; estamos livres para determinarmos toda a estrutura da nossa funcionalidade. Só que estas classes precisam ser incorporadas na execução, e para isso, vamos criar um middelware que incluirá esta funcionalidade na coleção de Features do contexto da requisição.

public class TransactionMiddleware
{
    private readonly RequestDelegate _next;

    public TransactionMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext httpContext)
    {
        var transactionContext = new TransactionFeatureContext();
        httpContext.Features.Set<ITransactionFeature>(transactionContext);

        try
        {
            await _next(httpContext);
            transactionContext.Commit();
        }
        catch
        {
            transactionContext.Abort();
            throw;
        }
    }
}

O middleware é criado é os middlewares internos que serão invocados a partir dele estarão sendo gerenciados pela transação. Qualquer exceção não tratada que ocorra, a transação será automaticamente abortada. Por fim, depois do middleware criado, incluímos ele na aplicação através do método UseMiddleware.

Vale lembrar que a ordem em que se adiciona os middlewares são importantes. Como a classe TransactionMiddleware adiciona a feature TransactionFeature que criamos, os middlewares que vem a seguir podem acessar a funcionalidade através do método Get<TFeature>, conforme falado acima. Opcionalmente podemos criar na interface métodos para que os middlewares da sequência possam votar no sucesso ou falha e este, por sua vez, avalia os votos antes de chamar o método Commit.

public void Configure(IApplicationBuilder app)
{
    app.UseStaticFiles();
    app.UseTransactionMiddleware();
    app.UseClientCertificateLogMiddleware();
    app.UseMvc();
}

Convenções no ASP.NET

Durante a construção e evolução do ASP.NET MVC e Web API a Microsoft foi assumindo algumas regras, que durante a execução, os frameworks alteram e geram informações de acordo com o código escrito por nós. Isso é conhecido como convention-over-configuration, e tem como foco diminuir algumas configurações explícitas por parte do desenvolvedor, focando em detalhes mais importantes para atender o negócio e o framework se responsabiliza por realizar as configurações necessárias para o funcionamento daquele código respeitando as tais convenções.

Apesar de as vezes não notarmos, isso está bastante presente em nosso dia a dia. Quando criamos um controller, dentro dele uma ação e esta, por sua vez, retornarmos uma view, o ASP.NET MVC assume a estrutura física de Views/NomeDoController/NomeDaAcao.cshtml para armazenar o arquivo e localiza-lo para exibir para o cliente. Outro exemplo é quando utilizamos o Entity Framework e queremos que a entidade Cliente seja persistida na base de dados; o ORM irá criar, automaticamente, a tabela com o mesmo nome daquela entidade. Já no ASP.NET Web API, se criarmos uma ação com o método chamado Get, ele estará automaticamente acessível através do verbo GET do HTTP, sem precisar decorar o mesmo com o atributo HttpGetAttribute.

Note que nos dois exemplos a Microsoft assumiu essas regras e as asseguram durante a execução, ficando, de certa forma, transparente para nós. Se não houvesse isso, teríamos que configurar as mesmas coisas em 100% dos casos; com essas convenções predefinidas, precisamos apenas nos preocupar quando queremos algo diferente do padrão.

O ASP.NET MVC e Web API expõem tipos para que possamos customizar o framework de acordo com nossas necessidades. Até então, isso poderia ser realizado customizando alguns serviços de baixo nível (controller factory, por exemplo), mas o problema desta técnica é que estaremos tocando em outras atividades e que qualquer descuido poderemos ter problemas ao executar e, consequentemente, não atingirmos o objetivo esperado.

O ASP.NET 5 simplifica bastante essa customização expondo uma interface chamada IApplicationModelConvention. Ao implementar ela e acopla-la a execução, o ASP.NET identificará todos os controllers, ações, rotas e filtros candidatos a serem expostos (de acordo com a convenção predefinida) e entregará para analisarmos (através da classe ApplicationModel), e com isso, podemos customizar da forma que precisamos. Tudo isso através de uma API de mais fácil utilização, sem a necessidade de conhecer detalhes dos bastidores para implementar convenções que estão em um nível mais superficial.

Para exemplificar o uso, considere o controller abaixo, que possui duas ações e que retornam uma string. Como podemos perceber, o código foi escrito utilizando o português. Por convenção, o ASP.NET Web API assumirá que o nome dos membros (classe e métodos) serão os mesmos que serão expostos e utilizados pelo sistema de roteamento para montar os links de acesso à API e suas ações.

public class UtilitariosController : Controller
{
    [HttpGet]
    public string ExecutarTarefa()
    {
        return "..." ;
    }

    [HttpGet]
    public string EnviarEmail()
    {
        return "...";
    }
}

A ideia é deixar o desenvolvedor livre para codificar no idioma desejado, e através do recurso de convenção, vamos estipular que o framework irá traduzir de acordo com a escolha realizada durante a sua configuração. Na ausência do idioma, a nomenclatura utilizada no código será exposta.

O que fazemos abaixo é no método Apply (único, exposto pela interface IApplicationModelConvention) percorremos os controllers encontrados pelo ASP.NET, procuramos pela chave no dicionário (que em um ambiente real, poderíamos estar recorrendo à algum outro repositório ao invés de hard-code), e por fim substituímos o nome do controller e da ação pelo encontrado no dicionário, baseado na cultura escolhida.

public class LocalizedApiNamingModelConvention : IApplicationModelConvention
{
    private string culture;

    private IDictionary<string, Tuple<string, string>> mapping =
        new Dictionary<string, Tuple<string, string>>();

    public LocalizedApiNamingModelConvention(string culture)
    {
        this.culture = culture;

        mapping.Add(
            "en-US.Utilitarios.ExecutarTarefa",
            Tuple.Create("Utilities", "RunTask"));

        mapping.Add(
            "en-US.Utilitarios.EnviarEmail",
            Tuple.Create("Utilities", "SendEmail"));
    }

    public void Apply(ApplicationModel application)
    {
        foreach (var c in application.Controllers)
        {
            var controllerName = c.ControllerName;

            foreach (var a in c.Actions)
            {
                var key = BuildKey(controllerName, a.ActionName);

                if (this.mapping.ContainsKey(key))
                {
                    var info = this.mapping[key];

                    c.ControllerName = info.Item1;
                    a.ActionName = info.Item2;
                }
            }
        }
    }

    private string BuildKey(string controllerName, string actionName)
    {
        return $"{culture}.{controllerName}.{actionName}";
    }
}

Depois da classe criada, basta incluirmos na coleção chamada Conventions, e fazemos isso através do método AddMvcCore, conforme é mostrado no código abaixo:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvcCore(o =>
        o.Conventions.Add(new LocalizedApiNamingModelConvention("en-US")));
}

Repare que criamos a classe e no construtor passamos a cultura en-US e ao rodar a aplicação, os links acessíveis serão /Utilities/RunTask e /Utilities/SendEmail. Os links em português retornarão o erro 404 do HTTP (Not Found).