Construindo camadas de acesso a dados – Parte V – Unity of Work

O padrão Unit of Work mantém um rastreamento sobre todas as alterações que possam alterar sua fonte de dados durante uma transação. Assim, quando todas as alterações já tiverem sido executadas o padrão fica responsável por persistir todas na fonte de dados.

Geralmente, não somos responsáveis por implementar este padrão, acabamos por consumi-lo em ferramentas de persistência, como: Entity Framework, NHibernate e LINQ to SQL.

Algo relevante, é possibilidade de encapsularmos o funcionamento do Unit of Work destas ferramentas de persistência em nossa camada de acesso a dados. Esta necessidade está diretamente ligada ao desejo de não expor dependências técnicas (como componentes de controle de transações) nas camadas superiores à camada de acesso a dados, e também ao fato de não codificarmos regras de negócio na camada de acesso a dados. Permitindo que cada camada fique limitada a suas respectivas responsabilidades, sem infringir o princípio de responsabilidade única.

Para demonstrar o encapsulamento do padrão Unit of Work do Entity Framework utilizaremos como exemplo a emissão de uma ordem de compra. A emissão dessa ordem de compra possui algumas validações e um conjunto de passos, como: gravação da ordem de compra, gravação dos itens da ordem de compra e atualização dos dados do fornecedor. Cada um destes passos corresponde a uma tarefa que está associada a uma ou mais entidades e/ou tabelas.

Reunir em um único método todas essas iterações com a base de dados acaba tornando o código mais complexo e difícil de ser mantido. Assim, cada passo foi dividido em um método especialista em sua execução, cujas alterações são mantidas sob uma mesma instância de contexto (leia “uma mesma unidade de trabalho (Unit of Work)”). O resultado dessa implementação pode ser visto abaixo.

public class RepositorioOrdemCompra : IDisposable {

    private DataContext _contextoAtivo;

    public RepositorioOrdemCompra() {

        this._contextoAtivo = new DataContext();
    }

    public void SalvarOrdemCompra(OrdemCompra ordemCompra) {
                
        this._contextoAtivo.OrdensCompra.Add(ordemCompra);
    }

    public void SalvarItensOrdemCompra(OrdemCompra ordemCompra) {

        foreach (var itemCompra in ordemCompra.Itens) {

            this._contextoAtivo.ItensCompra.Add(itemCompra);

            this._contextoAtivo.Entry(itemCompra.Produto).State = EntityState.Unchanged;
        }
    }

    public void SalvarAtualizacaoFornecedor(OrdemCompra ordemCompra) {
        
        this._contextoAtivo.Entry(ordemCompra.Fornecedor).State = EntityState.Modified;
        if (ordemCompra.Fornecedor.Endereco != null)
            this._contextoAtivo.Entry(ordemCompra.Fornecedor.Endereco).State = EntityState.Unchanged;
    }

    public void EmitirOrdemCompra() {
            
        this._contextoAtivo.SaveChanges();
    }

    public void Dispose() {

        if (this._contextoAtivo != null)
            this._contextoAtivo.Dispose();
    }
}

Note que esta classe implementa a interface IDisposable, que por sua vez implica na implementação do método Dispose(). Esta classe implementa a interface IDisposable para que possamos implementar no método Dispose() todo o código necessário para a limpeza de recursos que não serão mais utilizados. Como esta classe mantém ativa uma instância ativa de um DbContext, é interessante fazer o expurgo dos recursos que esta classe consome. E nada melhor que a interface IDisposable para indicar que a classe contém recursos que precisam ser controlados com mais cuidado.

E por fim, temos a implementação da camada de negócio. Esta camada irá validar a entrada de dados para emissão da ordem de compra, executar regras específicas do cenário em execução e consumir a camada de acesso a dados, invocando os passos necessários para persistência da ordem de compra.

public class RegraNegocioOrdemCompra {

    public void Incluir(OrdemCompra ordemCompra) {

        ///////// VALIDAÇÕES
        if (ordemCompra == null)
            throw new ArgumentNullException("ordemCompra");
        if (ordemCompra.Itens == null || ordemCompra.Itens.Count == 0)
            throw new InvalidOperationException("É preciso adicionar itens para emissão da ordem de compra.");

        ///////// REGRAS
        // Atualizar total da ordem de compra
        decimal total = ordemCompra.Itens.Sum(i => i.Valor * i.Quantidade);
        ordemCompra.Total = total;
        // Atualizar datas dependentes da emissão do pedido
        ordemCompra.Fornecedor.UltimaCompra = 
            ordemCompra.DataRegistro = DateTime.Now;

        ///////// PERSISTÊNCIA DOS DADOS
        using (var repositorio = new RepositorioOrdemCompra()) {
                
            repositorio.SalvarOrdemCompra(ordemCompra);

            repositorio.SalvarItensOrdemCompra(ordemCompra);

            repositorio.SalvarAtualizacaoFornecedor(ordemCompra);

            repositorio.EmitirOrdemCompra();
        }
    }
}

Espero que seja útil.

Por

MSc. Fernando Henrique Inocêncio Borba Ferreira

Microsoft Most Valuable Professional – Visual C#

 

Referências:

http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application

http://msdn.microsoft.com/en-us/magazine/dd882510.aspx

http://martinfowler.com/eaaCatalog/unitOfWork.html

Publicidade

7 comentários sobre “Construindo camadas de acesso a dados – Parte V – Unity of Work

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.