Hello Prevalence
By FernandoVM
Bem vindos ao mundo da prevalência! Esse é o primeiro de uma sequência de
tutoriais que visa a esclarecer as principais dúvias que
normalmente surgem durante o início do aprendizado da prevalência,
contextualizando-a quando necessário, ao framework XPrevail. É extramente
recomendado uma prévia leitura das seções O que é e manifesto.
O aplicativo desenvolvido ao longo desse tutorial pode ser encontrado na pasta
samples da distribuição do XPrevail.
Ao longo desse tutorial criaremos uma aplicação console simples, ela apenas
realizará cadastro, exclusão e listagem de pessoas. Dessa forma,
definiremos uma class People com algumas informações básica, sua definição pode ser vista
abaixo:
- Delphi for .NET
[Serializable]
TPeople = class
strict private
FName: System.String;
FRecordDate, FBirthDate: System.DateTime;
FEMail: System.String;
public
function get_RecordDate: System.DateTime;
procedure set_Name(const Value: System.String);
procedure set_EMail(const Value: System.String);
procedure set_NiverDate(const Value: System.DateTime);
function get_Age: Integer;
property Name : System.String read FName write set_Name;
property Age : Integer read get_Age;
property EMail : System.String read FEMail write set_EMail;
property NiverDate : System.DateTime read FBirthDate write
set_NiverDate;
property RecordDate : System.DateTime read get_RecordDate;
constructor Create(aName : String; aEMail : String;
aBirthDate : DateTime);
end;
Acredito que o significado das propriedades das classes estejam claros,
devido a sugestividade de seus nomes, cabe aqui uma pequena observação
relacionada a propriedade RecordDate. Ela manterá qual a data e hora em que
uma determinada pessoa foi adicionada, seu valor é setado no construtor da
classe People. Mais adiante veremos que ela existe para um propósito especial.
Note que a classe People foi marcada com o atributo SerializableAttribute,
isso é preciso para cumprir a exigência do objeto de negócio ser
serializável.
O próximo passo agora é definir uma classe que será nosso PrevalentSystem,
a chamaremos de PeopleManager, apenas os objetos referenciados pelo
PrevalentSystem serão persistidos. Vejamos a seguir a definição dessa nova
classe:
[Serializable]
PeopleManager = class
strict private
FPeoples : ArrayList;
public
property Peoples : ArrayList read FPeoples;
constructor Create;
end;
A classe PeopleManager também é bem simples, ela apenas mantém uma lista
de objetos Peoples e fornece um meio para acessá-los. Devemos agora definir as classes que
executaram as operações no PrevalentSystem, que devem portanto implementar
a interface IOperation. Definiremos uma para incluir e outra para excluir
pessoas, vejamos abaixo:
[Serializable]
AddPeople = class(&Object, IOperation)
strict private
FName: System.String;
FBirthDate: System.DateTime;
FEMail: System.String;
public
function Execute(System: TObject): TObject;
constructor Create(aName : String; aEMail : String;
aBirthDate : DateTime);
end;
[Serializable]
RemovePeople = class(&Object, IOperation)
strict private
FName : String;
public
function Execute(System: TObject): TObject;
constructor Create(aName : String);
end;
Precisamos agora criar, e inicializar, uma referência a algum
PrevalenceEngine. Nesse exemplo usaremos o SimplePrevalenceEngine, dentro do
XPrevail ele é quem implementa o comportamento tradicional do conceito de
prevalência, sua definição está no namespace
FernandoVM.XPrevail.PrevalenceEngine. Isso pode ser feito como mostrado
abaixo:
{...}
var
Engine : SimplePrevalenceEngine;
PManager : PeopleManager;
begin
Engine := SimplePrevalenceEngine.Create(typeOf(PeopleManager),
Path.Combine(Environment.CurrentDirectory, 'data'));
PManager := Engine.PrevalentSystem as PeopleManager;
{...}
Observe que a criação do PrevalenceEngine (
SimplePrevalenceEngine no caso) exige a informação sobre qual classe será utilizada como
PrevalentSystem. Outro detalhe é que a instância de trabalho dessa
classe deve ser retornado pelo próprio PrevalenceEngine, atrav[es da
propriedade PrevalenteSystem . Pronto, estando a infra-estrutura montada já podemos realizar agora
algumas inclusões e/ou exclusões de pessoas, para tanto, precisamos para
instâncias das classes AddPeople e RemovePeople para o PrevalenceEgine
executá-las. Esse procedimento é mostrador adiante:
- Adicionando uma pessoa
Engine.ExecuteOperation(
AddPeople.Create('FernandoVM',
'fernandovm@users.sourceforge.net', Convert.ToDateTime('25/10/1979'))
);
- Removendo uma pessoa
Engine.ExecuteOperation(RemovePeople.Create('FernandoVM'));
Após inserir e/ou excluir algumas pessoas, conforme mostrado
acima, teremos no arquivo de log (extensão .xprvLog) o registro de
todas as operações realizadas. Caso o aplicativo seja fechado, de forma
normal ou anormal, ele será capaz de recuper seu estado na próxima vez que
for iniciado. Isso será feito através da recuperação e re-execução de
todas as operações logadas novamente. De fato esse é um mecanismo de
segurança, convém que o aplicativo solicite periodicamente, e no momento da
saída, um snapshot do sistema. Isso é feito de forma muito simples,
bastando chamar o método TakeSnapshot do PrevalenceEngine.
Engine.TakeSnapShot;
A solicitação do snapshot irá gerar um arquivo novo, chamado arquivo de
esnapshot (extensão .xprvSnapshot), contendo uma versão serializada do
PrevalentSystem. Isso torna desnecessário a leitura de arquivos de log
anteriores para re-execução de operações, por isso a carga a partir de um
snapshot é muito mais rápida que através do arquivo de log. Fique atento
para utilizar o método TakeSnapshot de forma adequada, a realização de um snapshot coloca o sistema em modo
read-only, além de ser algo bem mais demorado que a execução de uma
operação.
Lembra daquela propiedade RecordDate? Pois bem, chegou a hora de explicarmos
um importante aspecto da prevalência, o tratamento do fator tempo
(data/hora). Numa eventual necessidade de carregar o sistema novamente
através dos arquivos de log, as operações serão executadas novamente, mas
agora em um novo horário. Dessa forma, caso seus objetos acessem diretamente
a data/hora do sistema (System.DateTime.Now) os referidos campos perderam a
consistência. Para evitar esse problema os PrevalenceEngine's disponibilizam
uma propriedade Clock (implementação da interface IClock ) para fornecer a data/hora do sistema. A aplicação deve obter a data/hora
através dessa abstração e passar o valor obtido aos objetos, assim,
durante a recuperação das operações logadas os PrevalenceEngine's
asseguraram que os objetos obtenham sempre a data correta. Para adaptar
nosso código a essa filosofia precisamos tirar a obtenção da
data/hora referente a propriedade RecordDate de dentro do construtor de People e passarmos a obtê-la externa. Dessa forma, teríamos um código
semelhante ao mostrado abaixo:
- Antes
constructor People.Create(aName, aEMail: String; aBirthDate: DateTime);
begin
inherited Create;
FRecordDate := System.DateTime.Now;
{...}
- Depois
constructor People.Create(aName, aEMail: String; aBirthDate, aRecordDate:
DateTime);
begin
inherited Create;
FRecordDate := Clock.Now;
{...}
Engine.ExecuteOperation(
AddPeople.Create('FernandoVM',
'fernandovm@users.sourceforge.net', Convert.ToDateTime('25/10/1979'), Clock.Now)
);
O XPrevail recomenda que Clock deve ser uma referência externa aos objetos
de negócios, do tipo IClock , para que eventualmente possa ser setada com implementações
diferentes de obtenção de data/hora. Caso esse detalhe tenha ficado um
pouco confuso recomendo a análise do código fonte do exemplo.
Pronto! Está acabada nossa primeira aplicação prevalente. Opa, aliás,
falta vermos como podemos realizar consultas, mas essa é a parte mais
fácil, basta interarmos peolos objetos mantidos pela classe PopleManager, tal qual é mostrado abaixo:
var
Arr : ArrayList;
I : Integer;
begin
Arr := PManager.Peoples;
for I := 0 to Arr.Count - 1 do
begin
with (Arr[I] as People) do
begin
Console.WriteLine('Name:{0} Email{1}', Name,
Email);
end;
end;
O trecho acima exemplifica uma consulta, com resultado sendo exibido no
console. Como a consulta é uma operação que não altera o estado
do PrevalentSystem ela pode ser realizada diretamente, sem intermédio do
PrevalenceEngine. Da mesma forma que a prevalência dispensa a
utilização de bancos de dados, também dispença o uso de SQL, sempre
estaremos acessando diretamente os objetos em memória.
Agora sim fechamos esse primeiro tutorial. Não deixe de acessar o exemplo
HelloPrevalence disponível na pasta samples da distribuição do XPrevail.