Архитектура приложения — транзакции с RavenDB

#repository-pattern #ravendb #n-tier-architecture

#репозиторий-шаблон #ravendb #n-уровневая архитектура

Вопрос:

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

Я пишу бизнес-уровни для каждой сущности (почти), и у меня есть уникальный класс репозитория, который обрабатывает запросы и хранилище документов. Я не понимаю, как я должен делиться хранилищем документов по уровням служб и обрабатывать транзакции.

Я показываю пример, в котором я пытаюсь сохранить и прочитать документ в одной транзакции.

Пример репозитория:

 public class RavenRepository
{
    private static DocumentStore _store;
    private IDocumentSession _session;

    public RavenRepository(DocumentStore store)
    {
        _store = (_store==null) ? new DocumentStore() { Url = "http://wsk-gcardoso:8081" } : store;           
    }
    public T SingleOrDefault<T>(Func<T, bool> predicate) where T : BaseModel
    {
        using (var session = _store.OpenSession())
        {
            return session.Query<T>().SingleOrDefault(predicate);
        }
    }
    public T Add<T>(T item) where T : BaseModel
    {            
        using (var session = _store.OpenSession())
        {
            session.Advanced.AllowNonAuthoritiveInformation = this.AllowNonAuthoritiveInformation;
            session.Store(item);
            session.SaveChanges();
        }
        return item;
    }
    public void Initialize() {
        _store.Initialize();
    }
    public void Dispose() {
        _store.Dispose();
    }
}
  

Бизнес-уровень будет выглядеть следующим образом:

 public class NewsletterBusiness
{
    private RavenRepository repository;
    public NewsletterBusiness(RavenRepository ravenRepository)
    {       
        repository = (ravenRepository == null) ? RavenRepository(null) : ravenRepository;
    }

    public Newsletter Add(Newsletter newsletter)
    {
        Newsletter news = repository.Add(newsletter);
        return news;
    }
    public Newsletter GetById(long Id)
    {
        Newsletter news = repository.SingleOrDefault<Newsletter>(x => x.Id == Id);
        return news;
    }
}
  

Теперь я пытаюсь сохранить и прочитать объект (информационные бюллетени) в той же транзакции.
Из того, что я прочитал, я должен установить для AllowNonAuthoritativeInformation хранилища документов значение false, чтобы дождаться завершения транзакции. Но, исходя из того, как я работаю со слоями и репозиторием, сохраняю ли я и запрашиваю БД в одной транзакции?

Честно говоря, я думаю, что меня смущает метод хранения OpenSession. Я думаю, что я путаю сеанс с транзакцией.

Например, этот код:

 var repository = new RavenRepository(null);
newsletterBusiness = new NewsletterBusiness(repository);
repository.Initialize();
using (var tx = new TransactionScope())
{
    Newsletter new = newsletterBusiness.Add(new Newsletter { Title = "Created by Tests", Content = "Created By Tests" });

    Newsletter objectCreated = newsletterBusiness.GetById(new.Id);
    repository.Dispose();
    tx.Complete();
}
  

Если я создам второй бизнес-уровень (например, для изображений) и установлю picturebusiness.repository = repository (тот же объект RavenRepository, установленный для BusinessLayer), я буду работать в том же сеансе newsletterBusiness.repository?

 picturesBusiness = new PicturesBusiness(repository);
Picture pic = picturesBusiness.GetById(20);
  

Я был бы очень признателен за помощь по этому вопросу,
Приветствия из Португалии!

Ответ №1:

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

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

Если вы хотите иметь транзакционную запись, которая охватывает типы, учитывая вашу архитектуру, вам придется использовать DTC, потому что каждый отдельный сеанс представляет собой другое соединение с базой данных. Если бы вы использовали модель сеанса для каждого запроса, у вас был бы только один сеанс, и у вас была бы транзакционная семантика, просто вызвав SaveChanges() один раз.

Ответ №2:

Вы пытаетесь абстрагировать сеанс raven (который в основном является UnitOfWork) с помощью шаблона репозитория.

Я бы предложил вам следующий подход — создайте абстракцию UnitOfWork, которая будет инкапсулировать сеанс RavenDB и создавать из него экземпляры репозиториев. Вот некоторые псевдокоды:

 public class RavenUnitOfWork {
    private IDocumentSession m_session;

    public UnitOfWork() {
        // initialize m_session here
    }

    public Repository<T> GetRepository() {
        return new Repository<T>(m_session);
    }

    public void Commit() {
        m_session.SaveChanges();
    }
}

public class RavenRepository<T> {
    public Repository(IDocumentSession session) {
        // Store the session
    }
    public T Load(string id) {...}
    public void Store(T entity) {...}
    public void Delete(T entity) {...}
    public IQueryable<T> Query() {...}
}
  

Здесь вы явно реализуете эти шаблоны.

PS: Конечно, вы можете объявить IUnitOfWork и IRepository на свой вкус…