sexta-feira, 10 de setembro de 2010

sexta-feira, 3 de setembro de 2010

Contextualizando os seus testes

O Giovanni Bassi postou em seu blog sobre a técnica AAA (Arrange, Act, Assert) para criação de testes. Ele aproveita para abordar o principio de que cada teste só deve testar uma única coisa, e o quanto isso pode ser complicado.

Aproveitando o gancho, partindo do exemplo que ele apresentou da calculado, vamos implementar o método de divisão:

[TestClass]
public class TestCalculadora
{
    private Calculadora _calculadora;
    private int _resultado;

    [TestInitialize]
    public void Inicializar()
    {
        Arrange();
        Act();
    }

    private void Arrange()
    {
        _calculadora = new Calculadora();
    }

    private void Act()
    {
        _resultado = _calculadora.Dividir(10, 5);
    }

    [TestMethod]
    public void Resultado2()
    {
        Assert.AreEqual(2, _resultado);
    }
}

public class Calculadora
{
    public int Dividir(int a, int b)
    {
        return a / b;
    }
}

Legal, mas também precisamos testar a divisão por zero para saber como o método irá se comportar. Como podemos fazer isso?

É nesse momento que precisamos contextualizar nossos testes, isso significa que vamos testar a mesma rotina em cenários diferentes, no exemplo da calculadora precisamos gerar o cenário do dividir com o divisor maior que zero e divisor zero. Podemos testar conforme abaixo:

public abstract class TestarCalculadora
{
    protected Calculadora Calculadora { get; private set; }
    protected int Resultado { get; set; }

    [TestInitialize]
    public void Inicializar()
    {
        Arrange();
        Act();
    }

    private void Arrange()
    {
        Calculadora = new Calculadora();
    }

    protected abstract void Act();

}

[TestClass]
public class TestarDivisaoComDivisorMaiorQueZero : TestarCalculadora
{

    protected override void Act()
    {
        Resultado = Calculadora.Dividir(10, 5);
    }

    [TestMethod]
    public void ResultadoIgual2()
    {
        Assert.AreEqual(2, Resultado);
    }
}

[TestClass]
public class TestarDivisaoComDivisorZero : TestarCalculadora
{
    private bool _divisaoPorZeroGerada = false;

    protected override void Act()
    {
        try { Resultado = Calculadora.Dividir(10, 0); }
        catch (DivideByZeroException) { _divisaoPorZeroGerada = true; }
    }

    [TestMethod]
    public void ExcecaoDivisaoPorZeroGerada()
    {
        Assert.IsTrue(_divisaoPorZeroGerada);
    }

    [TestMethod]
    public void ResultadoIgualZero()
    {
        Assert.AreEqual(0, 0);
    }
}

Contextualizar os testes em cenários nos permite realizar várias verificações em vários cenários de uma mesma transação.
Essa é uma técnica muito simples, porém muito poderosa. Se você utiliza BDD com certeza já conhecia.

sexta-feira, 16 de abril de 2010

Permitir que faça errado!!

Você já tomou uma bronca por ter feito uma chamada de um método em um lugar errado? (Ex.: gravar um log, abrir ou fechar uma conexão ou transação).
Geralmente quando tomamos uma bronca dessa, do arquiteto responsável por exemplo, abaixamos a cabeça e vamos arrumar o "erro". Mas hoje, quando me deparo esse tipo de coisa eu levanto a questão: O método só foi chamado do lugar errado por que a aplicação deixou que isso fosse feito.

Desenhar uma aplicação de forma que seja fácil fazer o que é certo e difícil fazer o errado é um grande desafio, mas o fato de ser difícil não significa que não deva ser tentado. Ter um conhecimento mais profundo de OO e das ferramentas que utilizamos com certeza nos ajudam a alcançar esse objetivo.

Por tanto, da próxima vez que você passar por esse tipo de situação independente do seu papel, do cara que esta dando ou recebendo a bronca, reflita sobre como esta desenhado a aplicação, isso pode fazer uma grande diferença.

Desvendando o Application Layer

