Marco Baccaro
MCP • MCT • MCAD • MCSD • MCTS • MCPD Enterprise App Dev
Team System com metodologias ágeis
Para quem me conhece sabe que Team System é um produto no qual admiro bastante, bem como boas práticas de desenvolvimento de software e agilidade, segue abaixo o compartilhamento de alguns conteúdos bem interessantes.
Tell, don’t ask
Precisamos ter em mente que devemos nos esforçar a dizer aos objetos que desejamos, não fazer-lhes perguntas sobre seu estado, tomar uma decisão, e em seguida dizer-lhes o que fazer, isso é o que o termo Tell, don’t ask quer nos dizer. Veja uma situação, dentre muitas que passa despercebido pela maioria dos desenvolvedores.
while (itens.HasMoreElements) {
string name = itens.NextElement();
}
Analizando o código acima, se o atributo HasMoreElements é igual a true, então a tentativa de obter o próximo elemento da coleção deve funcionar. Agora, se esse código acima estiver sendo executado em um ambiente multi-thread, sem o uso correto de sincronização (lock), facilmente a invariante HasMoreElements não se sustente, de modo que alguma outra thread capiturou o último elemento antes da sua thread. Onde erramos nesse exemplo? Simplesmente não deveria ser utilizado uma informação do estado da coleção para tomar uma ação, mas sim deixá-la se gerenciar a si mesma. De certa forma quebramos o seu encapsulamento. Vamos para mais um exemplo, que de forma semelhante pecamos no dia-a-dia.
PedidoDTO pedido = new PedidoVendaDTO();
ProdutoDTO item = new ProdutoDTO() { Descricao = "AEIOU", ValorUnitario = 10.00, Qtde = 1 };
pedido.Carrinho.Add(item);
É muito comum se ver a utilização direta de uma propriedade do tipo coleção ser utilizada do lado de fora. O que acontece com esse exemplo, se preciso atualizar o total do pedido para alimentar uma outra propriedade chamada TotalPedido? Ou, realizar a reserva do item no banco de dados para essa transação? Complicou… pois o objeto PedidoVendaDTO está sendo manipulado externamente, em outras palavras, invadido. Quando deveríamos fazer algo do tipo:
PedidoDTO pedido = new PedidoVendaDTO();
ProdutoDTO item = new ProdutoDTO() { Descricao = "AEIOU", ValorUnitario = 10.00, Qtde = 1 };
pedido.AdicionarItem(item);
pedido.RemoverItem(item);
pedido.Carrinho; // Propriedade ReadOnly.
Objetos devem falar uns com os outros através de métodos. Quando eles começam a se comunicar por meio de atributos (propriedades) é indício que as coisas podem não estar indo bem. Em Domain-Driven Design o conceito de Agregados faz muito sentido, pois quem deve gerenciar o ciclo de vida dos atributos agregados é a entidade raiz. É comum encontrarmos a colocação que orientação a objeto é um modo de expressar entidades do mundo real dentro do software. Mas no mundo real quando vamos comprar um lanche, via de regra entramos em uma fila, solicitamos o nosso pedido, pagamos e aguardamos a entrega. Mas parecem que nossos objetos se comportam como se, cortamos a fila, fazemos o pedido e efetuamos o pagamento, e depois vamos no cozinha pegamos o lanche e vamos embora. Até a próxima.
Aggregates
Indo diretamente ao ponto, quando trabalhamos com nossos objetos, criando, processando, reconstruindo (tirando do relacional para a mémória)… Como saber onde um objeto formado por outros objetos começa ou termina? Em sistemas de armazenamentos, bem como banco de dados relacionais, temos diversas formas de travamento em relação ao limites. Pois para uma transação teremos chaves e dependências que nos guiaram no começo e término das tabelas. E nos objetos? Quem direciona os limites e o ciclo de vida de uma informação? Na relação de uma entidade com seu atributos tem que ser claro e transparente. Ex: Se excluirmos uma entidade Pessoa com nome, data de nascimento, sexo… devemos apagar o endereço também? Será que ninguém mais tem referência a esse endereço?
Em um modelo dirigido por domínio, utilizamos o conceito de Agregado , que é um grupo de objetos associados que tratamos como sendo uma unidade para fins de alterações de dados, de forma que cada agregado possui um entidade raiz e um limite (o que está dentro do agregado). Os agregados demarcam o escopo dentro do qual as invariantes (invariante: algo que não tem variação), tem que ser mantidas em cada estágio do ciclo de vida.
Exemplo:
Uma loja de peças automotiva que utiliza o modelo de um carro. O carro é uma entidade com uma identificação global (id), queremos distinguir esse carro de todos os outros carros do mundo. É possível que queiramos rastrear o histórico de rotação dos pneus nas quatro rodas. Para saber qual pneu é qual, eles também devem ser entidades com seus respectivos códigos identificadores. Mas é bastante improvável que nos preocupamos com a identidade desses pneus fora do contexto daquele carro específico. Se substituirmos os pneus e enviarmos para uma pilha anônima de descarte e reciclagem, o sistema não precisará mais rastreá-lo. O que acontece nesse caso é que o carro é a entidade raiz do agregado, cujo o limite também inclui os pneus.
Veja o diagrama ilustrativo direto do livro do Evans do exemplo citado acima:

