Você realmente precisa de um banco de dados? Conheça o
XPrevail
.
By
Fernando Vasconcelos Mendes
Resumo:
Este artigo tem por objetivo falar um pouco sobre o conceito de prevalência
e como ela pode ser uma alternativa ideal ao uso de banco de dados em várias
situações. Conheceremos o XPrevail
, um framework de prevalência open
source
desenvolvido em Delphi for .NET e como seus recursos podem possibilitar uma
programação simples e moderna. Meu desejo é que esse artigo atue como uma
carta de apresentação do projeto XPrevail
a nossa comunidade de desenvolvimento de software.
Download do exemplo aqui.
Download do
XPrevail.
O que é prevalência?
O conceito de prevalência foi originalmente idealizado pelo brasileiro
KlausWuestefeld
e concretizado no projeto Prevayler
, uma implementação em Java. Ele é uma união de
antigos conceitos já aplicados isoladamente em outros cenários.
Como mencionei no resumo, a utilização da prevalência é uma alternativa
ao uso de 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;
- A drástica redução de custos de uma solução (elimine aqui custos
de servidor par o banco de dados (hardware), licenças do software servidor
de banco de dados, custo operacional de manutenção e etc);
- Induz um desenvolvimento verdadeiramente orientado a objetos (na minha
opinião a mais interessante).
Não deixe de visitar a seção “
O que é?” nosite
do projeto
XPrevail
.
Conceitos chaves
Para entendermos o funcionamento geral da prevalência é importante que
antes conheçamos alguns termos comumente utilizados dentro desse conceito,
dessa forma, vejamos a seguir os principais deles.
PrevalenceEngine
: Trata-se de uma classe interna dos framework’s de prevalência, que é
responsável por todo o processo de execução, registro e recuperação das
alterações realizadas no PrevalentSystem
. Muitas vezes a referenciamos simplesmente como “
engine
”. Veremos mais adiante que o
XPrevail
disponibiliza quatro tipo de
engines
diferentes, cada um com suas particularidades.
PrevalentSystem
: Uma classe definida pelo usuário que deve referenciar todos os objetos de
negócios da aplicação, ou seja, os objetos que serão persistidos.
Os objetos nela referenciados serão
serializados
quando for solicitado ao
PrevalenceEngine
um snapshot. Dessa forma, qualquer objeto fora dessa classe terá seu estado
perdido.
Snapshot
: É o processo no qual o
PrevalenceEngine
serializa
todo o
PrevalentSystem
(na realidade veremos mais adiante que o
XPrevail
suporta o trabalho com múltiplos
PrevalentSystem
’s), tal qual ele está no momento da solicitação. Isso dá origem um
novo arquivo de snapshot, e torna desnecessária a leitura dos arquivos
de log
de operações anteriores.
Operation
: Pelo conceito original proposto pela prevalência, toda alteração a ser
realizada nos objetos mantidos por um PrevalentSystem
deve ser feita através de objetos de classes que implementam a interface
IOperation
definida pelo framework. Essas instâncias são passadas ao
PrevalenceEngine
para registro e execução. No projeto
prevayler
a interface equivalente chama-se
Transaction
. (ver em
manifest
mais detalhes).
Fluxo de execução da prevalência
Para entendermos melhor o conceito, vou descrever agora qual seria o fluxo de
execução de uma aplicação prevalente. Imagine que tenhamos apenas
uma classe de negócio numa dada aplicação, a classe
Client
. Vamos aos passos para tornar essa aplicação prevalente.
1.
Precisamos definir uma classe que manterá referência a nossos objetos
Client
,
ela será nosso
PrevalentSystem
e poderia chamar-se
Clients
.
2.
Deveremos declarar uma referência para oPrevalenceEngine
e
inicializá-la
, momento no qual será exigida a informação de qual classe será o
PrevalentSystem
, naturalmente devemos informar
Clients
.
3.
Ainicialização
do
PrevalentSystem
deve ser solicitada ao
PrevalenceEngine
, através da propriedade
PrevalentSystem
. Ela retornará uma instância da classe
Clients
com um estado exatamente igual ao da última execução (caso tenha havido
uma).
4.
Como foi mencionado, qualquer alteração noPrevalentSystem
, para prevalecer é exigido que seja feita através de objetos de
classes que implementem a interface
IOperation
. Assim, podemos declarar duas classes, uma chamada
AddClient
e
outra chamada
RemoverCliente
, ambas
implementando a interface
IOperation
. Respectivamente elas seriam utilizadas para adicionar e remover objetos
Cliente
doPrevalentSystem
. (Ao longo desse texto poderemos ver o que o
XPrevail
propõe para uma programação mais simples e intuitiva, não exigindo a
criação dessas classes auxiliares)
5.
Para adicionar um cliente, precisamos agora criar uma instância da classe
AddClient
, passando em seu construtor as informações necessárias
a
execução de sua tarefa. Devemos então solicitar ao
PrevalenceEngine
que execute essa operação. Isso envolverá a
serialização
do objeto
AddClient
em um arquivo destinado ao
log
de operações e posteriormente sua execução, onde de fato 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
Client
. O arquivo de
log
de operações conterá uma versão
serializada
de todos os objetos (que implementam
IOperation
) 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 operações gerados. Ele
deserializará
todos os objetos contidos nos arquivos e irá executá-los novamente. Atente
para o detalhe que os objetos mantidos nesse arquivo são os da classe
AddClient
, não os da classe
Client
. 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 solicitar um snapshot. Nesse momento o
PrevalenceEngine
irá gerar um arquivo novo, não um de operaçõ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 operações posteriores ao último snapshot e re-executará todas as
operações em cima do PrevalentSystem
obtido anteriormente. Isso resultará em um
PrevalentSystem
tal qual existia antes da falha, da forma mais otimizada possível.
Exigências da prevalência
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
. No .NET a forma mais simples e eficiente de tornar uma classe
serializável
é através do atributo
SerializableAttribute
.
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
diminui significativamente 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 do log
de operaçõ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 operações e executá-los novamente. Por isso é
recomendado realizar snapshots
periodicamente, mas tenha cuidado, durante o snapshot de um
PrevalentSystem
ele não pode ser alterado.
Um outro detalhe importante, como todos os objetos são mantidos em memória,
para trabalhar de forma eficiente com prevalência é preciso ter memória
RAM suficiente para hospedar todos os objetos de negócio do sistema.
Espero que tenham notado que percorrendo os passos descritos
anteriormente não temos nenhuma 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.
O que é o
XPrevail
?
O
XPrevail
é uma camada .NET de prevalência de objetos, amigável e
extendida
, implementada em Delphi for .NET. O framework é compatível com qualquer
linguagem .NET, além de ser freeware
e
opensource
. O tópico
ManifestoXPrevail
traz os motivos pelo qual oXPrevail
existe, bem como seus diferenciais frente a outras implementações de
prevalência.
Grande parte dos diferencias buscado por mim, frente aos outros
frameworks
de prevalência, foram em prol de uma programação mais natural, provendo um
framework tão transparente e menos intromicivo
quanto fosse possível. Procurei deixar o modelo de
programação com o XPrevail
de tal maneira que uma aplicação ficasse menos dependente dele,
facilitando a troca do mecanismo de persistência sem grandes esforços.
Vejo a persistência como um aspecto isolado de um software, não devendo influenciar significativamente em seu modelo, arquitetura e implementação. Essa ideologia foi uma constante durante a modelagem e implementação do XPrevail . Esses objetivos levaram-me a fazer algumas coisas de forma diferente, além de adicionar alguns pequenos recursos.
Dentre os principais diferencias trazidos pelo
XPrevail
posso destacar as seguintes:
Múltiplos
PrevalentSystem's
O
XPrevail
traz o
MultiSystemsPrevalenceEngine
, capaz de trabalhar com
múltimplos
PrevalentSystem
’s
baseados emproxy
’s transparentes do .NET. Esse tipo de
engine
traz a grande vantagem de não precisar definir objetos para realizar todas
as alterações no PrevalentSystem
, essas alterações podem ser feitas através de métodos definidos no
próprio PrevalentSystem
. As chamadas a esses métodos são interceptadas e automaticamente
convertidas em objetos que são, da maneira tradicional, serializados
e executados.
Ele possibilita uma boa separação de responsabilidades, de modo que cada
PrevalentSystem
mantenha operações apenas sobre objetos logicamente
relacionados. Além disso, podemos usar as classes gerentes do
próprio sistema como os PrevalentSystem's
, não precisando criar uma global envolvendo-as.
De forma resumida, o uso de múltiplos
PrevalentSystem's
ajuda a termos um código amigável e elegante, mais condizente com a boa
programação orientada a objetos.
Cobertura sobre todos os objetos de negócios
O
XPrevail
introduz, através do
ExtremePrevalenceEngine
, a possibilidade de interceptar todas as requisições realizadas aos
objetos de negócio e automaticamente transformá-las em objetos
apropriados, serializá-los
e executá-los. Com isso, podemos trabalhar de forma completamente
transparente, acessando os objetos de negócios e modificando-os diretamente,
sem a intervenção de uma classe externa, nem mesmo de um PrevalentSystem
.
Esse é um importante passo no processo de tornar o uso do XPrevail mais amigável ao código cliente, com algumas poucas exigências, poderemos fazer a prevalência dos objetos de negócios, mesmo para alterações realizadas diretamente neles. Isso também contribui para uma forma de programação mais natural e menos dependente do XPrevail ou do conceito de prevalência (Veja em roadmap a intenção doXPrevail em suportar persistência em banco de dados relacionais também).
Suporte programação orientada a aspectos – AOP
Outra característica bem interessante trazida pelo XPrevail é a possibilidade de utilizar um modelo de desenvolvimento baseado nos conceitos da programação orientada a aspectos, muitas vezes referenciada simplesmente como AOP. Grande parte do framework é baseado em proxy ’s , por sua vez, na interceptação de código, princípio básico da AOP. Dessa forma, como eu já vinha utilizando uma abordagem similar para comunicação entre proxy ’s e classes internas do framework, foi apenas questão de publicar uma maneira do código cliente obter recursos dessa abordagem, tão logo eu percebi esse potencial.
Os
engines
do framework que implementam a interface
IAspectsSupport
, atualmente o
MultiSystemsPrevalenceEngine
e o
ExtremePrevalenceEngine
, disponibilizam meios de registrar e
desregistrar
aspectos (qualquer objeto que implemente a interface
IAspect
)
.
O
XPrevail
disponibiliza dois aspectos implementados, em versão pré-liminar, que podem
já ser utilizados livremente pelo código cliente
, são eles o
TraceAspect
e
ProfileAspect
. Respectivamente, eles são capazes de registrar chamadas a métodos em
cima dos objetos de negócios e registrar os tempos levados para execução
dos mesmos. Esse é um recursos extremamente útil e poderoso, certamente ele
será ainda bem desenvolvido nas versões futuras do XPrevail
(ver
roadmap
).
Não deixe de visitar a seção “ Manifesto” nosite do projeto XPrevail .
Trabalhando com o
XPrevail
Veremos agora um exemplo completo de utilização do
ExtremePrevalenceEngine
, o principal
PrevalenceEngine
do
XPrevail
, nele é onde encontramos a real essência do framework. Antes de
prosseguir, uma leitura dos tutoriais Hello
Prevalence
e Hello
Prevalence
2
, nosite
do projeto
XPrevail
, pode ajudar bastante o entendimento do exemplo que aqui desenvolveremos.
Definindo o modelo de objetos
Antes de pensar na persistência precisamos primeiro definir o modelo de
nossos objetos de negócio. Por questões de simplicidade vamos trabalhar com
apenas um objeto de negócio,
People
.
Usaremos ele
para criar uma pequena aplicação para cadastro de pessoas.
People = class
strict private
FName
:
System.String
;
FBirthDate
:
System.DateTime
;
FEMail
:
System.String
;
public
function
ToString
: String; override;
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
BirthDate
:
System.DateTime
read
FBirthDate
write
set_NiverDate
;
constructor Create(
aName
: String;
aEMail
: String;
aBirthDate
:
DateTime
);
end;
Pronto, essa seria a definição padrão de nossa classe, contudo precisamos
fazer algumas alterações de modo que ela atenda as exigências da
prevalência e do
ExtremePrevalenceEngine
. Para isso, precisaremos fazer as seguintes modificações:
Realizadas as alterações necessárias, a interface de nossa classe
People
fica assim:
[
Serializable
]
[
BusinessObjectClassID
(
PeopleClassID
)]
People = class (
MarshalByRefObject
,
IObjectID
)
strict
private
FID :
System.String
;
FName
:
System.String
;
FBirthDate
:
System.DateTime
;
FEMail
:
System.String
;
public
function
ObjectID
: string;
function
ToString
: String; override;
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
BirthDate
:
System.DateTime
read
FBirthDate
write
set_NiverDate
;
constructor
Create(
aID
,
aName
: String;
aEMail
: String;
aBirthDate
:
DateTime
);
end;
O próximo passo é criar uma classe que será nosso
PrevalentSystem
, vejamos então a definição da classe
PeopleManager
para esse propósito.
[
Serializable
]
[
PrevalentSystemClassID
(
'{319D4955-4CE8-4443-9010-9BAE12046C24}')]
PeopleManager
= class (
MarshalByRefObject
,
IBusinessObjectFromID
)
strict
private
FPeoples
:
ArrayList
;
strict
protected
function
BusinessObjectFromID
(
BOClassID
,
ObjectID
: string):
TObject
;
public
function
AllPeoples
:
ArrayList
;
[
BusinessObjectExport
]
function
get_Peoples
(Index: Integer): People;
property
Peoples[Index : Integer] : People read
get_Peoples
;
function
get_PeopleCount
: Integer;
property
PeopleCount
: Integer read
get_PeopleCount
;
[Operation]
[
BusinessObjectExport
]
function
AddPeople
(
aID
,
aName
: String;
aEMail
: String;
aBirthDate
:
DateTime
) : People;
[Operation]
[
RealBusinessObjectRecovery
]
procedure
RemovePeople
(
aPeople
: People);
constructor
Create
;
end;
Tal qual uma classe de objeto de negócio, o
PrevalentSystem
também precisa descender de
MarshalByRefObject
e receber o atributo
Serializable
(na verdade ser
serializável
). Como o
ExtremePrevalenceEngine
é um
PrevalenceEngine
que suporta trabalhar com vários
PrevalentSystem
’s, é necessário que eles sejam marcados com o atributo
PrevalentSystemClassID
, para que o framework consiga identificá-los corretamente. Note também que
a interface
IBusinessObjectFromID
é implementada pela classe
PeopleManager
, isso é necessário apenas se o
PrevalentSystem
em questão mantém referência a objetos de negócios.
Observe a utilização de vários atributos adornando métodos da classe
PeopleManager
, vejamos
agora para que serve cada um deles.
1.
Operation
: Indica ao
XPrevail
que o método em questão realiza alterações no estado do
PrevalentSystem
. Caso um método que altere o estado do
PrevalentSystem
não seja marcado com esse atributo, a alteração não prevalecerá.
2.
BusinessObjectExport
: Indica ao framework que esse método retorna um objeto de negócio para
fora do PrevalentSystem
, dessa forma, o
XPrevail
intercepta essa chamada e retorna um
proxy
para o objeto em questão, de modo que as alterações realizadas diretamente
no objeto sejam persistidas. Observe que, como em todo mecanismo de
persistência, cabe ao programador decidir o que será persistido.
Alterações em um objeto de negócio obtido por um método sem esse atributo
não serão persistidas.
3.
RealBusinessObjectRecovery
: Indica ao
XPrevail
que ele deve recuperar o objeto a partir de uma versão duplicada dele
(maiores detalhes no quadro a seguir).
RealBusinessObjectRecovery
A prevalência de maneira geral proíbe que classes que implementem a
interface
IOperation
(ou
Transaction
no
Prevayler
), ou seja, que alterem o estado de um
PrevalentSystem
tenha referência direta a um objeto de negócio. Pois dessa forma, o objeto
de negócio será duplicado e a alteração em questão se dará nessa
versão duplica, isso causa bastantes problemas. Esse erro é bastante comum,
sendo considerado um erro de batismo da prevalência.
Pois bem, o
XPrevail
não recomenda que isso seja feito, contudo não proíbe. Ele lhe deixa a
opção de assim você modelar seu código, estando ciente de envolverá
algum custo de performance. Estando certo dessa opção, basta marcar o
método com o atributo
RealBusinessObjectRecovery
e o
XPrevail
cuidará para que isso não gere problemas inesperados. Isso será
demonstrado no exemplo.
Pronto, agora já temos nossas classes ajustadas para desenvolvermos nosso
exemplo prevalente. E o melhor, praticamente todo o código de persistência
já foi feito, ou seja, quase nenhum. Observe que as exigências do XPrevail
não são muito intrusivas, visto que são resolvidas com adornos através de
atributos e/ou implementações de interfaces. Uma eventual decisão de
remover o XPrevail
como mecanismo de persistência, ou adaptar o software a outro mecanismo,
não implica numa perda significativa de código.
Desenvolvendo uma aplicação console
Vamos agora criar uma aplicação console para ilustrarmos a utilização das
classes que definimos. Para que o texto não fique demasiadamente extenso,
colocarei apenas os trechos de implementação realmente relevantes. Para ter
acesso ao código completo basta fazer o download
do aplicativo de exemplo deste artigo. Para compilá-lo, é essencial baixar o
XPrevail
, você pode fazer isso através do link
http://xprevail.sourceforge.net/pt-br/download.htm.
O primeiro ponto a comentar a como obter uma instância inicial de nosso
PrevalentSystem
e como veremos, ela deve ser solicitada ao
PrevalenceEngine
. Mas para isso, precisamos primeiro obter uma instância do próprio
engine
. O código abaixo ilustra todo esse processo:
001. var
002.
Engine
:
ExtremePrevalenceEngine
;
003.
GenKeys
:
GenerateKeys
;
004.
PManager
:
PeopleManager
;
005.
006.
begin
007.
Engine :=
XPrevailFactory.CreateExtremePrevalenceEngine
([
typeOf
(
GenerateKeys
),
typeOf
(
PeopleManager
)],
Path.Combine
(
Environment.CurrentDirectory
, 'data'));
008.
GenKeys
:=
Engine.PrevalentSystems
[
typeOf
(
GenerateKeys
)] as
GenerateKeys
;
009.
PManager
:=
Engine.PrevalentSystems
[
typeOf
(
PeopleManager
)] as
PeopleManager
;
010. {…}
011.
end
.
As linhas de 2 a 4 contêm apenas as declarações das referências. Na
linha 007 é solicitada a criação de uma instância da classe
ExtremePrevalenceEngine
, como podemos ver, isso é feito através da classe
XPrevailFactory
. O método
CreateExtremePrevalenceEngine
requer como primeiro parâmetro um
array
de
System.Type
representando os
PrevalentSystem
’s que serão gerenciados pelo
engine
. O segundo parâmetro informa a pasta onde serão colocados os arquivos de
log
de operações e arquivos de
snapshots
.
Certamente você já percebeu que estamos utilizando um segundo
PrevalentSystem
, representado pela classe
GenerateKeys
. Que como o nome sugere, servirá justamente como um gerador de ID’s para
nossos objetos. A definição dessa classe é muito simples e pode ser vista
abaixo:
[
Serializable
]
[
PrevalentSystemClassID
('
{5CFE36DA-9C6A-495F-911B-10CD99374195}')]
GenerateKeys
= class (
MarshalByRefObject
)
strict
private
HashKeys
:
HashTable
;
public
[Operation]
function
NewKey
(
ObjectType
:
System.Type
) : Integer;
constructor
Create
;
end;
Ela assegura que seja gerado um seqüencial único para os objetos de um tipo
especificado, pode ocorrer de objetos de negócios terem ID’s iguais, desde
que sejam de tipos diferentes. Essa é uma exigência do
ExtremePrevalenceEngine
. Observe que como ela não mantém objetos de negócios, não precisamos
implementar a interface IBusinessObjectFromID
. A seguir podemos ver a implementação de seu principal método, o
NewKey
.
function
GenerateKeys.NewKey
(
ObjectType
:
System.Type
): Integer;
var
KeyID
:
Integer;
begin
Monitor.Enter
(
ObjectType
);
try
if
HashKeys.Contains
(
ObjectType
) then
begin
KeyID
:=
Integer(
HashKeys.Item
[
ObjectType
]);
Inc(
KeyID
);
HashKeys.Item
[
ObjectType
] := &Object(
KeyID
);
end
else
begin
KeyID
:=
1;
HashKeys.Add
(
ObjectType
, &Object(
KeyID
));
end
;
Result :=
KeyID
;
finally
Monitor.Exit
(
ObjectType
);
end
;
end
;
As linhas 008 e 009 obtêm as instâncias de nossos
PrevalentSystem
’s. Como
as operações do
PeopleManager
utilizará
o
GenerateKeys
, é importante que esse último seja solicitado primeiro. Isso é feito
através da propriedade
PrevalentSystems
, informando o
System.Type
do
PrevalentSystem
pretendido. Vamos agora prosseguir analisando alguns trechos chaves da
implementação de nosso aplicativo.
Adicionando uma pessoa
var
{...}
ID :
String;
begin
{...}
ID :=
Convert.ToString
(
GenKeys.NewKey
(
typeOf
(People)));
PManager.AddPeople
(
ID, Name, Email,
BirthDate
);
{...}
end;
Removendo uma pessoa
{...}
PManager.RemovePeople
(
PManager.Peoples
[
{...}
Editando uma pessoa
var
Ind :
Integer
;
Pp :
People;
Value :
String;
begin
{...}
Pp :=
PManager.Peoples
[
{...}
WriteLine
(
'');
WriteLine
(
'Type new value:');
Value :=
Console.ReadLine
;
case
1 :
Pp.Name
:= Value;
2 :
Pp.EMail
:= Value;
3 :
Pp.BirthDate
:=
Convert.ToDateTime
(
Console.ReadLine
);
else
begin
WriteLine
(
'Invalid option, exiting.');
Exit;
end
;
end
;
{...}
end;
Listando as pessoas
var
Arr
:
ArrayList
;
I :
Integer;
begin
Console.WriteLine
(
'');
Arr
:=
PManager.AllPeoples
;
for
I := 0 to
Arr.Count
- 1 do
begin
with
(
Arr
[I] as People) do
begin
Console.WriteLine
(
'[{0}] {1}', &Object(I), &Object(
ToString
));
end
;
end
;
Console.WriteLine
('
');
end;
Mostrando os resultados
Como podemos ver, todo o código é surpreendentemente simples. Vejamos agora
um pouco de nossa aplicação em execução:
Figura 1.
Abertura do exemplo
ObjectApp
Figura 2.
Adicionando uma pessoa
Figura 3.
Listando as pessoas cadastradas
Estando nosso aplicativo pronto nós podemos cadastrar, alterar e excluir
pessoas a
vontade, e o melhor, independente do que ocorra, seus dados estarão salvos.
Realize essas operações várias vezes, solicite
snapshot’s a avalie o resultado. Você pode fazer testes matando o processo
ou mesmo desligando a máquina, verás que os dados serão mantidos e
recuperados na próxima execução. Tudo isso sem usar um banco de dados!
Após realizar algumas
operções
,
solicitar um snapshot e depois mais operações podemos ver
que o
XPrevail
gerou os arquivos conforme mostrado na figura 4.
Figura 4.
Arquivos de
log
de operações e arquivo de snapshot
Suporte a Programação Orientada a Aspectos – AOP
Neste artigo apenas introduzirei esse tema, mas ele certamente será abordado
em maiores detalhes em artigos ou tutoriais posteriores.
Um dos excelentes recursos trazidos pelo
XPrevail
é o suporte a
um programação
baseada nos princípios da Programação Orientada a Aspectos – AOP. Esse
suporte é dinâmico e extensível, o processo de criação de aspectos é
simples e muito poderoso. Através dele podemos ter soluções elegantes para
geração de logs
, controle de concorrência,
profiler
, auditoria e etc.
A AOP promete preencher uma lacuna não resolvida pela POO, onde o exemplo
mais clássico citado é a geração de log
’s. Na abordagem tradicional quando necessitamos gerar um
log
do fluxo de execução de nosso aplicativo acabamos sendo obrigados a
espalhar código por várias classes. Código esse que não é o do
propósito da classe.
A AOP entende que a geração de
log
’s, assim como diversos outros problemas – incluindo a persistência, é
aspecto do software e não deve se misturar com as regras de negócio. A
implementação da AOP hoje ainda não é algo padronizado tal qual a POO,
dessa forma, podemos encontrar várias formas de abordagens e filosofias de
implementação desse conceito.
Vou mostrar agora apenas um pouquinho do potencial dessa maravilha e como
você fazer uso dela através do XPrevail
.
Utilizando o
ProfilerAspect
Suponha que queiramos fazer um
profiler
de nossa aplicação para saber quanto tempo está demorando a execução de
alguns métodos. Como você faria isso? Obviamente abriria o fonte
de aplicação e começaria a distribuir códigos de medição, claro. Mas,
vejamos como fazemos isso com o XPrevail
. Para isso alteraremos apenas duas linhas de nosso aplicativo de teste, veja:
{...}
Engine.RegisterAspect
(
ProfilerAspect.Create
(nil, false));
{...}
Essa linha registra um novo aspecto (esse já disponibilizado pelo
XPrevail
) em nosso
engine
. Seu construtor pede dois parâmetros, o primeiro é uma
stream
para registro das medições – passando
nil
ele emitirá no console. O segundo parâmetro indica se ele deve atuar
indiscriminadamente sobre todos os métodos dos PrevalentSystem
’s e objetos de negócios mantidos pelo
Engine
.
Como passamos
false
ele apenas atuará sobre os métodos marcados com o atributo
MethodProfiler
. Então, para testarmos, vamos colocar esse atributo no método
PeopleManager.AllPeoples.
{...}
[
MethodProfiler
]
function
AllPeoples
:
ArrayList
;
{...}
Pronto, feito isso basta re-compilar a aplicação e executarmos novamente.
Veja na figura 5 o resultado.
Figura 5.
ProfilerAspect
em ação
Assustador? Não, eu diria empolgante. Certamente esse é um recurso muito
poderoso. Como disse anteriormente, queria agora só deixar esse gostinho,
haverá um outro artigo ou tutorial abordando em maiores detalhes o uso e
desenvolvimento de aspectos com o XPrevail
.
Atual estado do projeto
XPrevail
Hoje o
XPrevail
está na versão 0.9.3
alpha
, melhorias e
refatorys
estão sendo feito nele constantemente. Embora seja uma versão
alpha
ela já é completamente funcional. Visite a seção “
Roadmap
” dosite
do projeto para conhecer em que direção ele vai ser conduzido.
Em linhas gerais, é possível que o
XPrevail
venha a suportar persistência em bancos de dados relacionais, de forma
transparente. Recursos como replicação, suportes eficientes a transações
e lock de objetos também estão previstos. O objetivo maior é que possamos,
através do XPrevail
, programar aplicações prevalentes, com aspectos, com tolerância à falhas
e balanceamento de carga. Inclusive suporte a dualidade, seria um tipo de
tolerância à falhas mesmo para aplicações
statefull
.
Ainda estou gradativamente incrementando a documentação das classes,
artigos e tutoriais e testes de unidades. Qualquer ajuda nesse sentido, bem
como com tradução para outros idiomas será apreciada. A divulgação do
framework através de artigos, criação de exemplos e casos de estudo serão muito bem vindas
.
Conclusão
Simples não? Fora o modelo de objeto, você viu muito código referente
a
persistência em nossa aplicação? Realmente não. Consegue calcular quanto
tempo já gastou codificando SQL dentro de suas classes
L
? Mas para mim, o mais legal é a sensação de que a persistência não
existe, que estamos livremente trabalhando com objetos, sem nos preocuparmos
com seu estado, pois eles prevalecerão.
Não deixem de visitar o
site
do projeto
http://xprevail.sourceforge.net/ e comece já a programar aplicações prevalentes.
Versão 1.0 – 23/10/2004
FernandoVM
é Borland Delphi Certified, palestrante de todas as edições da
BorCon
. Já atuou como articulista, colunista e editor técnico da revista
ClubeDelphi
. É gerente de tecnologia, arquiteto e coordenador da equipe de
desenvolvimento do produto Tactium, uma solução real-time distribuída de CTI/CRM
utilizada por mais de 90 empresas, ao longo de 20 estados brasileiros, na
Softium Informática. Criador e mantenedor do projeto XPrevail
, Pode ser contactado em
fernandovm@users.sourceforge.net
ou em http://fernandovm.blogspot.com
.