Как использовать шаблон репозитория и единицу работы при работе с несколькими хранителями данных?

StackOverflow https://stackoverflow.com/questions/2913308

Вопрос

У меня есть уникальная ситуация, когда я создаю систему на основе DDD, которая должна получить доступ как к Active Directory, так и в базе данных SQL в качестве настойчивости. Изначально это не было проблемой, потому что наш дизайн был установлен, где у нас была единица работы, которая выглядела так:

public interface IUnitOfWork
{
   void BeginTransaction()
   void Commit()
}

И наши репозитории выглядели так:

public interface IRepository<T>
{
   T GetByID()
   void Save(T entity)
   void Delete(T entity)
}

В этой настройке наша нагрузка и сохранение будут обрабатывать сопоставление между как хранилищами данных, потому что мы сами написали это. Блок работы будет обрабатывать транзакции и будет содержать контекст данных LINQ в SQL, что репозитории будут использовать для настойчивости. Часть Active Directory обрабатывается службой домена, реализованной в инфраструктуре и потребляется репозиториями в каждом методе сохранения (). Сохранить () отвечал с взаимодействием с контекстом данных, чтобы выполнить все операции базы данных.

Теперь мы пытаемся адаптировать его к основу для объектов и воспользоваться POCO. В идеале нам не понадобится метод сохранения (), потому что объекты домена отслеживаются контекстом объекта, и нам просто нужно будет добавить метод сохранения () на единицу работы, чтобы контекст объекта сохранить изменения, и способ Чтобы зарегистрировать новые объекты с контекстом. Новый предложенный дизайн выглядит больше похоже на это:

public interface IUnitOfWork
{
   void BeginTransaction()
   void Save()
   void Commit()
}

public interface IRepository<T>
{
   T GetByID()
   void Add(T entity)
   void Delete(T entity)
}

Это решает проблему доступа к данным с помощью структуры объекта, но не решает проблему с нашей интеграцией Active Directory. Раньше он был в методе сохранения () на репозитории, но теперь у него нет дома. Единица работы не знает ничего, кроме контекста данных структуры объекта. Куда должна идти эта логика? Я утверждаю, что эта конструкция работает только, если у вас есть только один магазин данных, используя основу для объектов. Любые идеи, как наилучшим образом подходить к этой проблеме? Где я должен положить эту логику?

Это было полезно?

Решение

Я хотел вернуться и последую, что я узнал с тех пор, как я разместил это. Похоже, что если вы собираетесь вернуться к узору репозитория, хранимы данных оно сохраняется не имеет значения. Если есть две магазины данных, напишите им как в одном репозитории. Важно для того, чтобы сохранить фасад, который представляет собой шаблон репозитория: в сборе памяти. Я бы не сделал отдельных репозиториев, потому что это не похоже на настоящую абстракцию. Вы позволяете технологии под капотом диктовать дизайн в этот момент. Цитировать из dddstepbystep.com:

Что сидит за репозиторий? Почти все, что вам нравится. Да, вы слышали это правильно. Вы можете иметь базу данных, или у вас может быть много разных баз данных. Вы можете использовать реляционные базы данных или объектные базы данных. У вас могут быть в базе данных памяти или Singleton, содержащий список в пунктах памяти. У вас может быть слой отдыха или набор услуг SOA или файловая система, или в кэше памяти ... Вы можете иметь в значительной степени что-нибудь - ваше единственное ограничение - это то, что репозиторий должен быть в состоянии вести себя как коллекцию для вашего домена Отказ Эта гибкость - это ключевое различие между хранилищем и традиционными методами доступа к данным.

http://thinkdddd.com/assets/2/domain_driviven_design_-_step_by_step.pdf.

Другие советы

Сначала я предполагаю, что вы используете контейнер IOC. Я выступаю, вы делаете истинные репозитории для каждого типа сущности. Это означает, что вы будете обернуть каждый объект контекста в классе, который реализует что-то вроде:

interface IRepository<TEntity> {
  TEntity Get(int id);
  void Add(TEntity entity);
  void Save(TEntity entity);
  void Remove(TEntity entity);
  bool CanPersist<T>(T entity);
}

CANPERSIST просто возвращает, поддерживает ли эта поддержку экземпляра репозитория, сохраняющую проходящую сущность и используется полиморфически с помощью UnitOfWork .Save, описанный ниже.

Каждый IRePository также будет иметь конструктор, который позволяет построить мерозию в режиме «транзакционный». Итак, для EF, у нас может быть:

public partial EFEntityARepository : IRepository<EntityA> {
  public EFEntityARepository(EFContext context, bool transactional) {
    _context = context;
    _transactional = transactional;
  }
  public void Add(EntityA entity) {
    _context.EntityAs.Add(entity);
    if (!_transactional) _context.SaveChanges();
  }
}

UnitOpwork должен выглядеть так:

interface UnitOfWork {
  void Add(TEntity entity);
  void Save(TEntity entity);
  void Remove(TEntity entity);
  void Complete();
}

Реализация UnitOfWork будет использовать инъекцию зависимости, чтобы получить экземпляры всех предложений. В UnitOfWork.save/add/remove UOW пройдет сущность аргумента в каремическую каремию каждого мерозота. Для любого true Возвращающие значения, UnitOfWward будет хранить эту сущность в частной коллекции, специфичной для этого ирпозиота и к предполагаемой работе. В полной мере, UnitOfWork будет проходить все коллекции частных сущностей и вызывают соответствующую операцию на соответствующем ирпозиторе для каждого объекта.

Если у вас есть сущность, которая должна быть частично сохраняться по EF и частично сохраняется по AD, у вас будет два целованных класса для этого типа сущностей (они оба вернулись от Canpersist, когда передают экземпляр этого типа объекта).

Что касается поддержания атомности между EF и AD, то есть отдельная нетривиальная проблема.

IMO Я бы обернул звонки на обоих этих репо, в типе обслуживания класса. Тогда я бы использовал IOC / DI, чтобы ввести типы репо, в класс услуг. У вас будет 2 REPOS, 1 для ENT. Рамки и 1, что поддерживает объявление. Таким образом, каждое репо касается только его подложного хранилища данных и не нужно переходить.

То, что я сделал, чтобы поддержать несколько единиц типов рабочих мест, состоит в том, чтобы иметь IUNITOFWORKWORK BEOR FACTION. Я создаю другой тип, называемый IUNITOFWORKSCOPE, который является фактической единицей работы, и у него есть только метод фиксации.

namespace Framework.Persistance.UnitOfWork
{
    public interface IUnitOfWork
    {
        IUnitOfWorkScope Get();

        IUnitOfWorkScope Get(bool shared);
    }

    public interface IUnitOfWorkScope : IDisposable
{
    void Commit();
}
}

Это позволяет мне вводить различные реализации устройства работы в службу и иметь возможность использовать их рядом.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top