Sabemos que o coração do DDD esta no domínio, dedicamos grande parte da nossa atenção a ele.
Mas gostaria de destacar o papel do application layer e tentar esclarecer algumas confusões sobre ele.

Se você já procurou sobre application layer na internet, que vou chamar aqui de camada de aplicação, com certeza deve ter encontrado várias definições e aplicabilidades diferentes sobre o mesmo, por exemplo, "Application Layer é o mesmo que Service Layer" ou até mesmo relacionar com tecnologias como Web Services e WCF.

Essa confusão de certa forma se justifica pelo fato de que, a responsabilidade da camada de aplicação é de coordenar (ou delegar) os trabalhos realizados pelo domínio, o que é muitas vezes encarado como um façade, o que não esta errado, mas é nessa comparação que começa os problemas, por que o service layer também pode ser um façade.

Devido a esses problemas de definições e comparações, como você não tem um Service Layer em todos os projetos, você pode encarar que talvez não precise de uma camada de aplicação, e é ai que ao meu ver chegamos no ápice do engano.

Para entender bem as diferenças entre eles é necessário destacar uma frase da definição do Evans sobre o application layer: "The tasks this layer is responsible for are meaningful to the business"

Ou seja, não é simplemente chamar métodos de objetos de domínio em uma ordem específica, mas coordenar as tarefas de forma significativa ao negócio. Se você vê semelhanças entre a camada de aplicação e o controller do MVC elas param na palavra coordenar, ok? A controller coordena as interações entre a View e a Model, não é disto que estamos tratamos nesse momento.

Nós, desenvolvedores, geralmente pensamos nas funcionalidades essencialmente como CRUD, fazemos isso por vários motivos, mas talvez o principal se refere a comodidades de interações com banco de dados. O problema desse pensamento é que para o negócio nem sempre o CRUD atende, o cliente não deleta um recebimento ele provavelmente faz um estorno, o cliente não faz um insert de ordem ele abre uma ordem de produção.

A função da camada de aplicação é expor operações realizadas pelo domínio de forma que façam sentido ao domínio, por tanto, se o seu domínio abre uma ordem de produção não faz sentido eu ter SaveProductionOrder() na minha camada de aplicação mas sim OpenProductionOrder().

Isso não quer dizer que os famosos métodos Save, Delete e similires não devam existir na sua camada de aplicação, devido ao fato de que, em alguns casos, não é possível determinar um termo de negócio para essas ações.

Outro ponto importante é a questão da leitura de dados, no DDD nós obtemos os dados através do repositório, faz sentido criar métodos no Application Layer para ler dados? Eu respondo, nenhum. Enquanto em um serviço nós geralmente o utilizamos para operações de leitura e/ou escrita, a camada de aplicação só coordena as operações realizadas pelo meu domínio, quando eu preciso ler alguma coisa eu peço direto para o repositório.

Você deve estar se perguntando: Mas eu preciso de um acesso único ao domínio, não preciso?
Essa busca por um caminho linear é herança do BOLOVO onde você tinha UI > BL > DL > BL > UI.
Essa idéia de que tudo tem que passar pelo domínio gera grandes transtornos para identificar as responsabilidades de cada coisa ou de até mesmo um alto acoplamento entre o domínio e outros recursos da minha aplicação, afinal, tudo passa pelo meu domínio.

No desenho apresentado pelo Evans vemos claramente que isso não é necessário e nem deve ser praticado. Se precisamos de uma funcionalidade que se encontra na camada de infraestrutura podemos acessar diretamente dessa camada.


Concluíndo, apesar das semelhanças que a camada de aplicação tenha com outras funcionalidades precisamos ter em mente que ela precisa estar totalmente comprometida com o negócio.

domingo, 4 de abril de 2010

Problema com códigos no post

Estou com problemas com o syntaxhighlighter, nisso exibir código aqui no blog esta um pouco complicado, estou tentando resolver esse problema, mas até lá vou disponibilizar os códigos para download.

Na sexta feira vou finalizar uma apresentação sobre DDD e vou publicar o código utilizado, nele tem exemplos de BDD e Domain Events.

Aguardem.

Testes unitário x TDD x BDD

