Как чисто (физически) отделить слой домена от кода данных Spring?

В моем приложении DDD-by-the-book у меня есть такое определение репозитория на моем уровне домена:

public interface CustomerRepository {
    Customer findById(long id);
    ...
}

Уровень интеграции базы данных содержит реализацию этого интерфейса, например:

public class CustomerDao implements CustomerRepository {
    public Customer findById(long id) {
        // access EntityManager or JDBCTemplates or ...
    }
}

Для каждого уровня существует модуль, а модуль базы данных зависит от домена и всех интеграционных библиотек (например, спящего режима), а модуль домена зависит от чего угодно. Таким образом, у нас есть четкое разделение проблем и никаких «технических» зависимостей домена, как предлагает DDD. Как следствие, я могу переключиться с базы данных на постоянство в памяти, создав соответствующую реализацию репозитория. Используемая реализация настроена на уровне моего приложения.

Реализация репозиториев для доступа к базе данных ужасна и больше не нужна, поскольку у нас есть Spring Data. Чтобы использовать данные Spring, мне нужно определить такой репозиторий.

public interface CustomerRepository implements Repository<Customer, Long> {
    ....

Это означает, что поскольку определение интерфейса репозитория находится в моем доменном слое, у меня теперь есть зависимость моего доменного слоя от «технической» библиотеки. При переключении на реализацию в памяти у меня также будут классы spring-data в моем приложении. Я думаю, это немного попахивает.

Как ты об этом думаешь? Как ты с этим справишься? Можно ли использовать данные Spring и НЕ иметь зависимости от моего уровня домена?

Спасибо

(Кстати: мой бизнес-объект аннотирован JPA, поэтому мой модуль домена зависит от javax.persistence. Но я могу жить с этим, в основном потому, что javax.persistence содержит только аннотации и не содержит реализаций. Так что это скорее «логический», чем "техническая" зависимость. Я полагаю, что зависимость от модуля spring-data-annotation будет меньше пахнуть.)


person tchick    schedule 09.12.2015    source источник
comment
Я не слишком знаком с данными спринта, но не могли бы вы сказать что-то вроде того, что CustomerDae реализует CustomerRepository, Repository ‹Customer, Long›   -  person Batavia    schedule 10.12.2015
comment
Поскольку Repository<Customer, Long> - это интерфейс из Spring Data, и автор не хочет, чтобы его интерфейс домена напрямую зависел от него.   -  person Ken Chan    schedule 10.12.2015
comment
Это не проблема, если CustomerDao зависит от Spring Data Repository, потому что Dao является реализацией интерфейса репозитория и находится внутри уровня интеграции. Но чтобы использовать Spring Data напрямую, интерфейс репозитория расширил бы Spring Data Repository.   -  person tchick    schedule 12.12.2015


Ответы (2)


Недавно я столкнулся с той же проблемой, работая над проектом с использованием Spring Data и следуя архитектуре Onion. Ниже мой рецепт:

  1. определить CustomerRepository (на уровне вашего домена) без ссылок на Spring Data:

    public interface CustomerRepository {
      Customer findById(long id);
      // ..
    }
    
  2. определите CustomerDao на уровне доступа к данным, который зависит от Spring Data:

    public CustomerDao implements Repository<Customer, Long> { }
    
  3. определить реализацию домена CustomerRepository, который будет делегировать все свои вызовы в CustomerSpringDataRepository и в который Spring будет внедрять экземпляр CustomerDao:

    public class CustomerRepositoryImpl implements CustomerRepository {
       @Autowired private CustomerDao dao;
    
       public Customer findById(long id) { return doa.findById(id); }
    }
    
  4. внедрить CustomerRepository в уровень вашего домена

При таком подходе:

  • уровень вашего домена не зависит от Spring Data, только уровень доступа к данным зависит от Spring Data.
  • ваш домен не зависит от уровня доступа к данным, что означает, что классы инфраструктуры (например, Spring Data) не отображаются в домене

Я создал образец проекта с использованием Spring Data и архитектуры Onion следуя описанному выше подходу.

person Adam Siemion    schedule 11.12.2015

Внедрите репозиторий Spring Data в репозиторий вашего домена, тогда интерфейс репозитория вашего домена больше не будет зависеть от «технической» библиотеки.

public interface CustomerSprignDataRepository implements Repository<Customer, Long> {

}

public class CustomerDao implements CustomerRepository {

      @Inject
      private CustomerSprignDataRepository customerSprignDataRepository;

      public Customer findById(long id) {
          // access EntityManager or JDBCTemplates or Spring Data Repository
      }
}
person Ken Chan    schedule 09.12.2015
comment
Если вы вводите репозиторий Spring Data, который использует классы / интерфейсы данных Spring (репозиторий), в свой домен, тогда ваш домен должен иметь зависимость от Spring Data. - person Adam Siemion; 11.12.2015
comment
@adam, в чем разница между вашим предложением и моим? Оба внедряют репозиторий Spring данных в реализацию репозитория doman, я не вижу каких-либо фундаментальных различий, за исключением того, что оба используют разные имена для именования репозиториев. Кроме того, домен будет зависеть только от интерфейса репозитория домена, но не от его реализации, поэтому не будет зависеть от зависимости данных Spring - person Ken Chan; 11.12.2015
comment
Ввести репозиторий Spring Data - вы имеете в виду CustomerDao или CustomerSpringDataRepository? - person Adam Siemion; 12.12.2015
comment
@adam, я думаю, его название уже дает вам ответ. Репозиторий Spring Data означает это. Итак, это Клиент SpringDataRepository. - person Ken Chan; 12.12.2015
comment
Итак, чтобы скомпилировать CustomerSpringDataRepository, который расширяет Repository Spring Data (кстати, в вашем фрагменте кода есть ошибка), вам нужны Spring Data, поэтому для того, чтобы иметь возможность вставлять CustomerSpringDataRepository в уровень вашего домена, вам нужна зависимость от Spring Data, что-то что автор вопроса действительно хочет. - person Adam Siemion; 12.12.2015
comment
@AdamSiemion: домен напрямую использует CustomerRepository, который является просто интерфейсом и свободен от любых зависимостей Spring Data. . CustomerSpringDataRepository никогда не будет внедряться в домен, он внедряется в CustomerDao. Оба CustomerSpringDataRepository и CustomerDao находятся внутри уровня интеграции базы данных автора. Опять же, я действительно не вижу фундаментальных различий между вашим предложением и моим. - person Ken Chan; 12.12.2015
comment
Да, это тот же подход. Я предполагаю, что первое предложение ввело меня в заблуждение. Внедрить репозиторий Spring Data в репозиторий вашего домена, из-за чего было сделано неверное предположение, что вы предлагаете внедрить репозиторий Spring Data в домен. Mea culpa, +1. - person Adam Siemion; 12.12.2015