No .Net Architects Day eu apresentei rapidamente um exemplo iniciante de um sistema de blog durante minha palestra de DDD. Eu havia criado algumas entidades, post e comentário, e tudo funcionava.
Aí resolvi criar outra entidade: usuário. Criei a entidade, criei o controlador as views, liguei tudo, e tudo continuava funcionando. Não criei repositórios específicos, nem mapeamento, nem nada a mais, só a entidade e as partes de interface gráfica. Estava usando NHibernate para resolver o "problema" da relação objeto e dados relacionais. De repente a tabela apareceu lá, os dados salvavam corretamente, o SQL gerado era otimizado e eu estava feliz.
Tudo funcionava. Tudo automático. Muitos me perguntaram como isso foi feito. A resposta é muito simples. Além do NHibernate usei também um pouco de NHibernate Fluente e sua possibilidade de trabalhar automapeamento baseado em convenções. Minha classe de configuração do NHibernate fazia mapeamento de três formas: a básica do NH (com XML), a fluente (do NH fluente), e a automática. O código da classe de autoconfiguração é muito curto, cerca de 60 linhas:
public class Autoconfiguracao
{
private static ISessionFactory _factory;
public static ISessionFactory Configurar(bool gerarBanco = true)
{
if (_factory != null)
return _factory;
var config = Fluently.Configure()
.Database(
FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2005
.ConnectionString(c =>
c.Is(@"Data Source=.\sqlexpress;Initial Catalog=DNAD09Blog;Integrated Security=True"))
.ShowSql()
);
config.Mappings(m =>
{
m.HbmMappings.AddFromAssemblyOf<Autoconfiguracao>();
m.FluentMappings
.AddFromAssemblyOf<Autoconfiguracao>();
m.AutoMappings.Add(
AutoPersistenceModel.MapEntitiesFromAssemblyOf<Postagem>()
.WithSetup(s => s.IsBaseType = (type => type == typeof(EntidadeComId)))
.ConventionDiscovery.Setup(c =>
{
c.Add(PrimaryKey.Name.Is(x => x.EntityType.Name + "_Id"));
c.Add(DefaultLazy.AlwaysFalse());
//c.Add(ForeignKey.Format((prop, tipo) => prop.Name + "_Id"));
c.Add(Table.Is(x => x.EntityType.Name));
}
)
.Where(t =>
t.Namespace == "Dnad09.Blog.Dominio.Entidades"
&& t.IsInterface == false
&& t.IsAbstract == false
)
);
}
);
if (gerarBanco)
{
config.ExposeConfiguration(cfg =>
{
var schemaExport = new SchemaExport(cfg);
schemaExport.Create(false, true);
});
}
_factory = config.BuildSessionFactory();
return _factory;
}
}
Na parte de configuração, que vai até "config.Mappings" (linhas 17 a 42) todas as chamadas são baseadas em Lambdas. Em seguida se o flag gerar bancos é configurado então o banco é criado, e a factory do NH é retornada. Nela estão todas as configurações do NH, e a criação de sessões do NH, que é o que realmente importa, é muito facilitada. Para trocar o banco de dados para Oracle, por exemplo, bastaria trocar a chamada "Fluently.Configure" (linhas 9 a 15) para trabalhar com este banco de dados.
O mais interessante é a possibilidade de mapear automaticamente apenas a parte que interessa. Podemos mapear algumas classes automaticamente, aceitando as convenções, e em outras utilizar mapeamento fluente ou baseado em XML. Em um cliente recentemente fizemos isso através de atributos: se uma classe tinha um atributo de automapeamento que criamos, então usávamos ela no automapeamento. Senão, teríamos que trabalhar o mapeamento de forma fluente ou com XML.
A produtividade neste tipo de cenário é gigantesca. O foco é o domínio, ou seja, o coração de um software de negócios. A persistência dos dados não é sequer uma preocupação.
Essa abordagem toda é muito legal, mas levantou questões interessantes no .Net Architects Day, que foram inclusive discutidas em outras palestras, como a dada pelo Juliano, que foi mais focada em ORM. Entre elas, a mais polêmica foi sem dúvida a seguinte:
- O DBA morreu?
Minha resposta desta pergunta nesta quarta-feira de manhã. Aguardem.
a6e82a62-06cc-4eda-9476-24fc944ed712|0|.0