Acompanhando os grupos de discussão eu percebo que ainda tem muita gente fazendo confusão entre testes unitários, TDD e BDD. Eu sei que a "sopa de letrinhas" não colabora, mas é fundamental entender as as diferenças entre eles para que você consiga usufluir dos seus benefícios.

Eu não vou ficar aqui dando as definições formais de cada um, abaixo segue alguns links onde você pode obter mais informações, mas vou me preocupar em destacar as diferenças entre eles.


http://pt.wikipedia.org/wiki/Teste_de_unidade
http://pt.wikipedia.org/wiki/Test_Driven_Development
http://pt.wikipedia.org/wiki/Behavior_Driven_Development

Quando estamos aplicando teste unitário, estamos testando um único trecho do nosso código, nada mais do que isso. Se o nosso teste faz mais do que isso ou ele é um teste de integração ou é qualquer outra coisa parecida mas não é teste unitário.

Isso quer dizer que testes de integração não importantes? De forma alguma, mas um dos problemas em se ter teste de integração sem testes unitários é que se o seu teste falhar como você irá descobrir o motivo? Lembrando que, dependendo do tipo de teste de integração que você esteja fazendo, outros fatores podem influenciar o resultado (fatores externos como banco de dados por exemplo).

Um dos fatores de resistência aos testes unitários é a mudança que ele causa no nosso código. Como escrevemos testes para que testem apenas um único trecho, precisamos desacoplar nossas classes e métodos para garantir que estamos testando apenas uma única coisa.

TDD é uma disciplina de desenvolvimento, o seu conceito é bem simples, você cria o testa, o teste falha, você faz funcionar e refatora caso necessário. O famoso red, green, refactoring.
Se você não desenvolve dessa forma você não esta utilizando TDD, isso não significa que você não esteja produzindo testes unitários, mas você não esta seguindo a disciplina de desenvolvimento.

Uma das desvantagens mais levantadas contra a disciplina contida no TDD é referente ao tempo: "Escrever testes e fazer falhar é muito demorado.". A parte ironica disso, é que não é uma reclamação dos gestores mas sim dos desenvolvedores.

BDD é uma evolução do TDD, a quem diz que o TDD foi mal interpretado e que na verdade o BDD sempre existiu.
Com o BDD não é apenas testar, o BDD tem como finalidade documentar o código através de testes feitos com base em especificações contruídas a partir de contextos.
Se você acha o desenvolvimento com TDD é demorado o BDD talvez não ajude a mudar esse conceito.

Bom, podemos ter teste unitário sem TDD e BDD, podemos desenvolver com TDD sem BDD e podemos escrever especificações conforme o BDD sem TDD, entretanto não podemos ter BDD ou TDD sem teste unitário.

Se você vai utilizar TDD ou BDD o mais importante é ter os testes escritos e utilizando as melhores práticas da mesma forma que você escreve o código do produto final, pois com certeza, testes mal escritos são difíceis de entender e manter atualizados.

Como você tem escrito seus testes?



sábado, 13 de março de 2010

Pensando em ágil

150px-Rodin_The_Thinker_Laeken_cemetery O quanto às metodologias ágeis são eficases? Essa é uma pergunta que todos com certeza já se fizeram a partir do momento que tiveram contato com elas.

Mas realmente é muito complicado garantir que tais metodologias funcionam ou não. Mas eu ando bem desencanado com isso, Por quê?

Por que já vi projetos ágeis falharem e terem sucesso, assim como já vi projetos cascata falharem e terem sucesso também.

Ayende Rahien, o cara por trás do Rhino Mock, escreveu um post  falando sobre metologias e sabe o que é engraçado? Nenhuma delas fala sobre engenharia de software.

Uma vez fui questionado por um colega se eu gostava de XP e quando eu perguntei o porquê ele me falou: O projeto “xyz” foi feito com XP. O detalhe desse projeto em questão é que ele funciona, o cliente esta satisfeito de uma forma geral, mas tem um código horrível de dar manutenção. Por isso eu respondi: “Que culpa a qualidade do código tem de você utilizar XP?”.

Não interessa que tipo de metodologia você utiliza, sem aplicar boas práticas de engenharia de software, boa arquitetura, bom profissionais, adivinha o que vai acontecer?

