Architektura cebuli, jednostka pracy i ogólny wzór repozytorium
Po raz pierwszy wdrażam podejście projektowe oparte na domenie. Postanowiłem spróbowaćArchitektura cebuli ponieważ koncentruje się na domenie, a nie na infrastrukturze / platformach / itp.
Aby oderwać się od Entity Framework, stworzyłemogólne repozytorium zJednostka pracy realizacja.
TheIRepository<T>
iIUnitOfWork
interfejsy:
public interface IRepository<T>
{
void Add(T item);
void Remove(T item);
IQueryable<T> Query();
}
public interface IUnitOfWork : IDisposable
{
void SaveChanges();
}
Implementacje Framework EntityIRepository<T>
iIUnitOfWork
:
public class EntityFrameworkRepository<T> : IRepository<T> where T : class
{
private readonly DbSet<T> dbSet;
public EntityFrameworkRepository(IUnitOfWork unitOfWork)
{
var entityFrameworkUnitOfWork = unitOfWork as EntityFrameworkUnitOfWork;
if (entityFrameworkUnitOfWork == null)
{
throw new ArgumentOutOfRangeException("Must be of type EntityFrameworkUnitOfWork");
}
dbSet = entityFrameworkUnitOfWork.GetDbSet<T>();
}
public void Add(T item)
{
dbSet.Add(item);
}
public void Remove(T item)
{
dbSet.Remove(item);
}
public IQueryable<T> Query()
{
return dbSet;
}
}
public class EntityFrameworkUnitOfWork : IUnitOfWork
{
private readonly DbContext context;
public EntityFrameworkUnitOfWork()
{
this.context = new CustomerContext();;
}
internal DbSet<T> GetDbSet<T>()
where T : class
{
return context.Set<T>();
}
public void SaveChanges()
{
context.SaveChanges();
}
public void Dispose()
{
context.Dispose();
}
}
TheKlient magazyn:
public interface ICustomerRepository : IRepository<Customer>
{
}
public class CustomerRepository : EntityFrameworkRepository<Customer>, ICustomerRepository
{
public CustomerRepository(IUnitOfWork unitOfWork): base(unitOfWork)
{
}
}
Kontroler ASP.NET MVC korzystający z repozytorium:
public class CustomerController : Controller
{
UnityContainer container = new UnityContainer();
public ActionResult List()
{
var unitOfWork = container.Resolve<IUnitOfWork>();
var customerRepository = container.Resolve<ICustomerRepository>();
return View(customerRepository.Query());
}
[HttpPost]
public ActionResult Create(Customer customer)
{
var unitOfWork = container.Resolve<IUnitOfWork>();
var customerRepository = container.Resolve<ICustomerRepository>();;
customerRepository.Add(customer);
unitOfWork.SaveChanges();
return RedirectToAction("List");
}
}
Zastrzyk zależności z jednością:
container.RegisterType<IUnitOfWork, EntityFrameworkUnitOfWork>();
container.RegisterType<ICustomerRepository, CustomerRepository>();
Rozwiązanie:
PROBLEMY?
Implementacja repozytorium (kod EF) jest bardzo ogólna. To wszystko siedzi na bokuEntityFrameworkRepository<T>
klasa. Konkretne repozytoria modeli nie zawierają żadnej z tych logik. Oszczędza mi to pisania ton nadmiarowego kodu, ale może poświęcić elastyczność?
TheICustomerRepository
iCustomerRepository
klasy są zasadniczo puste. Są wyłącznie po to, by zapewnić abstrakcję. O ile rozumiem, pasuje to do wizji architektury Onion, w której infrastruktura i kod zależny od platformy znajdują się na zewnątrz systemu, ale posiadanie pustych klas i pustych interfejsów jest błędne?
Aby użyć innej implementacji trwałości (powiedzmy Azure Table Storage), to nowaCustomerRepository
klasa musiałaby zostać utworzona i dziedziczyćAzureTableStorageRepository<T>
. Ale może to prowadzić do nadmiarowego kodu (wiele CustomerRepositories)? Jak ten efekt drwił?
Inna implementacja (powiedzmy Azure Table Storage) ma ograniczenia w obsłudze międzynarodowej, więc klasa AzureTableStorageUnitOfWork nie będzie działać w tym kontekście.
Czy są jakieś inne problemy ze sposobem, w jaki to zrobiłem?
(Większość inspiracji czerpałem zten post)