Как правильно использовать шаблон хранилища?

Мне интересно, как мне группировать свои репозитории? Как из примеров, которые я видел на asp.net mvc, и в моих книгах они в основном используют один репозиторий для каждой таблицы базы данных. Но это похоже на множество репозиториев, заставляющих вас впоследствии вызвать много репозиториев для насмешек и т.д.

Итак, я предполагаю, что я должен их сгруппировать. Однако я не уверен, как их группировать.

В настоящий момент я создал регистрационный репозиторий для обработки всех моих регистрационных материалов. Однако есть 4 таблицы, которые мне нужно обновить, и до того, как у меня было 3 репозитория для этого.

Например, одна из таблиц - это таблица лицензий. Когда они регистрируются, я просматриваю их ключ и проверяю его, чтобы увидеть, существует ли в базе данных. Теперь, что произойдет, если мне нужно проверить этот лицензионный ключ или что-то еще из этой таблицы в другом месте, кроме регистрации?

Одно место может быть логином (проверьте, не истекает ли ключ).

Итак, что бы я сделал в этой ситуации? Перепишите код еще раз (перерыв DRY)? Попытайтесь объединить эти 2 репозитория вместе и надеяться, что ни один из методов не понадобится в какой-то другой момент времени (например, возможно, у меня может быть метод, который проверяет, используется ли имя пользователя), возможно, мне понадобится это где-то еще).

Кроме того, если я объединю их вместе, мне потребуется либо 2 уровня обслуживания, идущих в один и тот же репозиторий, так как я думаю, что вся логика для двух разных частей сайта будет длинной, и мне нужно будет иметь имена типа ValidateLogin(), ValdiateRegistrationForm(), ValdiateLoginRetrievePassword() и т.д.

Или вызывать репозиторий в любом случае и просто иметь странное звучание?

Мне кажется, сложно создать репозиторий с достаточно общим именем, чтобы вы могли использовать его во многих местах вашего приложения и по-прежнему иметь смысл, и я не думаю, что вызов другого репозитория в репозитории будет хорошей практикой?

Ответ 1

Одна вещь, которую я сделал неправильно, когда играл с шаблоном репозитория - точно так же, как вы, я думал, что таблица относится к репозиторию 1:1. Когда мы применяем некоторые правила из Domain Driven Design - проблема с группировкой репозиториев часто исчезает.

Репозиторий должен быть за Агрегировать root, а не таблицу. Это означает, что если объект не должен жить один (т.е. Если у вас есть Registrant, в котором участвует, в частности, Registration) - это просто сущность, ему не нужен репозиторий, он должен быть обновлен/создан/получен через репозиторий совокупного корня он принадлежит.

Конечно, во многих случаях эта методика сокращения количества репозиториев (на самом деле - это скорее метод структурирования вашей модели домена) не может применяться, поскольку каждый объект должен быть совокупным корнем (который сильно зависит от ваш домен, я могу предоставить только слепые догадки). В вашем примере - License представляется сводным корнем, потому что вы должны иметь возможность проверять их без контекста объекта Registration.

Но это не ограничивает нас каскадными репозиториями (репозиторий Registration разрешен при необходимости ссылаться на репозиторий License). Это не ограничивает нас ссылкой License репозитория (предпочтительно - через IoC) непосредственно из объекта Registration.

Просто старайтесь не сводить свой дизайн с помощью усложнений, предоставляемых технологиями, или что-то не понимаете. Группировка репозиториев в ServiceX только потому, что вы не хотите создавать 2 репозитория, это не очень хорошая идея.

Гораздо лучше было бы дать ему собственное имя - RegistrationService i.e.

Но услуг вообще следует избегать - часто это приводит к тому, что приводит к модели анемичного домена.

EDIT:
Начните использовать IoC. Это действительно облегчает боль от инъекционных зависимостей.
Вместо написания:

var registrationService = new RegistrationService(new RegistrationRepository(),  
      new LicenseRepository(), new GodOnlyKnowsWhatElseThatServiceNeeds());

вы сможете написать:

var registrationService = IoC.Resolve<IRegistrationService>();

P.s. Было бы лучше использовать так называемый общий локатор сервисов, но это просто пример.

Ответ 2