Bom, mas o que tem realmente chamado a minha atenção e que me tem trazido bons resultados são algumas práticas contidas nas metodologias ágeis. Aqui vão algumas que tenho me atentado:

1) Comece a desenvolver pelo que agrega maior valor ao negócio.

Eu não sei com vocês, mas geralmente as funcionalidades que você tem que desenvolver que agregam maior valor ao negócio, ou seja, o que seu cliente mais precisa, também são as mais difíceis.

Mas isso é bom, pois afeta diretamente o desenho do projeto, arquitetura, forma de comunicação entre outras coisas.

Exemplo, no ramo de manufatura se a funcionalidade que agrega maior valor é a de ordem de produção, por que começar com cadastro de produtos? Ai você vai me questionar o seguinte: Mas você não precisa de produto para criar a ordem de produção? Sim, realmente vou precisar de produtos. Porém, nesse nosso cenário a tela de produto pode ser afetada por várias funcionalidades do nosso sistema (compras, por exemplo).

Bom se eu sei que a tela de ordem de produção vai precisar de informações de produto, não é melhor eu colocar no produto somente as informações necessárias para gerar a ordem de produção?

Perceba o seguinte, se você já desenvolveu tudo que as outras funcionalidades precisam de produto, a tela de produto só terá o necessário, nada a mais e nada a menos. Isso ajuda muito para evitar que você fique tendo retrabalho em funcionalidades que já estavam prontas por que precisa atender necessidades de outras funcionalidades.

2) Revisão entre pares

Eu ainda não tive a oportunidade de trabalhar em lugar que adota programação em pares, mas é uma realidade, se você tem uma pessoa te ajudando a desenvolver ou revisando algo que você já tenha feito as chances disso sair com sucesso aumentam, principalmente se a outra pessoa for mais experiente que você.

3) Validação contínua com o cliente

Tem pessoas que simplesmente tem pavor de validar uma funcionalidade com o cliente antes da entrega formal do projeto, por quê? E o se o cliente pedir alguma alteração?

Ivar Jacobson diz o seguinte:

"Todos os sistemas mudam durante o ciclo de vida. Isto deve estar na cabeça quando se desenvolve sistemas que se espera durar mais do que a primeira versão."

Obs.: Estou à procura da fonte onde ele disse essa frase, assim que eu encontrar eu atualizo o post.

Ou seja, a mudança é uma constante em nosso cotidiano podemos estar desenvolvendo novos produtos ou dando manutenção, o negócio é algo que vive evoluindo, a isso é devido a importância de construirmos softwares flexíveis o bastante para acompanhar essas mudanças.

O que eu coloquei acima não é nenhuma novidade, se você conhecer o manifesto agile vai encontrar tudo isso e muito mais lá. Vale apena conhecer as práticas independente se você irá utilizá-las ou não.

terça-feira, 23 de fevereiro de 2010

Domain Events

Quando você utiliza DDD e começa a trabalhar com o modelo rico, você pode começar a se deparar com algumas situações que nem sempre é muito simples achar a melhor a solução, mas uma dessas soluções que você pode utilizar são os eventos de domínio (Domain Events).

Vamos imaginar o seguinte cenário: “Eu preciso cadastrar uma ordem de compra, que no momento da criação ela fica aguardando aprovação.”

Antes de adicionar algum tempero nesse cenário vamos começar a desenhar a classe:

namespace Domain.Entities
{
public enum PurchaseOrderStateEnum
{
WaitingApproval,
Approval
}

public class PurchaseOrder
{
public virtual int Id { get; private set; }
public virtual int OrderNumber { get; private set; }
public virtual PurchaseOrderStateEnum State { get; private set; }

protected PurchaseOrder() { }

public PurchaseOrder(int orderNumber)
{
OrderNumber = orderNumber;
State = PurchaseOrderStateEnum.WaitingApproval;
}

public virtual void Approval()
{
State = PurchaseOrderStateEnum.Approval;
}
}
}

Agora vamos começar a adicionar os temperos: “Quando a ordem de aprovação for aprovada é necessário enviar um e-mail para o gestor da área de compas informando da aprovação da ordem.”

