Unit-of-Work & Repository Framework aplicada a DDD



A URF, Unit-of-Work & Repository Framework, é uma framework que tem por objectivo juntar as Patterns Repository e Unit of Work sobre tecnologia O/RM ao Domain-Driven Design. Documentação desta framework aqui.

O exercício seguinte consiste em aplicar a URF Framework à solução desenhada aqui, com dependency injection.

Criação da solução

Criar a nova solução UnitOfWorkRepository.Framework com:
  • Pasta Application
  • Pasta Domain
  • Pasta Infrastructure

Criação dos projectos Domain-Driven

À Infrastructure adiciona-se o projecto do tipo biblioteca de nome EntityFramework com os repositórios de acesso à Entity Framework.
Ao Domain adiciona-se o projecto do tipo biblioteca com o Core.
À Application adiciona-se o projecto do tipo biblioteca com o nome Application.

Mais detalhes aqui.

Geração de POCO e DbContext

Na Consola de Gestão de Pacotes NuGet procede-se à instalação da Entity Framework, tendo o projecto EntityFramework como padrão.
PM> install-package EntityFramework

Uma vez instalada a última versão da Entity Framework, procede-se ao reverse engineering para a geração do DbContext, classes POCO e Configuração POCO a partir da BD SQL Server Pluto_Queries. É usado para o efeito o módulo EntityFramework Reverse POCO Generator, disponível aqui, que integra com o Visual Studio 2017. Artigo dedicado, aqui, pela Microsoft MVP Julie Nerman.

Connection string PlutoContext usada aqui:
  <connectionStrings>
    <add name="PlutoContext" connectionString="data source=(localdb)\MSSQLLocalDB;initial catalog=Pluto_Queries;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
  </connectionStrings>

Mover as classes POCO Author e Course, geradas automaticamente pelo EF.Reverse.POCO.Generator para a pasta Model do Core e a bilbioteca EntityFramework tem de passar a referenciá-lo.
using UnitOfWorkRepository.Framework.Core.Model;

Compilar a solução para verificação.

Integração com a URF Framework

Usar a gestão de pacotes NuGet através da UI da ferramenta no VS para instalar o pacote Urf.Repository.Pattern.EF6 nos projectos Application, Core e EntityFramework.

Integração do DbContext
Para integrar o DbContext com a URF Framework, o DbContext tem de herdar do DataContext:
namespace UnitOfWorkRepository.Framework.EntityFramework
{
    using System.Linq;
    using UnitOfWorkRepository.Framework.Core.Model;
    using Repository.Pattern.Ef6;

    [System.CodeDom.Compiler.GeneratedCode("EF.Reverse.POCO.Generator", "2.32.0.0")]
    public partial class PlutoContext : DataContext, IPlutoContext
    {
        public System.Data.Entity.DbSet<Author> Authors { get; set; } // Authors
        public System.Data.Entity.DbSet<Course> Courses { get; set; } // Courses

        static PlutoContext()
        {
            System.Data.Entity.Database.SetInitializer<PlutoContext>(null);
        }

        public PlutoContext()
            : base("Name=PlutoContext")
        {
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
        }

       [...]

     }
}

Integração do Model
Para integrar as entidades de domínio com esta framework de persistência, estas classes têm de herdar da classe base Entity.cls:
namespace UnitOfWorkRepository.Framework.Core.Model
{     
  using Repository.Pattern.Ef6;     

  // Authors
  [System.CodeDom.Compiler.GeneratedCode("EF.Reverse.POCO.Generator", "2.32.0.0")]
  public partial class Author: Entity
  {
   [...]
  }
}

Compilar a solução para verificação.

Integração das Interfaces
À biblioteca Core, adiciona-se a pasta Interfaces. Esta pasta comporta as interfaces intrínsecas aos repositórios e estendem a interface de repositório da URF Framework. Exemplo, IAuthorRepository:
using UnitOfWorkRepository.Framework.Core.Model;
using Repository.Pattern.Repositories;

namespace UnitOfWorkRepository.Framework.Core.Interfaces
{
   public interface IAuthorRepository: IRepositoryAsync<Author>
   {
     Author GetAuthorWithCourses(int id);
   }
}