Certamente precisamos de uma boa abstração e compreensão do negócio por parte do modelador para um entendimento correto das entidades raízes (roots) e seus objetos agregados. Não existe um cookbook para uma boa modelagem, mas abaixo estão algumas práticas de utilização de agregados:
- A entidade raiz deve possuir uma identificação global e é em última instância responsável por verificar invariâncias.
- As entidades dentro do limite possuem uma identificação local e única somente dentro do agregado.
- Nada fora do limite do agregado pode fazer referência a qualquer coisa dentro, exceto à entidade raiz. A entidade raiz pod fornecer referência às entidades internas para outros objetos, mas esses objetos podem utilizá-las apenas de forma transitória, sem reter a referência.
- Somente raízes dos agregados podem ser obtidas diretamente através de consultas ao banco de dados. Todos os outros objetos devem ser achados através de travessia de associações.
- Uma operação de exclusão deve remover tudo dentro do limite do agregado de uma só vez.
Agrupe as Entidades e Objetos de Valor em agregados e defina os limites em torno do cada um. Escolhe uma Entidade para ser a raiz de cada agregado e controle todo o acesso aos objetos dentro do limite (escopo) através da raiz. Permita que objetos externos façam referência somente raiz, por exemplo: Não permitir utilizar o objeto Endereco diretamente sem a utilizar a entidade raiz Pessoa).
Padrões como Fabrics e Repositories nos ajudam a operar sobre os agregados, encapsulando a complexidade de transições específicas do ciclo de vida. De forma curta e objetiva, Agregado é um termo usado para definição das propriedades do objeto e o limites entre os objetos e seus relacionamentos. Boa modelagem e até o próximo post. Hugs.
Fechamento de Operações
É correto dizer que computação é da área da exatas. Embora muitas vezes de forma trágica ou cômica parece não ser (algumas vezes o 1 + 1 não retorna 2), isso devido a problemas de ambiente, bugs, intervenção humana e N outros fatores. Esse comentário foi apenas nos introduzirmos aos assunto da exatas (matemática). Quando efetuamos a multiplicação entre dois números reais (números racionais e irracionais), obtemos um outro número real. Como isso sempre é verdade, dizemos que os números reais são “fechados sob a operação de multiplicação”. Baseado nessa condição temos o conceito chamado FECHAMENTO DE OPERAÇÕES. Esse conceito traz uma enorme simplificação e interpretação de uma operação, de modo que recebendo um tipo de argumento o retorno é do mesmo tipo. Por exemplo, o uso de XSLT é transformar um documento XML em outro documento XML.Sempre que possível, defina uma operação que o tipo de retorno seja do mesmo tipo do(s) argumento(s). Se o implementador tiver um estado que é utilizado na computação, ele é efetivamente um argumento da operação. Por isso, o(s) argumento(s) e o valor de retorno devm ser do mesmo tipo que o implementador. Uma operação como essa é fechada sob o conjunto de instância daquele tipo. Uma operação fechada proporciona uma interface avançada sem introduzir qualquer dependência de outros conceitos, logo, bem objetiva e coesa.Esse padrão é mais utilizado com OBJETOS DE VALOR, do que com ENTIDADES, já que ENTIDADES tem um ciclo de vida importante no DOMINIO e não se pode simplesmente criar-se um novo. Podemos ter algumas exceções, mas de no geral ENTIDADES não são resultados de uma computação. Quanto mais procuramos reduzir interdependências e aumentar a coesão, mais próximos estamos desse padrão. Uma situação válida na fechamento de operações é o retorno de um tipo abstrato, por exemplo: argumento Funcionário e retorno Pessoa. O mesmo se aplica em um retorno de um objeto básico do framework, que elimina qualquer dúvida da mente de quem utiliza.
Exemplo de uma operação fechada:
class Program {
static void Main(string[] args) {
// Salarios fixos de exemplo.
List<double> salaries = new List<double>() {
100, 900, 5000, 9000, 490, 700, 3000, 270, 300
};
List<double> lowSalaries = GetLowSalary(salaries);
}
static List<double> GetLowSalary(List<double> salaries) {
List<double> lows = new List<double>();
salaries.ForEach(delegate(double salary) {
if (salary <= 500) {
lows.Add(salary);
}
});
return lows;
}
}
Nesse exemplo acima, o método GetLowSalary retorna e um subconjunto da coleção salário, sendo assim uma operação fechada, fechada para outras referências e fechada para complexidade e falta de entendimento. Claro que é um exemplo sem regra de negócio qualquer, mas poderia ser uma operação complexa com uma computação avançada, porém com alta coesão. Podemos ter em nossos objetos complexos operações fechadas, enriquecendo nossas interfaces com objetividade, bem como o .NET Framework faz, principalmente nas coleções nativas. Veja alguns exemplos:
class Program {
static void Main(string[] args) {
// Exemplo de fechamento com classe abstrata.
// Pois o tipo string[] é um Array.
string[] itens = new string[5] { "A", "E", "I", "O", "U" };
Array vetor = null;
itens.CopyTo(vetor, 5);
// Exemplo simples, objeto string que recebe string e
// que retorna uma string após a computação.
string vogais = "@BCDE";
vogais = vogais.Replace("@", "A");
}
}
Chegamos ao fim do post, que teve o intuito de trazer a frente o conceito matemático de fechamento de operações para o mundo da computação. Assunto esse abordado em DDD por Eric Evans. Pouco mais de cultura e entendimento para melhores práticas, e aprimoramento continuo na coesão no desenvolvimento de softwares. Até +.
Expression Linq
Existe uma classe responsável para cada parte de uma expressão lógica. A tabela abaixo destaca as respectivas classes.
| Expression | Fornece um método estático para criar outros objetos de expressão. |
| BinaryExpression | Representa uma expressão com uma operador binário. Ex: >, <, !=, == e etc. |
| ConstantExpression | Representa uma referência constante em uma expressão. Ex: 50 de typeof(int), “Olá” de typeof(string) e etc. |
| MemberExpression | Representa a referência de um membro (membro obtido por reflection). Ex: Propriedade qualquer de um objeto, x.Id > 10. |
| UnaryExpression | Representa uma operação unária na expressão. Ex: Negativo de um valor. |
A imagem abaixo ilustra a divisão lógica da expressão, no qual cada item é composto por uma classe.

