Arquitetura de Aplicações Web Real-Time
Ontem, 26/09, apresentei para alunos da FACCAT uma pequena ideia sobre o assunto. Seguem os slides para quem se interessar.
Ontem, 26/09, apresentei para alunos da FACCAT uma pequena ideia sobre o assunto. Seguem os slides para quem se interessar.
O ASP.NET MVC vem se tornando uma alternativa tão popular quanto o Web Forms, isso já é uma tendência vista no mercado. Pessoalmente, gosto bastante da abordagem MVC, é organizada e produtiva de forma equilibrada.
Inspirada no Rails, a versão MVC do ASP.NET conseguiu trazer as melhores práticas para a boca do povo que, acostumados com o Web Forms, não davam muita bola pra HTTP, HTML, Javascript e Web Standards. Acho que apenas isso já representa uma grande vitória, que causa reflexos inclusive no Web Forms.
Sem contar que o Razor é fantástico!
A estrutura de um novo projeto utilizando MVC 4 traz diversos recursos interessantes, como o Modernizr (essencial em tempos de adaptação ao HTML5), jQuery, jQuery UI, jQuery Validation, Knockout, Entity Framework, entre outros!
Mas peraí, entre outros!? Não tem muita coisa aí? Em projetos de nível médio e grande, provavelmente a maioria desses componentes serão utilizados. Quando não utilizados, não fazem diferença, não atrapalham na execução da aplicação.
Mesmo assim, ao criar um novo projeto MVC (Empty!?) nessa versão (que ainda é beta) são gerados 294 arquivos e pastas na solução, somando quase 20 MB. E, pra dizer a verdade, isso me incomoda (bastante) quando quero criar uma aplicação mais simples. Gosto de manter a casa limpa.
Fiz um desafio a mim mesmo: fazer o MVC funcionar com o menor número de recursos possível. E foi bem legal, gerou uma solução muito limpa. Vamos aos passos.
Antes de tudo é bom lembrar que é necessário ter o ASP.NET MVC instalado, estou usando a versão 4 BETA.
System.Web.Mvc.Global.asax) na aplicação.Global.asax, adicione os using System.Web.Mvc e System.Web.Routing.Application_Start, adicione a rota padrão do MVC:
RouteTable.Routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
Controllers no projeto.HomeController, que herda Controller (namespace System.Web.Mvc).Index. HomeController ficará assim:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace WebApplication.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return Content("<h1>Hello, MVC Lite</h1>");
}
}
}
System.Web.WebPages e System.Web.WebPages.Razor, com Copy Local True, assim como na etapa anterior.Views.Views.Home, dentro do diretório das views, onde ficarão as referentes ao HomeController.Index.cshtml, dentro do diretório Home, com o seguinte conteúdo:
@model string
<!DOCTYPE html>
<html>
<head>
<title>ASP.NET MVC Lite</title>
</head>
<body>
<h1>ASP.NET MVC Lite</h1>
<h3>@Model</h3>
</body>
</html>
Index para o seguinte:
public ActionResult Index()
{
object model = "With Razor View!";
return View(model);
}
Para quem quiser, coloquei esse projeto de exemplo no GitHub.
Thunderstruck is a .NET library that makes access to database simpler and faster using ADO.NET. A really fast way to access databases.
Another ORM? No! Thunderstruck isn’t an ORM. It doesn’t abstract the features of the database, just makes the access easier.
Entity Framework é um projeto de mapeamento de banco de dados relacionais da Microsoft, desenvolvido pela equipe responsável pelo ADO.NET. Uma ORM (mapeamento objeto relacional, como NHibernate, SubSonic, ActiveRecord) que tem crescido muito em adoção, principalmente em novos projetos utilizando ASP.NET.
O Code First é uma tecnologia recentemente incorporada ao EF, possibilitando gerar os objetos de negocio sem pensar no banco de dados e na forma como serão armazenados. A partir de padrões e convenções que seguem o que já é de praxe no desenvolvimento com .NET, o desenvolvedor define os objetos e seus comportamentos, depois configura o mapeamento de forma desacoplada, sem impactar nos objetos já definidos.
Embora o CF tenha sido pensado para que a base de dados seja criada a partir dos modelos (objetos), é fácil utilizá-lo sobre uma base legada que não segue os padrões e convenções definidas para o CF. Mas nesse caso, qual é a vantagem te utilizar o CF e não os modelos convencionais do EF (famoso edmx)? O CF permite um controle mais apurado dos objetos, e remove a dependência do gerador visual dos modelos. Mas isso é assunto para um próximo artigo.
Vamos fazer passo a passo um mapeamento com Code First.
O primeiro passo é esquecer que existe EF, CF e até banco de dados. Se desapegue disso, pelo menos por enquanto. Pense no negócio, no que sua aplicação deve fazer e crie os objetos.
Para o exemplo, vamos criar algo bem simples, uma exclusiva, unica e jamais utilizada em exemplos: engine de blog. Primeiramente vou imaginar o que meu blog vai ter: Artigo, Autor e Tags… Muito inovador. Agora é criar os objetos.
public class Artigo
{
public Artigo()
{
Tags = new List<Tag>();
}
public int Id { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public Autor Autor { get; set; }
public IList<Tag> Tags { get; set; }
}
public class Autor
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Tag
{
public int Id { get; set; }
public string Title { get; set; }
}
Perceba que as as classes não possuem referencia alguma para o Entity Framework, são objetos normais. Inclusive essa é a intenção, desacoplar a lógica de acesso a banco de dados dos objetos de negócio.
Duas formas de fazer isso. Ou baixe os binários do Entity Framework (versão 4.1) ou utilize o NuGet (que você já deveria conhecer) com o comando Install-Package EntityFramework. Se você baixou os binários, não esqueça de adicionar os Assemblies nas referências do projeto; se usou o NuGet, isso já foi feito por ele.
Agora sim começamos a pensar no banco de dados, criando um objeto chamado DatabaseContext, que vai fazer todo trabalho sujo. Esse objeto deve herdar DbContext, que é do Entity Framework (namespace System.Data.Entity).
Vale lembrar que é extremamente recomendável separar esse objeto das classes de negócio, deixe-os em projetos diferentes, evitando acoplamento e facilitando a manutenção. O projeto de negócio não conhece banco de dados, ele nem deve saber que vai ser persistido.
public class DatabaseContext : DbContext
{
public DbSet<Artigo> Artigos { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<Autor> Autores { get; set; }
}
O BbSet é um objeto do EF que representa uma tabela, mais ou menos isso, então cada objeto que vai ser persistido no banco deve ser mapeado.
Primeiro é necessário inserir (no arquivo de configuração da aplicação) uma ConnectionString com o mesmo name do objeto DbContext, que nesse caso é DatabaseContext. Essa configuração vincula o objeto ao banco de dados.
Notem que no meu caso vou utilizar o provider System.Data.SqlClient, que é do Sql Server. O EF já possui suporte a outros bancos de dados, como Oracle (que possui projeto oficial em fase beta) e MySQL, mas esse também é assunto para outro artigo.
<connectionStrings>
<add name="DatabaseContext" connectionString="[INSIRA_SUA_CONNECTION_STRING]" providerName="System.Data.SqlClient" />
</connectionStrings>
Agora, para criar o banco de dados, rode o seguinte comando:
using (var dataContext = new DatabaseContext())
{
dataContext.Database.Create();
}
Preste atenção na forma de usar o DatabaseContext, ele deve ser utilizado com o using, pois esse se encarrega de fechar os recursos alocados pelo acesso a banco, como a famosa connection. Se, por algum motivo muito estranho, não for possível utilizar using, feche os recursos utilizando o método Dispose do DatabaseContext.
Agora vamos manipular os primeiros dados utilizando EF. Como sempre, precisamos do objeto DatabaseContext, lembre-se que ele é sua porta de entrada para o banco de dados.
var autor = new Autor { Name = "Wagner Andrade" };
var artigo = new Artigo
{
Autor = autor,
Title = "Entity Framework",
Text = "Uma forma simples e limpa de acessar os dados relacionais."
};
artigo.Tags.Add(new Tag { Title = ".NET" });
artigo.Tags.Add(new Tag { Title = "Desenvolvimento" });
artigo.Tags.Add(new Tag { Title = "ADO.NET" });
artigo.Tags.Add(new Tag { Title = "Entity Framework" });
using (var dataContext = new DatabaseContext())
{
dataContext.Artigos.Add(artigo);
dataContext.SaveChanges();
}
Primeiramente criei a estrutura de objetos que representa um Artigo, com seu autor e tags. Depois abri o contexto de acesso ao banco de dados, adicionei o novo artigo e, por fim, o método SaveChanges do contexto. Esse método é o que faz a coisa acontecer de verdade, até ele nada foi manipulado a nível de banco de dados. Lembra do commit? Pois então, é uma analogia perfeita.
Acessar dados no banco é tão fácil quanto persistí-los.
using (var dataContext = new DatabaseContext())
{
var artigo = dataContext.Artigos.Find(1);
}
Nesse caso utilizei o Find para buscar no banco o Artigo com PrimaryKey 1, nesse caso, segundo as convenções do EF (PrimaryKey padrão é a property Id), Artigo com Id == 1.
O Artigo encontrado retorna sem suas Tags (lista Tags == null), pois essas estão em outra tabela no banco de dados, e é necessário fazer um Join para buscá-las. Porem fazer Join entre as tabelas também é trivial e transparente utilizando EF. Na verdade nem deve ser encarado como um Join (porque esse conceito não existe na programação orientada a objetos), mas sim como o carregamento de uma lista dentro de um objeto.
using (var dataContext = new DatabaseContext())
{
var artigo = dataContext.Artigos
.Include(a => a.Tags)
.First(a => a.Title.Contains("Entity"));
}
O método Include está informando que as tags devem ser incluídas, através de um Join, na query que vai buscar o artigo no banco, mas isso é o que acontece por baixo do capô. Na prática, o Include deve ser utilizado para buscar as listas do objeto. Para utilizar tal método, é necessário incluir (using) o namespace System.Data.Entity.
Mas porque isso não é feito automaticamente? Porque, na maioria dos casos, um objeto tem muitas dependências, e essas dependências também possuem outras. Ou seja, uma simples busca de um objeto poderia ocasionar um query gigantesca, carregando todas as dependências, recursivamente.
Esse talvez seja o processo mais facilitado pelo EF, se já não bastasse todo tempo que ele nos poupou até agora!
// Alterar.
using (var dataContext = new DatabaseContext())
{
var artigo = dataContext.Artigos.Find(1);
artigo.Title = "EF";
dataContext.SaveChanges();
}
// Remover.
using (var dataContext = new DatabaseContext())
{
var artigo = dataContext.Artigos.Find(1);
dataContext.Artigos.Remove(artigo);
dataContext.SaveChanges();
}
É simples assim. Primeiro busquei no contexto o Artigo que quero modificar, depois alterei o titulo e SaveChanges. Pronto, atualizado no banco.
Para remover foi tão fácil quanto. Primeiro busquei o mesmo Artigo, removi da lista de artigos do contexto e SaveChanges. Feito, removido do banco.
Esse artigo obviamente aborda um passo a passo bem básico sobre o Entity Framework Code First. Existem muitos assuntos a serem tratados, mas espero que isso sirva de porta de entrada para uma tecnologia tão promissora.
Cada vez que você abre um UpdatePanel no inicio de um página ASP.NET Web Forms e fecha no fim, um Stormtrooper morre em uma galáxia distante. Se bem que isso seria bom… Mas enfim.
Isso cria o que eu chamo de Fake AJAX. Porque cada request assíncrono trafegará toda a página! Peraí, nesse caso qual é o ganho de usar a tecnologia do sabão?!
Então, caro desenvolvedor, pare com isso, honre suas calças — espero que você use-as, pelo menos.
Como? Criando UpdatePanels em zonas que consiga identificar independência de atualização. Mas isso é só o primeiro passo, e talvez o mais difícil. Depois, cada um dos UpdatePanels deve ser criado com as seguintes propriedades:
Dessa forma o server não envia o conteúdo do UpdatePanel a cada request, só quando é marcado pra que isso aconteça, utilizando idDoUpdatePanel.Update(), lá no server mesmo.
Um pouco mais preciosista, mas útil. Significa que esse UpdatePanel não será atualizado, como acima, nem quando o evento de PostBack se originar de um controle dentro dele. Pra atualizá-lo, da mesma forma: idDoUpdatePanel.Update().
Agora sim, com vários UpdatePanels na página e todos com as propriedades UpdateMode e ChildrenAsTriggers, você terá total controle sobre o que será trafegado nos ensaboados requests assíncronos.
Essa pergunta me ocorre quando vejo o quanto um sistema de partículas feito em Ruby é custoso para máquina.
Mesmo desenvolvendo pequenos joguinhos experimentais, Ruby permanece prazeroso e rápido, assim como pretende ser. Mas vamos ao problema: Porque apresenta baixo rendimento quando o assunto é game?
Um jogo que se preze tem como objetivo rodar 60fps. Expectativa alcançada somente quando os ciclos de processamento chegam, no máximo, a 16 milisegundos. Quando o processamento ultrapassa essa pequena fração de tempo, o frame rate cai, as vezes vai a níveis tão baixos que temos os famosos lags.
Tive o maior exemplo de lag quando tentei rodar Fifa 98 no meu 486. Nesse caso a medida de calculo mais óbvia não era frames per secound, mas sim secounds per frame.
Enfim, lags não são bem-vindos. Claro que esse foi um caso extremo, e o desenvolvedor do Fifa não tem culpa do idiota querer rodar o jogo em um micro da geração passada.
Ruby não é uma das tecnologias mais rápidas do mercado e o blá blá blá de sempre. Mesmo assim Python também não é um foguete e se sai melhor quando o assunto é velocidade em games.
Encontrei um artigo que resume o problema. Quem escreve é Glenn Fiedler, um desenvolvedor de jogos que trabalha na Sony.
Segundo ele o problema está no GC do Ruby, que ainda utiliza o método mark-sweep, mais lento que os GCs de outras tecnologias concorrentes. Então, o problema não está diretamente ligado à performance, mas sim na arquitetura.
E porque o mesmo problema não acontece com aplicações web? Na verdade acontece, mas a necessitade é outra. Nesse caso a requisição web chega ao servidor, é processada, e depois encarrega-se de limpar seu proprio lixo.
Enfim, continuo gostando de Ruby para estudos na área e pequenos joguinhos experimentais. E para isso existem diversos frameworks que ajudam:
Mas quando a coisa é comercial e exige performance, talvez não seja uma boa escolha.
E antes que perguntem: Não testei com RubyEE, JRuby, IronRuby, MacRuby ou qualquer outra variante. E sim, teremos resultados diferentes.
Quando práticas ágeis são mal implementadas no desenvolvimento de software, tornam-se processos Go Horse, praticamente fábricas com ciclos de trabalho. Como qualquer outra metodologia. E sinceramente, ainda não trabalhei com um processo ágil implementado da maneira correta.
Não sou consultor, certificado, entusiasta ou qualquer outro tipo de evangelizador de métodos ágeis. Então como posso avaliar que nunca trabalhei com implementações corretas?
Simples! Porque as diretrizes do manifesto não foram atendidas. Esquecidas é uma palavra que encaixa melhor aqui. Vou explicar, para isso vamos a eles, os valores:
Não vi nem de perto. Geralmente os processos e, pior, as ferramentas é que modelam os indivíduos. Resultado? Equipe desanimada porque precisa trabalhar para uma ferramenta ou processo, e não para um software.
Ok, essa diretriz não é difícil de seguir. A documentação era precária mesmo antes de existir metologias ágeis. Software em funcionamento sim, é mais complicado.
Na visão de alguns lideres de projetos o cliente continua sendo um inimigo que não sabe o que quer. Fechar o contrato com o escopo bem restrito é o objetivo principal.
O plano foi feito, deveria ter sido seguido, mesmo que todos concordemos que mudanças são inerentes ao desenvolvimento. Uma guerra de egos inflados impossibilita que as mudanças sejam feitas sem traumas. Uma mudança no escopo do projeto geralmente resulta em reuniões longas das quais o verdadeiro objetivo é encontrar culpados.
O que quero dizer é que metodologias ágeis são excelentes ferramentas para gerenciar projetos de desenvolvimento de software. Não fazem diferença nenhuma quando não implementadas da maneira correta, e para isso seguir seus valores pensando nos objetivos da metodologia é fundamental.
Não tente seguir a agilidade se não concorda com os valores ou não está disposto a segui-los.
Hoje o acesso à internet é trivial para o desenvolvedor de software. Por isso raramente utilizamos a documentação local da instalação do Ruby e das gems.
Muitos até já instalam as gems sem essa documentação, com os argumentos --no-ri --no-rdoc. Instalar o Rails sem documentação local fica assim:
gem install rails --no-ri --no-rdoc
Escrever esses argumentos é um saco, mas o pior é esquecê-los.
É fácil padronizar a instalação da gem para incluí-los automaticamente. Edite o arquivo ~/.gemrc, adicione:
gem: --no-ri --no-rdoc
É isso.
Daniel Tamiosso escreveu o primeiro plugin do Wind, o Wordpress Importer. Como o nome já diz, serve para importar os artigos escritos no Wordpress.
É muito fácil de instalar: copie o arquivo wordpress_to_wind.rb para o diretório de plugins do Wind. Apenas o usuário admin terá acesso ao widget de administração.
Para utilizar basta enviar o arquivo de exportação do Wordpress (WordPress eXtended RSS file) pelo widget de admin.
Qualquer dúvida é só comentar.
Sugestões e melhorias são bem-vindas. E novos plugins também. Se você tem uma idéia, coloque a mão na massa e submeta o seu.
Esqueci de mencionar que é necessário instalar a gem hurricane para usar o plugin.
Estou liberando a primeira versão do Coollection. Para quem não sabe, se trata de uma API Java que facilita operações sobre Collections, como filtro, ordenação e mapeamento. Essa versão só contempla filtros.
É uma versão estável, porem precisa ser aprimorada, e é isso que vai acontecer até a versão 1.0. A participação de vocês é importante, então não deixem de reportar eventuais bugs e melhorias.
As instruções de uso estão no README.