Compilar a solução para verificação.

Integração dos Repositórios

Depois de adicionada a pasta Repositories à biblioteca EntityFramework, juntam-se as classes que implementam as interfaces definidas no Domain e que herdam do repositório base da URF Framework. Exemplo, AuthorRepository:
using Repository.Pattern.DataContext;
using Repository.Pattern.Ef6;
using Repository.Pattern.UnitOfWork;
using System.Linq;
using UnitOfWorkRepository.Framework.Core.Interfaces;
using UnitOfWorkRepository.Framework.Core.Model;

namespace UnitOfWorkRepository.Framework.EntityFramework.Repositories
{
  public class AuthorRepository: Repository <Author>, IAuthorRepository 
  {
    public AuthorRepository(IDataContextAsync dataContext, IUnitOfWorkAsync unitOfWork)
 : base(dataContext, unitOfWork){}
 
    public Author GetAuthorWithCourses(int id) 
    {
      var authorWithCourses = base.Query().Include(a => a.Course).Select().AsQueryable();
   
      return authorWithCourses.FirstOrDefault();
    }
  }
}

Introdução da lógica de negócio

Usar a gestão de pacotes NuGet através da UI da ferramenta no VS para instalar o pacote Urf.Service.Pattern nos projectos Application e Core.

A biblioteca Application vai ter os serviços que implementam a lógica de negócio, enquanto a pasta Interfaces da biblioteca Core vai ter as interfaces intrínsecas a estes serviços. Exemplo, IAuthorService e AuthorService:
using System.Collections.Generic;
using UnitOfWorkRepository.Framework.Core.Model;
using Service.Pattern;

namespace UnitOfWorkRepository.Framework.Core.Interfaces
{
  public interface IAuthorService: IService<Author>
  {
    IEnumerable<Author> GetAllAuthors();
    Author GetAuthor(int Id);
    Author GetAuthorWithCourses(int id);
  }
}
using System.Collections.Generic;
using UnitOfWorkRepository.Framework.Core.Model;
using UnitOfWorkRepository.Framework.Core.Interfaces;
using Service.Pattern;
using Repository.Pattern.Repositories;

namespace UnitOfWorkRepository.Framework.Application
{
    public class AuthorService : Service<Author>, IAuthorService
    {
        private IAuthorRepository _repository;

        public AuthorService(IRepositoryAsync<Author> repository)
            :base(repository)
        {
            _repository = repository as IAuthorRepository;
        }

        public IEnumerable<Author> GetAllAuthors()
        {
            return base.Query().Select();
        }

        public Author GetAuthor(int Id)
        {
            return base.Find(Id);
        }

        public Author GetAuthorWithCourses(int id)
        {
            return _repository.GetAuthorWithCourses(id);
        }

        public override void Insert(Author author)
        {
            // TODO: lógica de negócio
            base.Insert(author);
        }

        public override void Delete(Author author)
        {
            // TODO: lógica de negócio
            base.Delete(author);
        }

        public override void Update(Author author)
        {
            // TODO: lógica de negócio
            base.Update(author);
        }
    }
}

Consumo por framework de apresentação

É usado para exemplo o controller AuthorsController.cs da framework .NET MVC 5.

I. Criar o projecto do tipo ASP.NET MVC Web Application no Visual Studio 2017 com a template de fábrica.

II. Usar a gestão de pacotes NuGet através da UI da ferramenta no VS para instalar neste projecto o pacote mais recente do Unity.Mvc (unity bootstrapper para asp.net mvc), pela Microsoft.

III. Adicionar o seguinte código ao App_Start\UnityConfig.cs
using UnitOfWorkRepository.Framework.Application;
using Repository.Pattern.Repositories;
using UnitOfWorkRepository.Framework.Core.Model;
using UnitOfWorkRepository.Framework.Core.Interfaces;
using Repository.Pattern.UnitOfWork;
using Repository.Pattern.Ef6;
using Repository.Pattern.DataContext;
using UnitOfWorkRepository.Framework.EntityFramework;
using UnitOfWorkRepository.Framework.EntityFramework.Repositories;
using Service.Pattern;
container.RegisterType<IUnitOfWork, UnitOfWork>(new PerRequestLifetimeManager());
container.RegisterType<IUnitOfWorkAsync, UnitOfWork>(new PerRequestLifetimeManager());
container.RegisterType<IDataContext, PlutoContext>(new PerRequestLifetimeManager());
container.RegisterType<IDataContextAsync, PlutoContext>(new PerRequestLifetimeManager());
container.RegisterType<IRepositoryAsync<Author>, AuthorRepository>(new PerRequestLifetimeManager());
container.RegisterType<IService<Author>, Service<Author >> (new PerRequestLifetimeManager());
container.RegisterType<IAuthorService, AuthorService>(new PerRequestLifetimeManager());