Para ilustrar a utilização do consultas linq dinâmicas, criei um exemplo utilizando linq to object no qual a condição da filtragem (clausula where), será criado em tempo de execução. Para o exemplo criei uma entidade complex type chamado Student para o exemplo:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ExpressionLinqSample {
public class Student {
public int Id { get; set; }
public int Registry { get; set; }
public string Name { get; set; }
}
}
Nessa classe que denominei DynamicLinq, crio a expressão linq baseado nos parâmetros informados pelo usuário na UI:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;
namespace ExpressionLinqSample {
public class DynamicLinq {
static public Expression<Func> MakeExpression(int field, int paramFromUi) {
Type type = typeof(Student);
ParameterExpression paramExp = Expression.Parameter(type, "Student");
MemberInfo studentyProperty = null;
switch (field) {
case 1:
studentyProperty = type.GetProperty("Id");
break;
case 2:
studentyProperty = type.GetProperty("Registry");
break;
}
MemberExpression memberExp = Expression.MakeMemberAccess(paramExp, studentyProperty);
ConstantExpression constExp = Expression.Constant(paramFromUi, typeof(int));
BinaryExpression binExp = Expression.GreaterThanOrEqual(memberExp, constExp);
return Expression<Func<Student, bool>>.Lambda<Func<Student, bool>>(binExp, paramExp);
}
}
}
Esse é método Main de uma aplicação console para iniciar o programa e consumirmos o método de criação de consulta dinâmica. Veja que o expression delegate é criado a partir da consulta dinâmica criado. Como um delegate, ele executará o algoritmo confeccionado em runtime.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
namespace ExpressionLinqSample {
class Program {
static void Main(string[] args) {
List<Student> students = new List<Student>() {
new Student{Id = 10, Registry=100, Name="Paulo"},
new Student{Id = 20, Registry=200, Name="Pedro"},
new Student{Id = 50, Registry=300, Name="Felipe"},
new Student{Id = 60, Registry=400, Name="Lucas"}
};
Expression<Func<Student, bool>> expression = DynamicLinq.MakeExpression(1, 50);
IEnumerable resultStudents = students.Where(expression.Compile());
foreach (Student item in resultStudents) {
Console.WriteLine(item.Name);
}
}
}
}
Veja abaixo o conteúdo da instância do delegate chamado “expression” na janela Immediate do Visual Studio, nada mais que uma instrução lambda final:

