O que é?


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.