Bom nós já discutimos aqui que enviar e-mail é algo que deve ser feito na camada de infraestrutura, e também sabemos que o domínio não pode depender da camada de infraestrutura, certo?

Uma possível solução para o problema poderia ser o seguinte:

public class PurchaseOrder
{
public virtual ISendEmailBeforeApprovedPurchase SendEmailBeforeApprovedPurchase { get; private set; }
public virtual int Id { get; private set; }
public virtual int OrderNumber { get; private set; }
public virtual PurchaseOrderStateEnum State { get; private set; }

protected PurchaseOrder() { }

public PurchaseOrder(int orderNumber)
{
OrderNumber = orderNumber;
State = PurchaseOrderStateEnum.WaitingApproval;
}

public virtual void Approval()
{
State = PurchaseOrderStateEnum.Approval;
SendEmailBeforeApprovedPurchase.SendEmail(this);
}
}

public interface ISendEmailBeforeApprovedPurchase()
{
void SendEmail(PurchaseOrder purchaseOrder);
}

Dessa forma podemos “injetar” o serviço de envio de e-mail através de um DI/IoC, mas como tudo nessa vida nada é perfeito, só Deus.

Como faríamos para implementar um segundo comportamento quando uma ordem compra fosse aprovada? Vamos ver como fazer isso com eventos de domínio:

public virtual void Approval()
{
State = PurchaseOrderStateEnum.Approval;
DomainEvents.Raise(new PurchaseOrderAprrovedEvent { PurchaseOrder = this });
}

O que estamos fazendo é dizendo para o gerenciador de eventos de domínio que, quando o método Approval for chamado, o evento PurchaseOrderApprovedEvent deve ser disparado. Vamos analisar a estrutura do evento:

namespace Domain.Events
{
public interface IDomainEvent { }

public class PurchaseOrderAprrovedEvent : IDomainEvent
{
public PurchaseOrder PurchaseOrder { get; set; }
}
}

Veja o código do gerenciador de eventos:

public static class DomainEvents
{
[ThreadStatic] //so that each thread has its own callbacks
private static List actions;

//Registers a callback for the given domain event
public static void Register(Action callback) where T : IDomainEvent
{
if (actions == null)
actions = new List();
actions.Add(callback);
}
//Clears callbacks passed to Register on the current thread
public static void ClearCallbacks()
{
actions = null;
}
//Raises the given domain event
public static void Raise(T args) where T : IDomainEvent
{
if (actions != null)
foreach (var action in actions)
if (action is Action)
((Action)action)(args);
}
}

Agora que disparamos o evento precisamos criar interessados em saber quando esse evento é disparado.

namespace Domain.Handles
{
public interface IHandles where T : IDomainEvent
{
void Handles(T args);
}
}

namespace Infrastructure.Handles
{
public class SendEmailBeforeApprovedPurchaseHandler : IHandles
{
public void Handles(PurchaseOrderAprrovedEvent args)
{
Console.WriteLine("mail sent successfully");
}
}
}

Agora só precisamos adicionar “o interessado” na classe de gerenciamento de eventos de domínio para que ele seja notificado:

static void Main(string[] args)
{
DomainEvents.Register(
new Action(delegate(PurchaseOrderAprrovedEvent evt)
{
new SendEmailBeforeApprovedPurchaseHandler().Handles(evt);
}));
var purchase = new PurchaseOrder(123456);
purchase.Approval();

Console.Read();
}

print 1

Talvez você esteja pensando o seguinte: “Cara, consigo fazer a mesma coisa com mas de outro jeito.”. Com certeza existe outras formas de se chegar no mesmo resultado, mas vamos adicionar um pouco mais de tempero para ver o potencial dos eventos de domínio: “Ao aprovar uma ordem de compra, além de enviar o e-mail, eu preciso que um recebimento seja gerado com as mesmas informações da ordem de compra automaticamente.”.

Primeiramente vamos criar mais um “interessado” em saber que a ordem de compra foi aprovada.