De forma totalmente dinâmica criamos a condição linq “tal que Student.Id seja maior ou igual a 50″. E assim poderíamos ter criado inumeras outras condições que satisfaça uma regra de negócio, bem como formulário de pesquisa baseado nos filtros desejado pelo usuário. Até +.
Covariancia, Contravariancia e Invariancia
Caríssimos, provavelmente essas palavras do título do post não seja algo muito comum para você. Ao menos a chance é grande. Acho isso porque muitas vezes temos o entendimento e compreensão de algumas coisas, mas não sabemos o nome delas. Para iniciar, vamos dizer que os dados (nossos objetos), podem ser de três tipos, são eles:Covariância: Conversão entre tipos de dados efetuados somente com base na herança entre os tipos de dados, e que segue o sentido “Generalização”, do objeto especializado para o generalizado. Exemplo:
// Lembre-se que string herda de object no framework! object instanceObject = new object(); string instanceString = "123 Testando"; //Cast implicito. instanceObject = instanceString;
Contravariância: Conversão entre tipos de dados efetuada somente com base no sentido inverso da herança entre os tipos de dados. Nesse caso uma “Especialização”, do objeto generalizado para o especializado. Exemplo:
// Lembre-se que string herda de object no framework! object instanceObject = new object(); string instanceString = "123 Testando"; //Cast explícito obrigatório. instanceString = (object)instanceObject;
Invariância: Restrição da conversão entre tipos de dados. Exemplo:
List<object> objectList = new List<object>(); List<string> stringList = new List<string>(); objectList = (List<object>)stringList;
O framework identifica como um erro, isso por questão de segurança. Isso está correto? Sim. Quando lidamos com generics, um tipo List<object> é um tipo, como List<string> é outro, não existe vínculo de herança entre eles. Se tentar fazer um cast explícito conforme o exemplo abaixo, em design time passa, mas em tempo de runtime ocorrerá um erro InvalidCastException.
objectList = (List<object>)stringList;
No framework .Net, quando trabalhamos com generic as interfaces são invariantes por padrão, e as classes concretas que implementam um interface genérica sempre serão invariantes. Como no caso das interfaces genéricas é um comportamente padrão, podemos alterá-los, permitindo conversão entre interfaces. Para isso temos que utilizar os recursos in e out.
interface IStoreWrapper<T> {
void SetData(T data);
}
interface IRetrieveWrapper<T> {
T GetData();
}
class Wrapper<T> : IStoreWrapper<T>, IRetrieveWrapper<T> {
private T storedData;
void IStoreWrapper.SetData(T data) {
this.storedData = data;
}
T IRetrieveWrapper.GetData() {
return this.storedData;
}
}
Wrapper<string> stringWrapper = new Wrapper<string>();
IStoreWrapper<string> storedStringWrapper = stringWrapper;
storedStringWrapper.SetData("Hello");
IRetrieveWrapper<string> retrievedStringWrapper = stringWrapper;
Esse exemplo acima funciona? Resposta: Não. O motivo é o mesmo citado no início, segurança. Para resolver o esse problema, façamos a seguinte modificação:
interface IRetrieveWrapper<out T> {
T GetData();
}
Em situações como esta, onde o tipo de parâmetro é apenas de retorno (get), você pode informar ao compilador que alguns conversions implícitas são legais e não restringir. Você faz isso especificando a palavra chave out. Em caso contrário, onde queremos apenas o set no métodos das interfaces genéricas, no lugar o out colocamos o in. Um exemplo disso é o contrato IComparable, que precisa receber dois tipos impostos na declaração do generics para compará-los. Exemplo:
public interface IComparer<in T> {
int Compare(T x, T y);
}
Na prática:
Object x = ...; Object y = ...; ObjectComparer<object> comparer = new ObjectComparer<object>(); IComparer<object> objectComparator = objectComparer; int result = objectComparator(x, y); IComparer<object> stringComparator = objectComparer;
Sei que essa história toda pode parecer confusa, mas é assim que acontece na prática e tudo isso faz sentido. Porém, muitas vezes nos deparamos com erros de conversões e não imediato não compreendemos. Tendo esse entendimento em mente irá nos auxiliar a compreender melhor os erros de conversões no dia-a-dia. Hugs!
CreateInstanceAndUnwrap
Esse método nos permite trabalhar o conceito do plugin pattern, sem utilizar mecanismos avançados ou refletion. Basta informarmos o nome do assembly existente na pasta do projeto e mesmo não havendo referencia do mesmo o método CreateInstanceAndUnwrap se encarregar do serviço.
Para ilustrar a simplicidade do uso montei uma solução com os seguintes projetos: CreateInstanceAndUnwrap project como o projeto main para a execução do exemplo, NotificationContract project para um assembly exclusivo para a interface de contrato e por último o NotificationImplements que faz referência ao NotificationContract e implementa a interface de contrato. Veja abaixo a imagem da solução:
Aqui temos a construção da interface para notificação, dentro do projeto NotificationContract.
namespace NotificationContract {
public interface INotification {
void Notify(string message);
}
}
Agora no projeto NotificationImplements, adiciono a referência ao projeto NotificationContract, crio a classe concreta para notificação implementando a interface de contrato INotification.
namespace NotificationImplements {
public class Notification : INotification {
public void Notify(string message) {
Console.WriteLine(message);
}
}
}
Por último, temos o projeto CreateInstanceAndUnwrap para execução do teste, no qual tem referência apenas ao projeto da interface de contrato. O código abaixo vai instanciar e invocar o método da classe concreta, sem referência de projeto, o que podemos dizer, “plugado”.
public class Program {
static void Main(string[] args) {
string assemblyName = "NotificationImplements, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
string typeName = "NotificationImplements.Notification";
INotification notification = (INotification)AppDomain.CurrentDomain.CreateInstanceAndUnwrap(assemblyName, typeName);
notification.Notify("Call method less internal reference.");
}
}
A idéia é válida, a necessidade de plugar componentes muitas vezes existe e a solução da aplicabilidade é simples e rápido. Esse modo de resolver é através do framework. Net por si só. Temos outros meios, tal como MEF, que nos trás features adicionais e possibilidade de utilização de técnicas interessantes como Service Locator e Dependency Injection. Fica aí a idéia, até o próximo post.
Specification Pattern
Quando existe mais de uma forma de executar um processo, uma abordagem é tornar o próprio algoritmo parte essencial dele, um objeto por si só. A chave para distinguir um processo que deve se tornar explícito de um processo que se deve permanecer oculo é simples: “Será que ele é algo sobre o qual os especialistas do domínio falam, ou será que ele é simplesmente parte do mecanismo do software?”
Foi em situações como essa que nasceu o pattern Specification, que fornece uma maneira concisa de expressar determinados tipos de regra, separando-as da lógica condicional e tornando-as explícitas pra o domínio, que é o coração vital de um software (DDD). O conceito básico do pattern Specification é bastante simples e nos ajudar a pensar sobre um problema encontrado na modelagem de domínios: “As regras de negócio geralmente não se enquadram na responsabilidade de nenhuma ENTIDADE ou OBJETO DE VALOR óbvio e sua variedade de combinações podem sobrecarregar o significado básico do objeto de domínio”. Retirar essas regras da camada de domínio é ainda pior, pois o código do domínio passa a não expressar o modelo.
Com o conceito criamos objetos de regras separados e combináveis, que podemos chamá-los de “predicados”. Colocando de outra forma, esse novo objeto é um Specification. Um Specification declara um restrição sobre o estado de outro objeto, que pode ou não estar presente, sendo utilizado para verificar se ele satisfaz os critérios especificados. Specification são normalmente testes simples com propósitos especiais, como por exemplo, “Validar se um cliente é inadimplente”. Em casos com regras complexas, o conceito pode ser ampliado para permitir combinações de especificações simples, assim como os predicados são combinados com operadores lógicos.
Aplicando o Specification
Uma grande particularidade o Specification é que ele pode unificar funcionalidades diferentes dentro do aplicativo. Talvéz seja necessário especificar o estado de um objeto para um ou mais dos três propósitos:
1- Validar um objeto para verificar se ele preenche alguma necessidade ou está pronto paa algum propósito.
2- Selecionar um objeto a partir de uma coleção (Busca em banco de dados).
3- Especificar a criação de um novo objeto para atender alguma necessidade, (Combinado com Fábricas).
Validação:
A utilização mais simples de um specification é a validação, é o uso que demonstra o conceito de forma mais direta. Exemplo: um sistema de e-commerce que precisa analisar um algoritmo que levanta uma bandeira vermelha, caso o cliente seja inadimplante.
Seleção:
A seleção de um subconjunto de uma coleção com base em alguns critérios. Exemplo: Como no exemplo acima, só que precisamos selecionar os clientes inadimplantes dentro de um repositório. Nesse caso é vincularmos uma Specification de validação dentro de um Repository, fazendo um filtro de negócio na consulta de objetos.
Abaixo, ilustro um exemplo prático do Specification Pattern. Nesse cenário, uma empresa faz o carregamento e transporte de cargas. Para isso, utiliza-se containers, no qual tem especialidades, tais como: container blindado, normal ou ventilado. Pois dependendo do produto embalado dentro de barris, serão alocados dentro desse containers para o transporte. Mas um produto químico precisa de ventilação, bem como um explosivo precisa estar em um container blindado, e assim por diante. Vejam o diagrama de classe desse nosso caso de exemplo:

