Как реализовать шаблон репозитория и единицу работы при работе с несколькими хранилищами данных? - PullRequest
2 голосов
/ 26 мая 2010

У меня есть уникальная ситуация, когда я строю систему на основе 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 To SQL, который репозитории будут использовать для сохранения. Часть активного каталога была обработана доменной службой, реализованной в инфраструктуре и используемой репозиториями в каждом методе Save (). Save () отвечал за взаимодействие с контекстом данных для выполнения всех операций с базой данных.

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

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

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

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

Ответы [ 3 ]

4 голосов
/ 09 февраля 2011

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

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

http://thinkddd.com/assets/2/Domain_Driven_Design_-_Step_by_Step.pdf

1 голос
/ 20 января 2011

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

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 также будет иметь конструктор, который позволяет создавать 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();
  }
}

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

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

Реализация UnitOfWork будет использовать внедрение зависимостей для получения экземпляров всего IRepository. В UnitOfWork.Save/Add/Remove UoW передает объект аргумента в CanPerist каждого IRepository. Для любых возвращаемых значений true UnitOfWork будет хранить эту сущность в частной коллекции, специфичной для этого IRepository и для предполагаемой операции. В завершение UnitOfWork проверит все коллекции частных объектов и вызовет соответствующую операцию в соответствующем IRepository для каждого объекта.

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

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

0 голосов
/ 29 мая 2010

IMO Я бы обернул звонки на оба этих репозитория в класс обслуживания. Тогда я бы использовал IoC / DI для внедрения типов репо в класс обслуживания. У вас будет 2 репо, 1 для Ent. рамки и 1, который поддерживает AD. Таким образом, каждый репо работает только со своим базовым хранилищем данных и не должен пересекаться.

Что я сделал для поддержки нескольких единиц рабочих типов, так это чтобы IUnitOfWork был скорее фабричным. Я создаю другой тип с именем IUnitOfWorkScope, который является фактической единицей работы и имеет только метод фиксации.

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

        IUnitOfWorkScope Get(bool shared);
    }

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...