XPrevail é uma camada .NET de prevalência de objetos, amigável e
extendida. Caso você já conheça o conceito de prevalência a seção manifesto traz os motivos pelo qual o XPrevail existe, bem como seus diferenciais.
O conceito de prevalência foi originalmente idealizado pelo brasileiro
Klaus Wuestefeld e concretizado no projeto Prevayler , uma implementação em Java. Ele é uma união de
antigos conceitos já aplicados isoladamente em outros cenários.
A utilização de prevalência é uma alternativa ao uso dos banco de dados.
Com ela, todos os objetos de negócios ficam persistidos na memória e há
garantia de que eles serão recuperados fielmente caso haja alguma queda de
energia ou falha na aplicação. Dentre as principais vantagens do uso de
prevalência podemos citar o grande ganho de performance para consultas
e a drástica redução de custos de uma solução (elimine aqui
custos de servidor para o banco de dados (hardware), licenças do software
servidor de banco de dados, custo operacional de manutenção e etc).
Vamos agora entender o funcionamento geral da prevalência, começando por
alguns termos comuns utilizados dentro desse conceito:
PrevalenceEngine:
A instância de uma classe interna do framework que é responsável por todo
o processo de execução, registro e recuperação das alterações
realizadas no PrevalentSystem.
PrevalentSystem:
Uma classe que deve conter referência interna a todos os objetos de negócio
de uma dada aplicação. Os objetos nela referenciados serão serializados
completamente quando for solicitado ao PrevalenceEngine um snapshot. Dessa
forma, qualquer objeto fora dessa classe terá seu estado perdido.
Command/Transaction:
A versão 2.0 do Prevayler agora chama de
Transaction o que na versão 1.0 chamava de
Command . Toda alteração a ser realizada nos objetos mantidos pelo PrevalentSystem
deve ser feita através de objetos de classes que implementam a interface
ITransaction definida pelo framework. Essa instâncias são passadas ao PrevalenceEngine
para registro e execução. No projeto XPrevail a interface
IOperation é a equivalente a
ITransaction existente no Prevayler(ver
manifest para mais detalhes).
Snapshot: É o processo no qual o PrevalenceEngine serializa todo PrevalentSystem,
tal qual ele está no momento da solicitação. Isso torna
desnecessário a leitura dos arquivos de log
de transações anteriores.
Conhecidos alguns dos termos envolvidos vamos agora a uma descrição do
processo. Imagine que tenhamos apenas uma classe de nogócio numa
dada aplicação, a classe Cliente. Vamos aos passos para tornar nossa aplicação prevalente.
1. Precisamos definir uma classe que manterá referência a nossos objetos da
classe Cliente, ela será nosso PrevalentSystem e poderia chamar-se Clientes.
2. Deveremos declarar uma referência para o PrevalenceEngine e inicializá- la,
momento no qual será exigido a informação de qual classe será o
PrevalentSystem, naturalmente devemos informar Clientes.
3. A inicialização do PrevalentSystem deve ser solicitada ao PrevalenceEngine,
através de uma propriedade PrevalentSystem. Ela retornará uma instância da
classe Clientes com um estado exatamente igual ao da última execução (caso tenha havido
uma).
4. Como foi mencionado, qualquer alteração no PrevalentSystem,
para prevalecer é exigido que seja feita através de objetos de classes
que implementem a interface ITransaction. Assim, podemos declarar duas classes, uma chamada AdicionarCliente e outra chamada
RemoverCliente, ambas implementando a interface ITransaction . Respectivamente elas seriam utilizadas para adicionar e remover objetos Cliente no/do PrevalentSystem.
5.
Para adicionar um cliente, precisamos agora criar uma instância da classe AdicionarCliente , passando em seu construtor as informações necessárias a execução de
sua tarefa. Devemos então solicitar ao PrevalenceEngine que execute essa
transação. Isso envolverá a serialização do objeto AdicionarCliente em um arquivo destinado ao log de transações e posterior
execução da transação, onde de fato deverá ocorrer a adição do cliente.
6.
Digamos que após executarmos várias vezes o item 5, além de também termos
realizados algumas remoções de clientes, teremos então um PrevalentSystem
contendo uma determinada quantidade de objetos Cliente . O arquivo de log de transações conterá uma versão serializada de
todos os objetos (que implementam ITransaction ) que efetuaram alterações no PrevalentSystem, exatamente na mesma ordem em
que eles ocorreram.
7.
Caso nesse ponto ocorresse uma falha de energia. Nossa aplicação deveria
ser reposta no ar, tão logo o ambiente tenha sido re-estabelecido. No
momento da inicialização do PrevalenceEngine, ele irá ler todos os
arquivos de log de transações gerados. Ele deserializará todos os objetos
contidos nos arquivos e irá re-executá-los. Esteja atento, os objetos
mantidos nesse arquivo são por exemplos, os da classe AdicionarCliente, não os da classe Cliente . O PrevalenceEngine está constantemente registrando as alterações
realizadas nos objetos de negócios, não os objetos de negócio em si.
8.
Tendo a aplicação voltado ao ar e seu estado anterior recuperado,
poderíamos gerar mais algumas alterações no PrevalentSystem e soliciar um
snapshot. Nesse momento o PrevalenceEngine irá gerar um arquivo novo, não
um de transações, mas um especificamente para o snapshot. Esse arquivo
conterá uma versão serializada do PrevalentSystem inteiro, ou seja,
efetivamente de todos os objetos de negócio. Digamos que após o snapshot,
fizemos mais algumas alterações no estado do PrevalentSystem e ocorreu
outra falha de energia.
9.
Quando nossa aplicação solicitar a inicialização do PrevalenceEngine, ele
buscará pelo mais recente snapshot gerado. De posse dele, ele deserializará
seu conteúdo e obterá uma versão do PrevalentSystem. Feito isso, pegará
todos os arquivos de log de transações posteriories ao último snapshot e
re-executará todas as transações em cima do PrevalentSystem obtido
anteriormente. Isso resultará em um PrevalentSystem tal qual existia antes
da falha, da forma mais otimizada possível.
Esse seria mais ou menos o fluxo de execução de uma aplicação prevalente.
A prevalência possui dois requisitos obrigatórios a serem atendidos pelos
objetos de negócios que serão persistidos, eles devem ser
serializáveis e determinísticos. Um objeto determinístico é aquele
que submetido a uma mesma série de alterações, numa mesma ordem, sempre
resultará num mesmo estado final. A classe que representa o PrevalentSystem,
obviamente, também precisa ser serializável.
Os conceitos envolvidos na prevalência estão estabelecidos de tal maneira a
executar as tarefas de uma forma otimizada, isso refletiu em questões como:
- Serializar as alterações nos objetos de negócio, no lugar deles
próprios, isso diminiu siginificativamente a quantidade de espaço em disco
necessário para garantir o estado do PrevalentSystem.
- Não ter qualquer tipo de indexação nos arquivos gerados, afinal o
objetivo não é simular um banco de dados, mas sim eliminá-lo. Dessa forma,
concentrou-se apenas no processo de serialização e recuperação de objetos.
- A recuperação de um PrevalentSystem através de seu snapshot é
extremamente mais rápida que a recuperação através de log de
transações. É fácil perceber o motivo, através de um snapshot
ele deserializa o PrevalentSystem como um todo, diretamente, já com o
log de operações, ele precisa deserializar todos os objetos de transação
e re-executá-los. Por isso é recomendado realizar snapshots periodicamente.
Espero que tenham notado que percorrendo os passos descritos
atenriormente não temos nenhum instrução SQL envolvida. Um efeito
colateral positivo do conceito de prevalência é que somos obrigados a
encarar com mais seriedade a orientação a objetos, pois será apenas com
ela que trabalharemos. Usamos nossa própria linguagem de programação para
realizar consultas, podemos trazer e manipular os dados de qualquer forma.
Bom, terminamos aqui essa visão geral do conceito de prevalência, recomendo
a leitura do
FAQ para tirar quaisquer eventuais dúvidas
restantes (ou geradas) após a leitura desse texto.