public class GenerateReceiptFromPurchaseOrderApprovedHandler : IHandles
{
public void Handles(PurchaseOrderAprrovedEvent args)
{
Console.WriteLine("Receipt order created.");
}
}

Agora basta apenas adicioná-lo ao gerenciador de eventos de domínio:

static void Main(string[] args)
{
DomainEvents.Register(
new Action(delegate(PurchaseOrderAprrovedEvent evt)
{
new SendEmailBeforeApprovedPurchaseHandler().Handles(evt);
}));
DomainEvents.Register(
new Action(delegate(PurchaseOrderAprrovedEvent evt)
{
new GenerateReceiptFromPurchaseOrderApprovedHandler().Handles(evt);
}));
var purchase = new PurchaseOrder(123456);
purchase.Approval();
Console.Read();
}

Pronto, conseguimos adicionar mais umcomportamento ao processo de aprovação de ordem de compra sem a necessidade de alterar a classe, respeitando assim o OCP, e cada novo comportamento tem apenas uma única responsabilidade respeitando o SRP.

Mas perceba o ato de adicionar os interessado, esta sendo feito manualmente pra facilitar um puco podemos utilizar um DI/IoC para adiciona-los ao gerenciador de eventos. Vamos ver um exemplo com o StructureMap.

Precisamos fazer a seguinte alteração no gerenciador de eventos:

public static class DomainEvents
{
[ThreadStatic] //so that each thread has its own callbacks
private static List actions;

public static IContainer HandlesContainer { get; set; }

//Registers a callback for the given domain event
public static void Register(Action callback) where T : IDomainEvent
{
if (actions == null)
actions = new List();
actions.Add(callback);
}
//Clears callbacks passed to Register on the current thread
public static void ClearCallbacks()
{
actions = null;
}
//Raises the given domain event
public static void Raise(T args) where T : IDomainEvent
{
if (HandlesContainer != null)
foreach (var handler in HandlesContainer.GetAllInstances>())
handler.Handles(args);


if (actions != null)
foreach (var action in actions)
if (action is Action)
((Action)action)(args);
}
}

Agora vamos juntar tudo:

static void Main(string[] args)
{
var container = new Container(x =>
{
x.For<IHandles<PurchaseOrderAprrovedEvent>>().Add<SendEmailBeforeApprovedPurchaseHandler>();
x.For<IHandles<PurchaseOrderAprrovedEvent>>().Add<GenerateReceiptFromPurchaseOrderApprovedHandler>();

});

DomainEvents.HandlesContainer = container;

var purchase = new PurchaseOrder(123456);
purchase.Approval();

Console.Read();

}

Legal né, mas no mundo real, quem faz uma alteração em uma entidade e não persiste no banco né? Como é que colocamos a persistencia no meio de tudo isso?

Vamos criar o repositório, pra isso vou utilizar o Fluent NHibernate:

namespace Domain.Repositories
{
public interface IPurchaseOrderRepository
{
void Save(PurchaseOrder purchaseOrder);
}
}

namespace Infrastructure.Repositories.Mapping
{
public class PurchaseOrderMap : ClassMap
{
public PurchaseOrderMap()
{
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.OrderNumber).Not.Nullable();
Map(x => x.State);
}
}
}

namespace Infrastructure.Repositories
{
public class PurchaseOrderRepository : IPurchaseOrderRepository
{
private readonly ISession _session;
public PurchaseOrderRepository(ISession session)
{
_session = session;
}
public void Save(PurchaseOrder purchaseOrder)
{
_session.SaveOrUpdate(purchaseOrder);
}
}
}

Vamos salvar a ordem de compra após realizar a aprovação:

static void Main(string[] args)
{
Configuration configuration = null;

var sessionFactory = Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory().ShowSql())
.Mappings(M => M.FluentMappings.AddFromAssembly(typeof(PurchaseOrderRepository).Assembly))
.ExposeConfiguration(c => configuration = c)
.BuildSessionFactory();

var container = new Container(x =>
{
x.For>().Add();
x.For>().Add();

x.For().Singleton().Use(sessionFactory);
x.For().Singleton().Use(s => s.GetInstance().OpenSession());
x.For().Use();
});

DomainEvents.HandlesContainer = container;