Baseado no diagrama de classe acima, criei um exemplo prático em Java como ilustração em um caso prático.
Criação do classe Container:
public class Container {
private List contents;
private EnumSet features;
public List GetContents() {
return contents;
}
public Set GetFeatures() {
return features;
}
public Container() {
features = EnumSet.noneOf(ContainerFeatures.class);
contents = new ArrayList();
}
public boolean IsSafetyPacked() {
Iterator iterator = GetContents().iterator();
while(iterator.hasNext()) {
Drum drum = iterator.next();
if (!drum.GetProduct().GetSpecification().IsSatisfiedBy(this)) {
return false;
}
}
return true;
}
}
Classe ContainerSpecification (Specification Pattern):
public class ContainerSpecification {
private ContainerFeatures requiredFeature;
public ContainerSpecification(ContainerFeatures feature) {
requiredFeature = feature;
}
public boolean IsSatisfiedBy(Container aContainer) {
return aContainer.GetFeatures().contains(requiredFeature);
}
}
Enumerador para os tipos de containers:
public enum ContainerFeatures {
AMORED,
VENTILATED,
NORMAL
}
Classe Drum (Barril que inseridos nos containers):
public class Drum {
private Product product;
public Product GetProduct() {
return product;
}
public void SetProduct(Product product) {
this.product = product;
}
}
Classe abstrata Product (Que são adicionados em barris e que tem característica de armazenamento):
public abstract class Product {
private ContainerSpecification specification;
public ContainerSpecification GetSpecification() {
return specification;
}
public void SetSpecification(ContainerSpecification specification) {
this.specification = specification;
}
}
// Produto especilizado explosivo
public class Tnt extends Product {
}
Executando na prática:
public class Samples {
public static void main(String[] args) {
RunSpecificationPattern();
}
public static void RunSpecificationPattern() {
// Criação de container com especificação do tipo blindado (ARMORED).
Container container = new Container();
container.GetFeatures().add(ContainerFeatures.AMORED);
// Criação de produto TNT com especificação de
// armazenamento em container blindado (ARMORED).
Tnt product = new Tnt();
product.SetSpecification(new ContainerSpecification(ContainerFeatures.AMORED));
// Criação de uma barril de armazenamento com inclusão do produto TNT.
Drum drum = new Drum();
drum.SetProduct(product);
// Adição do barril ao container.
container.GetContents().add(drum);
if (!container.IsSafetyPacked()) {
System.out.println("Packed failed!");
}
}
}
Bom, espero ter conseguido de forma resumida apresentar e expressar a boa utilidade desse padrão, que é uma técnica para manter regras dentro do domínio, sem poluir os objetos, dando a eles responsabilidades muitas vezes a sobrecarregar, porque precisamos achar um “dono” para o um processo. Eu particularmente me deparei muitas vezes com essa situação enquando modelava (… e ainda modelo!) domínios de software. Até o próximo post!
WS-BPEL
A linguagem WS-BPEL (Web Services Business Process Execution Language) é um padrão criado pela OASIS para a execução de processos de negócios, descrevendo como ocorre o relacionamento entre diversos web services participantes de uma composição simples ou complexa. A programação com WS-BPEL é parecida com a programação das linguagens de programação já existentes, na medida me que ela oferece determinados tipos de construções como estruturas de repetição, condicionais, variáveis e atribuições. Este fato possibilita que o processo de negócio seja visto como um algoritmo. O uso desses recursos é simples e limitado, objetivando a facilidade de uso e aprendizagem da mesma.
Uma composição de serviços especificada em WS-BPEL é vista como serviço, ou seja, quem usou um processo de negócio definido em WS-BPEL não tem a ciência que na verdade está usando vários serviços para realizar uma determinada operação. Por ser um web service, a composição necessita ter sua interface baseada em WSDL, troca de mensagens com outros web services através do protocolo SOAP e pode ser cadastrado em um repositório UDDI para fins de consulta. O padrão WS-BPEL assume que os serviços participantes da composição estejam descritos usando WSDL.
Um processo BPEL pode ser síncrono ou assíncrono. Um processo BPEL síncrono bloqueia o cliente (aquele que está usando o processo) até que o processo termine e retorne um resultado para o cliente. Um processo assíncrono não bloqueia o cliente. Em vez disso, ele usa um retorno de chamada para retornar o resultado (se houver). Geralmente, os processos assíncronos são usados para processos de longa duração, e os síncronos são usados para processos que retornam um resultado em um tempo relativamente curto.
As funcionalidades mais importantes de WS-BPEL são listadas a seguir:
• Descrever a lógica dos processos de negócio através da composição de serviços;
• Tratamento de operações de modo síncrono ou assíncrono;
• Invocar serviços de maneira seqüencial ou paralela.
• Prover mecanismos para tratar erros durante a invocação de serviços;
• Manter atividades de longa duração (transacionais), assim como interrompe-las;
• Reiniciar uma composição que foi interrompida ou apresentou erros;
• Agendar a execução de atividades e definir a ordem em que elas irão executar.
Conceitos do BPEL
Existem alguns elementos fundamentais da linguagem WS-BPEL que podem ser utilizados para a especificação de processos de negócio. A estrutura geral de um Processo de Negócio especificado em WS-BPEL é formada por quatro seções: PartnerLinks, Variables, FaultHandlers e Activities.
PartnerLinks: seção que define os diferentes parceiros (partes envolvidas) que interagem com o Processo de Negócio durante toda sua execução. Eles são usados para identificar a funcionalidade que deve ser oferecida por cada serviço parceiro. As ligações de parceiros (partner link) devem estar associadas a um tipo de ligação entre parceiros (partner link type) definidos na especificação de web services no WSDL;
Variables: seção que define as variáveis de dados usadas pelo Processo de Negócio. As definições são feitas em termos de tipos de mensagem WSDL, elementos ou tipos simples de esquemas XML. Elas são usadas para manter os dados de estado e o histórico do processo com base nas mensagens trocadas. As variáveis devem estar associadas a tipos de mensagens (messages) definidos na especificação WSDL;
FaultHandlers: seção que contém os tratadores de falhas que definem as atividades a ser executadas em resposta às falhas resultantes da invocação de serviços de avaliação e de aprovação;
Activities: seção que contém a descrição do comportamento normal para a execução do Processo de Negócio. Existem basicamente dois tipos de atividades:
• Basic Activity: tipo de atividade usado para executar alguma operação. Algumas dessas atividades básicas envolvem a interação com algum parceiro, as quais são: invoke, receive e reply. Outras dessas atividades básicas são executas sem a interação com qualquer parceiro, as quais são: wait, terminate, assign, empty, throw e compensate;
• Structured Activity: tipo de atividade usado para agrupar atividades básicas (basic activities) dentro de algumas estruturas de fluxo. Tais atividades são: while, pick, flow, sequence, switch e scope.
Abaixo está um gráfico de exemplo de um processo BPEL.

