Стратегии для абстрагирования идентификатора БД из класса домена

#java #database #spring #spring-data

#java #База данных #spring #spring-данные

Вопрос:

Я пишу простое CRUD-приложение на Java и Spring4 с spring-data, и я хочу посмотреть, смогу ли я сохранить домен как можно более чистым / чистым, и мне нужны некоторые идеи / мысли о том, как я могу добиться этого.
Я структурировал проект с родительским pom и некоторыми дочерними модулями следующим образом:

 parent pom
    |- domain
    |
    |- persistence
    |   |- api
    |   |- impl
    |
    |- service
    |   |- api
    |   |- impl
    |
    |- rest
  

В модуле persistence api у меня есть интерфейсы с методами для сохранения / извлечения моих классов домена. например:

 public interface GiftPersistence() {

    Gift saveOrUpdateGift(Gift gift);
    /* other methods ... */
}
  

Важно отметить, что эти методы не являются методами репозитория. Методы в этом модуле — это те, которые вызываются реализациями сервисного модуля.

Модуль persistence impl имеет реализацию интерфейса, специфичную для базы данных, которую я использую (сегодня это mongo, но может быть oracle или ms-sql или что-нибудь еще). Здесь у меня есть интерфейс репозитория, специфичный для поставщика базы данных.

В модуле домена у меня есть свои классы домена, но я не хочу моделировать что-либо, ориентированное на базу данных. например:

 public final class Gift {

    private String description;

    private boolean claimed;

    private String claimedBy;

    /* getters amp; setters etc ... */
}
  

Причины, по которым я не хочу моделировать специфические для базы данных вещи:

  • Постоянство не является проблемой домена
  • Разные поставщики баз данных могут использовать разные стратегии идентификаторов / типы данных, и я не хочу связывать код моего домена с каким-либо поставщиком базы данных
  • Возможно, я захочу импортировать модуль домена в pom другого проекта, и этот проект может ничего не делать с базой данных

В прошлом я участвовал в проекте, который делал что-то подобное (честно говоря, именно отсюда у меня возникла идея!), Но у меня больше нет доступа к этому проекту, поэтому я не могу использовать его для написания идей.
Сказав это, я смутно помню, что это что-то сделало с jackson mixins (хотя это могла быть похожая идея / концепция, но ближе к веб-проблеме, чем к проблеме БД).
Другой подход, о котором я думал, — это использование аспектов, но не совсем уверен, как / где я бы это сделал.

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

Отредактировано с новой идеей, которую я только что нашел
Это интересная идея — https://github.com/CK35/example-ddd-with-spring-data-jpa
Идея заключается в том, что модуль домена моделирует классы домена как интерфейсы, а конкретные реализации находятся (в моем случае) в модуле persistence-impl. Таким образом, они могут быть аннотированы с учетом проблем домена, включая дополнительные свойства, такие как id.
Это кажется хорошим вариантом, поскольку это означает, что домен чистый (хотя и не конкретная реализация), и у нас могут быть разные реализации модуля persistence-api, где реализации классов домена имеют общие аннотации jpa, или аннотации mongo, или oracle …

Есть еще идеи?

Комментарии:

1. Рассмотрите возможность использования JPA / Spring-Data-JPA? Он может обрабатывать большинство распространенных поставщиков баз данных для вас, и аннотации безопасно игнорируются (я полагаю), если JPA не настроен на их сканирование.

2. Спасибо @CollinD, но (если я правильно понимаю), это именно то, чего я хочу избежать. Если я использовал аннотации, это означает, что мне нужно добавить свойство для идентификатора в классы домена (что не имеет ничего общего с доменом), и мне также нужно было бы отключить модуль домена, импортировав классы jpa.

Ответ №1:

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

Я не уверен, почему вы думаете, что наличие поля ID будет означать, что класс будет зависеть от базы данных. Весь замысел spring заключается в том, чтобы не заставлять его взаимодействовать с какой-либо технологией сохранения, которую вы хотите, и поэтому вы можете использовать hibernate или что-то еще для сохранения. Если вы используете hibernate, вы можете настроить свои классы сохраняемости в xml, поэтому вы не увидите никаких данных о сохраняемости в вашем объекте домена… конечно, за исключением идентификатора, хотя я не вижу в этом проблемы, если это серьезная проблема, вы могли бы расширить базовый класс, у которого есть поле private id, но вам это может не понравиться.

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

IMHO превращать все сервисы, dao, объекты и все это в интерфейсы, а затем иметь только одну реализацию, является излишним, хотя я знаю, что некоторые люди не согласятся. Но я считаю, что это все еще запах кода, тем не менее, потому что в конце дня у вас есть интерфейс только с 1 реализацией. Допустим, у вас будет более одной реализации позже, вам буквально потребуется 10 секунд, чтобы превратить это обратно в интерфейс с 1 реализацией с рефакторингом в eclipse. Просто скопируйте свой код реализации где-нибудь, извлеките интерфейс, измените текущий класс на извлеченный интерфейс и верните свой код копии в свой проект с чем-то вроде InterfaceImp extends Foo сверху. Выполнено. Не нужно переусердствовать с самого начала с поддержанием некоторого интерфейса-призрака.

Комментарии:

1. Я согласен, что интерфейсы с 1 impl — это запах кода, но в данном случае это упражнение в обучении, а не то, что вы могли бы сделать в реальном проекте (если это имеет смысл!) Я думаю, что то, что я пытаюсь сделать, это DDD. Я думаю, что столбец ID зависит от базы данных. Oracle может использовать long, mongodb использует ObjectId (который может быть смоделирован как строка). Или может потребоваться, чтобы идентификаторы имели заданный шаблон (хотя я бы сказал, что это бизнес-ключ, а не идентификатор БД). В любом случае, Oracle и mongo — наглядный пример того, что идентификаторы отличаются. Моделирование их в домене привязывает вас к БД

2. Теперь я понимаю, что вы имеете в виду под идентификатором. Но разве вы не можете просто использовать дженерики? И, кроме того, у вас все еще могут быть объекты домена, которым требуется идентификатор. Классический пример — сотрудники с одинаковыми именами. Без идентификатора, такого как id / username, вы не сможете отличить их друг от друга. Но, похоже, вы настроены на то, чтобы избавиться от идентификаторов, вы ненавидите идентификаторы? ха-ха