BuildSchema(configuration, container.GetInstance());

var purchase = new PurchaseOrder(123456);
purchase.Approval();
var repository = container.GetInstance();
repository.Save(purchase);

Console.Read();
}


public static void BuildSchema(Configuration cfg, ISession se)
{
SchemaExport export = new SchemaExport(cfg);
export.Execute(true, true, false, se.Connection, null);

}

public class NHibernateTestRegistry : Registry
{

public static Configuration cfg;

public NHibernateTestRegistry()
{
Configuration configuration = null;

var sessionFactory = Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory().ShowSql())
.Mappings(M => M.FluentMappings.AddFromAssembly(typeof(PurchaseOrderRepository).Assembly))
.ExposeConfiguration(c => configuration = c)
.BuildSessionFactory();

For().Singleton().Use(sessionFactory);
For().Use(x => x.GetInstance().OpenSession());
For().Use();
}
}

Concluindo: Eventos de domínio é uma grande solução para manter a sua arquitetura saudavel. Podemos incorporar vários comportamentos a uma determinada ação que ocorre em um objeto do domínio sem ter que alterá-lo, com certeza vale a pena tê-lo como uma carta na manga.

Fontes:

http://www.udidahan.com/2009/06/14/domain-events-salvation/
http://martinfowler.com/eaaDev/DomainEvent.html
http://unplugged.giggio.net/unplugged/post/Domain-Events.aspx

quarta-feira, 3 de fevereiro de 2010

Boas Práticas para interface

Eu ja havia levantado a bandeira sobre qualidade e organização de código na camada de interface aqui no blog, e tenho percebido que a comunidade tem desprendido um grande esforço nesse sentido também. 

O Jeremy D. Miller publicou no seu blog uma série de posts sobre melhores práticas na elaboração de interface:

The Build Your Own CAB Series Table of Contents
http://codebetter.com/blogs/jeremy.miller/archive/2007/07/25/the-build-your-own-cab-series-table-of-contents.aspx
http://www.jeremydmiller.com/ppatterns/(X(1)S(x4of0b450vkpvp454kqddkvw))/default.aspx?AspxAutoDetectCookieSupport=1

Já temos também vários recursos para facilitar a nossa vida: ASP.Net MVC Framework
http://www.asp.net/%28S%28d35rmemuuono1wvm1gsp2n45%29%29/mvc/

E para quem tem o Visual Studio temos também:
Web Client Developer Guidance
http://www.codeplex.com/webclientguidance

Composite WPF and Silverlight
http://www.codeplex.com/CompositeWPF

Se o grande problema para se utilizar as boas práticas era a perca de produtividade, já faz algum tempo que esse argumento não cola a mais.

terça-feira, 12 de janeiro de 2010

Comentário anônimo, agora ta liberado

Eu não havia percebido que o blog não estava permitindo que fosse inserido comentários anônimos. Mas agora acredito ter resolvido.

Agora você pode descer a lenha sem precisar se identificar.

sexta-feira, 8 de janeiro de 2010

Brincando com delegates – Partes II

Dando continuidade com o assunto delegate hoje vou mostrar um cenário bem interessante de se aplicar o Func. Recursividade.

Vamos analisar o código abaixo:

public class Task
{
private IList<Task> _subTasks;
public Task Parent { get; set; }

public Task()
{
this._subTasks = new List<Task>();
}

public void AddSubTask(Task subTask)
{
if (subTask == null)
throw new ArgumentNullException("subTask");

if (IsMyParent(subTask))
throw new InvalidOperationException("Task cannot be my parent.");

this._subTasks.Add(subTask);
}

private bool IsMyParent(Task task)
{
if (task.Parent != null && task.Parent == this)
return true;

return IsMyParent(task._subTasks);
}

private bool IsMyParent(IList<Task> subTasks)
{
return subTasks.Any(c => c == this IsMyParent(c._subTasks));
}
}

Veja que o método IsMyParent tem uma sobrecarga para verificar se a tarefa infomada no parâmetro existe nas listas dos filhos das tarefa pai, mas perceba que o método só é criado para atender a necessidade da recursividade. Será que não tem como fazer tudo uma coisa só? Opa… claro que tem.