Одна вещь, которую я начал делать, чтобы решить эту проблему, - это фактически разработать службы, которые обертывают N репозиториев. Надеемся, что ваши рамки DI или IoC помогут сделать это проще.

public class ServiceImpl {
    public ServiceImpl(IRepo1 repo1, IRepo2 repo2...) { }
}

Это имеет смысл? Кроме того, я понимаю, что говоря об услугах в этой усадьбе может или не может фактически соответствовать принципам DDD, я просто делаю это, потому что это работает.

Ответ 3

То, что я делаю, это абстрактный базовый класс, который определяется следующим образом:

public abstract class ReadOnlyRepository<T,V>
{
     V Find(T lookupKey);
}

public abstract class InsertRepository<T>
{
     void Add(T entityToSave);
}

public abstract class UpdateRepository<T,V>
{
     V Update(T entityToUpdate);
}

public abstract class DeleteRepository<T>
{
     void Delete(T entityToDelete);
}

Затем вы можете получить репозиторий из абстрактного базового класса и расширить свой единственный репозиторий до тех пор, пока общие аргументы не будут отличаться, например:

public class RegistrationRepository: ReadOnlyRepository<int, IRegistrationItem>,
                                     ReadOnlyRepository<string, IRegistrationItem> 

и т.д....

Мне нужны отдельные репозитории, потому что у нас есть ограничения на некоторые из наших репозиториев, и это дает нам максимальную гибкость. Надеюсь, это поможет.

Ответ 4

У меня это как мой класс репозитория, и да, я расширяюсь в репозитории таблицы/области, но мне иногда приходится прерывать DRY.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MvcRepository
{
    public class Repository<T> : IRepository<T> where T : class
    {
        protected System.Data.Linq.DataContext _dataContextFactory;

        public IQueryable<T> All()
        {
            return GetTable.AsQueryable();
        }

        public IQueryable<T> FindAll(Func<T, bool> exp)
        {
            return GetTable.Where<T>(exp).AsQueryable();
        }

        public T Single(Func<T, bool> exp)
        {
            return GetTable.Single(exp);
        }

        public virtual void MarkForDeletion(T entity)
        {
            _dataContextFactory.GetTable<T>().DeleteOnSubmit(entity);
        }

        public virtual T CreateInstance()
        {
            T entity = Activator.CreateInstance<T>();
            GetTable.InsertOnSubmit(entity);
            return entity;
        }

        public void SaveAll()
        {
            _dataContextFactory.SubmitChanges();
        }

        public Repository(System.Data.Linq.DataContext dataContextFactory)
        {
            _dataContextFactory = dataContextFactory;
        }

        public System.Data.Linq.Table<T> GetTable
        {
            get { return _dataContextFactory.GetTable<T>(); }
        }

    }
}

ИЗМЕНИТЬ

public class AdminRepository<T> : Repository<T> where T: class
{
    static AdminDataContext dc = new AdminDataContext(System.Configuration.ConfigurationManager.ConnectionStrings["MY_ConnectionString"].ConnectionString);

    public AdminRepository()
        : base( dc )
    {
    }

У меня также есть datacontext, который был создан с использованием класса Linq2SQL.dbml.

Итак, теперь у меня есть стандартный репозиторий, реализующий стандартные вызовы, такие как "Все" и "Найти", и в моем AdminRepository у меня есть специальные вызовы.

Не отвечает на вопрос о СУШЕ, хотя я не думаю.

Ответ 5

Вот пример общей реализации Репозитория с использованием FluentNHibernate. Он способен сохранять любой класс, на который вы написали карту. Он даже способен генерировать вашу базу данных на основе классов сопоставления.

Ответ 6

Паттерн репозитория - плохой шаблон проектирования. Я работаю со многими старыми проектами .Net, и этот шаблон обычно приводит к ошибкам "Распределенные транзакции", "Частичный откат" и "Удаленный пул подключений", которых можно было бы избежать. Проблема в том, что шаблон пытается обрабатывать соединения и транзакции внутренне, но они должны обрабатываться на уровне контроллера. Также EntityFramework уже абстрагирует много логики. Я бы предложил использовать шаблон Service вместо повторного использования общего кода.

Ответ 7

Я предлагаю вам посмотреть Sharp Architecture. Они предлагают использовать один репозиторий для каждого объекта. Я использую его в настоящее время в своем проекте и очень доволен результатами.