No período de ascensão das aplicações web, era comum que se buscasse nesse ambiente a mesma experiência de uso que se tinha em aplicativos desenvolvidos para desktop, o que levou ao surgimento do conceito de RIA (Rich Internet Application), uma aplicação web desenvolvida para entregar as mesmas funcionalidades que eram normalmente associadas com uma aplicação Win32, por exemplo.
Com o passar do tempo, porém, e ainda mais com o crescente número de usuários de dispositivos móveis, passou-se a entender as individualidades das aplicações web e a importância de oferecer uma experiência de uso otimizada para esse ambiente, considerando todas as suas particularidades. A tendência atual, então, é para o desaparecimento das aplicações RIA e o fortalecimento de aplicações web com uma liberdade de design maior, com tecnologias como HTML5, JavaScript e CSS garantindo maior poder tanto ao desenvolvedor front-end quanto ao back-end.
Nesse cenário, alguns frameworks vêm ganhando cada vez mais espaço, por oferecerem maior flexibilidade para a construção da interface e interação com serviços no back-end que proveem dados em formatos leves e compatíveis com diversas linguagens de programação. Entre esses frameworks destacam-se, por exemplo, o Knockout.js, Backbone.js, Ember.js e AngularJS, que será utilizado neste artigo.
AngularJS e o padrão MVW
Por bastante tempo, o AngularJS foi muito propenso a adotar o padrão MVC (Model-View-Controller), mas depois de muitas refatorações em suas APIs, esse framework hoje está mais próximo do padrão MVVM (Model-View-ViewModel), pois o objeto $scope pode ser considerado o ViewModel, que está sendo decorado por uma função através da qual chamamos o controller. Após tantas discussões e questionamentos sobre qual seria realmente o modelo implementado pelo AngularJS, hoje ele é considerado um framework MVW, de Model-View-Whatever, ou seja, não importa qual é o nome dado ao terceiro componente da arquitetura, o importante é a função que ele desempenha e como ele o faz com eficiência.
O AngularJS nos dá uma grande flexibilidade para separar a lógica de apresentação da lógica de negócios e o estado da apresentação. Com isso, esse framework dá uma potência maior à nossa produtividade e à manutenibilidade de nossa aplicação. Não vamos nos aprofundar muito em como o AngularJS funciona, nosso principal escopo é mostrar como ele pode ser facilmente acoplado ao ASP.NET MVC e como esses frameworks podem trabalhar em conjunto. Para melhor compreender essa integração, portanto, vamos desenvolver uma aplicação de exemplo onde teremos o AngularJS na camada View, fazendo o trabalho que geralmente é delegado ao Razor.
Projeto prático
Neste projeto desenvolveremos um controle bem simples de gastos com categoria, consumo e unidades valoradas. Na Figura 1 temos o Diagrama Entidade Relacionamento da aplicação, onde podemos observar as tabelas que serão utilizadas, com suas chaves primárias e relacionamentos.
Figura 1. DER da aplicação de exemplo
Frameworks envolvidos na solução
Em nossa aplicação de exemplo utilizaremos o ASP.NET MVC 5, o AngularJS, o Fluent NHibernate para o Mapeamento Objeto-Relacional e o Unity para controlar a injeção de dependências, mantendo assim uma estrutura desacoplada e flexível.
Utilizaremos o Domain-Driven Design como abordagem para desenvolvimento, mas nesse DDD não contaremos com módulos, raízes agregadas e outros componentes que deixariam o sistema de exemplo bem complexo, uma vez que o escopo principal é demonstrar como o ASP.NET MVC pode ser integrado com o AngularJS em cenários onde essa metodologia de desenvolvimento é utilizada.
O propósito desse artigo não é aprofundar-se no uso específico de cada um dos frameworks, mas sim na sua integração, por tanto é necessário um conhecimento básico do padrão MVC e de JavaScript para que se possa compreender adequadamente os componentes da aplicação e os passos seguidos para desenvolvê-la.
Arquitetura do projeto
Na Figura 2 podemos observar como a arquitetura do nosso projeto está aderente ao que prega o DDD, com a camada de apresentação consumindo apenas a camada de aplicação e esta é sendo responsável por ir, através de seus serviços, às camadas inferiores para requisitar o que lhe é solicitado pelo usuário (dados).
Figura 2. Arquitetura do projeto com DDD
Na Figura 3 vemos como está organizada a solução utilizada neste exemplo, com os projetos separados em pastas para melhor organizar a divisão dos componentes por responsabilidades e com nomes sugestivos, facilitando inclusive a reutilização do código.
Figura 3. Estrutura do projeto no Solution Explorer
Framework para Persistência
Com relação à persistência, que ficará situada na camada de infraestrutura, utilizaremos o Fluent NHibernate com Auto Mapping. Dessa forma, basta seguirmos algumas convenções de nomenclatura e estrutura nas nossas entidades e o NHibernate fará a configuração e a conversão delas em tabelas do banco de dados automaticamente, sem a necessidade de realizar esse processo manualmente.
Além disso, utilizaremos o padrão Unit of Work para segmentar as transações em uma única sequência de regras isoladas dentro da ação, de forma que o acesso ao banco seja focado em economia de recursos. Com isso garantiremos que a transação será feita apenas para o bloco em questão, sendo descartada em seguida. Essa prática visa manter a memória da aplicação sempre enxuta, evitando heaps e outros problemas.
Para facilitar essa configuração, teremos de personalizar o arquivo de configuração (.config), pois é ali que apontamos qual será o assembly em que o NHibernate irá procurar por entidades a serem mapeadas e configuradas.
O NHibernate busca as chaves estrangeiras através da convenção Entidade_Id, mas como nossas chaves estrangeiras estarão com o nome no formato IdEntidade, então teremos de modificar as configurações do NHibernate, sobrescrevendo algumas classes e métodos.
Na Listagem 1 temos a classe de configuração
do Fluent NHibernate (contida no namespace MVCAngularJS.Infraestrutura.Orm.Nhibernate.Configuracao). No construtor da classe selecionamos a string de conexão e
o driver para utilização do SQL Server (linhas 15 a 24), em seguida (linhas 25
a 46) definimos novas convenções para mapeamento e os assemblies onde as entidades dever ...