Repository
Pensando em DDD, Repository é um padrão conceitual simples para encapsular soluções de SQL, mapeamento de dados (ORM, DAO, e etc), fábricas e trazer de volta o foco no modelo. Quando o desenvolvedor tiver construído uma consulta SQL, transmitindo essa consulta para um serviço de consultas da camada de infraestrutura, obtendo um RecordSet que extrai as informações e transmite para um construtor ou fábrica, o foco do modelo já não existe mais. Repository representa todos objetos de um determinado tipo como sendo um conjunto conceitual. Ele age como uma coleção, exceto por ter recursos mais elaborados para consultas. Essa definição une um conjunto coeso de responsabilidades para fornecer acesso às raízes dos agregados desde o início do clico de vida até o seu final. Em outras palavras, criar uma entidade com seus atributos agregados, por exemplo: Endereço > Logradouro > Bairro > Cidade >> Estado >> País.
Uma Fabric cuida do início da vida de um objeto, um Repository ajuda a controlar o meio e o final. Nesse caso o Repository cria objetos baseados em dados, vindos de um banco de dados, arquivo ou outros, o que no nível técnico não deixa de ser um Fabric. Mas para o DDD, Fabric e Repository tem responsabilidades distintas, Fabric cria novos objetos e Repository encontra objetos antigos, dando sempre a ilusão de que estão em memória. O que podemos fazer com elegância é combinar os dois patterns.
Umas das principais vantagens do Repository são:
| Oferecer aos clientes um modelo simples para obter objetos persistentes e controlar seu ciclo de vida. |
| Tomam o design do aplicativo e do domínio e os desacoplam da tecnologia de persistência, de várias estratégias do banco de dados e até de várias fonte de dados. Ex: Database, Web Services e Xml. |
| Comunicam decições de design sobre acesso a objetos. |
| Permitem fácil substituição de uma implementação fake para o uso de teste (como uso de coleção em memória). |
Ofereça métodos que selecionam objetos baseados em critérios e retornem objetos completamente instanciados ou coleções de objetos cujos valores de atributos satisfaçam os critérios. Crie Repositórios somente para as entidades raízes de agregados que realmente precisem de acesso direto, ou seja, nem todas as entidades precisam de um repositórios e objetos de valor devem ser criados por Fabrics.