IV. Descomentar a seguinte linha de código do método Start da App_Start\UnityMvcActivator.cs
Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));

V. Gerar o AuthorsController com scaffolding com modos de exibição usando a Entity Framework


VI.Substituir o código do AuthorsController.cs por
using Repository.Pattern.Infrastructure;
using Repository.Pattern.UnitOfWork;
using System.Web.Mvc;
using UnitOfWorkRepository.Framework.Core.Interfaces;
using UnitOfWorkRepository.Framework.Core.Model;
namespace UnitOfWorkRepository.Framework.WebApplication.MVC.Controllers
{
    public class AuthorsController : Controller
    {
        private IAuthorService _authorService;
        private IUnitOfWork _unitOfWork;

        public AuthorsController(IAuthorService authorService, IUnitOfWork unitOfWork)
        {
            _authorService = authorService;
            _unitOfWork = unitOfWork;
        }

        // GET: Authors
        public ActionResult Index()
        {
            return View(_authorService.GetAllAuthors());
        }

        // GET: Authors/Details/5
        public ActionResult Details(int id)
        {
            Author author = _authorService.GetAuthor(id);
            if (author == null)
            {
                return HttpNotFound();
            }

            return View(author);
        }

        // GET: Authors/Create
        public ActionResult Create()
        {
            return View();
        }

        // POST: Authors/Create
        // Para se proteger de mais ataques, ative as propriedades específicas a que você quer se conectar. Para 
        // obter mais detalhes, consulte https://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create([Bind(Include = "Id,Name")] Author author)
        {
            if (ModelState.IsValid)
            {
                _authorService.Insert(author);
                _unitOfWork.SaveChanges();
                
                return RedirectToAction("Index");
            }

            return View(author);
        }

        // GET: Authors/Edit/5
        public ActionResult Edit(int id)
        {
            Author author = _authorService.GetAuthor(id);

            if (author == null)
            {
                return HttpNotFound();
            }
            return View(author);
        }

        // POST: Authors/Edit/5
        // Para se proteger de mais ataques, ative as propriedades específicas a que você quer se conectar. Para 
        // obter mais detalhes, consulte https://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit([Bind(Include = "Id,Name")] Author author)
        {
            if (ModelState.IsValid)
            {
                author.ObjectState = ObjectState.Modified;
                _authorService.Update(author);
                _unitOfWork.SaveChanges();
                return RedirectToAction("Index");
            }
            return View(author);
        }

        // GET: Authors/Delete/5
        public ActionResult Delete(int id)
        {
            Author author = _authorService.GetAuthor(id);
            if (author == null)
            {
                return HttpNotFound();
            }
            return View(author);
        }

        // POST: Authors/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int id)
        {
            Author author = _authorService.GetAuthor(id);
            _authorService.Delete(author);
            _unitOfWork.SaveChanges();
            return RedirectToAction("Index");
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                _unitOfWork.Dispose();
            }
            base.Dispose(disposing);
        }
    }
}

Depurar no navegador pré-definido.
<url base>/Authors



Relacionado: Unit of Work in Repository Pattern, Arquitectura por camadas em aplicações ASP.NET Core



(artigo editado)







Referências: Introduction to EntityFramework Reverse POCO Generator for Visual Studio, EntityFramework Reverse POCO Generator, URF (Unit of Work & Repository Framework) in ASP.NET MVC 5 with Entity Framework 6 & Unity 3 - v2
Licença CC BY-SA 4.0 Silvia Pinhão Lopes, 23.10.17
Print Friendly and PDF

Sem comentários:

Com tecnologia do Blogger.