Agora vamos ver como criar o mesmo método mas dentro do escopo do IsMyParent.

//Versão 2
private bool IsMyParent(Task task)
{
if (task.Parent != null && task.Parent == this)
return true;

Func<IList<Task>, bool> isMyParent = null;
isMyParent = delegate(IList<Task> subTasks)
{
return subTasks.Any(c => c == this isMyParent(c._subTasks));
};

return isMyParent(task._subTasks);
}


Agora a verificação na lista de sub tarefas faz parte do escopo do método.

Por se tratar de um método recursivo temos que definir um valor para o delegate antes de implementá-lo, é por essa razão que temos a declaração e a implementação do isMyParent separadas.

segunda-feira, 4 de janeiro de 2010

O que eu faço nas férias? Estudar…

Minhas férias estão acabando, e estou aproveitando os últimos dias para estudar, pois é nerd.

Bom aqui vai alguns vídeos que eu achei interessante no site da InfoQ do Greg Young que eu recomendo.

http://www.infoq.com/presentations/greg-young-unshackle-qcon08
http://www.infoq.com/presentations/TDD-in-a-DbC-World-Greg-Young.

Brincando com delegates – Parte I

Bom primeiramente feliz ano novo.

Nesses últimos meses foi difícil manter o blog, infelizmente foi muito difícil manter o foco no trabalho, estudos e blog ao mesmo tempo.
Mas como em todo ano que se inicia costumamos traçar metas, uma das minhas é publicar conteúdo com mais regularidade.

Um recurso bem legal das novas versões do .Net é o delegate, principalmente a partir do 3.0. Abaixo vou colocar um exemplo de como utilizar o delegate do tipo Action.


//Versão 1
private void mtcDate_DateChanged(object sender, DateRangeEventArgs e)
{
DateTime date = this.mtcDate.SelectionEnd;

this.dtpInitital.Value = new DateTime(date.Year
, date.Month
, date.Day
, this.dtpInitital.Value.Hour
, this.dtpInitital.Value.Minute
, this.dtpInitital.Value.Second);

this.dtpFinal.Value = new DateTime(date.Year
, date.Month
, date.Day
, this.dtpFinal.Value.Hour
, this.dtpFinal.Value.Minute
, this.dtpFinal.Value.Second);

this.lblSelectedDate.Text = date.ToShortDateString();
}

O controle dtpInitital e dtpFinal são do mesmo tipo, perceba que eu tive que repetir o mesmo código para cada controle. Pra não ter que fazer isso, podemos criar um método para executar as alterações nessárias no valor dos controles da seguinte forma:
private void mtcDate_DateChanged(object sender, DateRangeEventArgs e)
{
DateTime date = this.mtcDate.SelectionEnd;

ChangedDate(this.dtpInitital, date);
ChangedDate(this.dtpFinal, date);

this.lblSelectedDate.Text = date.ToShortDateString();
}

private void ChangeDate(DateTimePicker control, DateTime date)
{
control.Value = new DateTime(date.Year
, date.Month
, date.Day
, control.Value.Hour
, control.Value.Minute
, control.Value.Second);
}

No exemplo acima criamos o método ChangeDate para realizar as alterações nos controles que passamos no parâmetro, mas tivemos que criar um método que será chamado sempre pelo mesmo método, agora veja a mesma solução com delegate.
private void mtcDate_DateChanged(object sender, DateRangeEventArgs e)
{
DateTime date = this.mtcDate.SelectionEnd;

Action<DateTimePicker> changedDate = null;

changedDate = delegate(DateTimePicker control)
{
control.Value = new DateTime(date.Year
, date.Month
, date.Day
, control.Value.Hour
, control.Value.Minute
, control.Value.Second);
};

changedDate(this.dtpInitital);
changedDate(this.dtpFinal);

this.lblSelectedDate.Text = date.ToShortDateString();
}

Perceba que agora colocamos o método dentro do escopo do evento, não temos a repetição de código que tivemos no primeiro controle e nem criamos um método no escopo da classe que será chamado sempre pelo memso lugar.