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.