Проблемы JPA в DDD-приложениях

В сети можно иногда видеть проекты, авторы которых с энтузиазмом заявляют, что они представили пример программы, исполненной в стратегиях и тактике Domain Driven Design. Но на какую Domain Entity не посмотришь, каждая покрыта JPA аннотациями с головы до ног. Да, для Presentation-Domain-Data это нормально.

Но почему разработчики считают, что это соответствует духу и букве DDD? Не имею ни малейшего понятия. Куда хуже то, что подобное мышление иногда приходит в продуктовый код, которым потом очень тяжело управлять из-за жёсткой связи между структурой Domain Entity и таблицей в БД, чего быть не должно категорически.

DDD это не чёткий набор действий, но набор ограничений, которые накладываются на разработку. Одно из самых важных ограничений – это чёткое разделение на слои. Можно положить, что есть 3 основных: бизнес-домен, приложение и инфраструктура.

Слои, как и типично для луковичных архитектур, знают только о том, что они сами “оборачивают”. Слой приложения оборачивает бизнес-домен. А слой инфраструктуры оборачивает слои приложения и бизнес-домена. Эта идея аналогично описывается в куда более известной Clean Architecture от Роберта Мартина (Uncle Bob). Из-за этой схожести DDD и Clean Architecture рекомендуют к совместному использованию.

Именно тут заключается основная проблема с примерами “настоящих DDD-приложений”. JPA – это деталь реализации, элемент исключительно инфраструктурного уровня. Ни уровень приложения, ни тем более уровень бизнес-домена, не должны знать о @Id, @Entity, @Table, @Column, @JoinColumn и плеяде других аннотаций…

В принципе, код слоя бизнес-домена не должен иметь никаких внешних зависимостей. Не играет никакой роли, аннотации ли это или вызов метода из внешней библиотеки. Единственное разумное отступление – библиотеки для тестирования и выделенный артефакт кода с Shared Kernel, если Ваш проект действительно этого требует.

Чтобы упростить себе задачу, удобно создать отдельный модуль, в котором будет только код слоя бизнес-домена. Gradle и Maven отлично с этим подсобят.

Дальше нужно смотреть на конкретную ситуацию. Если получившаяся модель домена будет полностью совпадать со структурой хранения, а Вы используете Hibernate и Вам не хочется создавать конвертеры (мапперы) для каждого объекта, то можно прибегнуть к маппингу, например, через orm.xml, чтобы не трогать сущности.

Если модель не совпадает, можно сделать маппинг из JPA Entity в Domain Entity и обратно. Можно наследовать JPA Entity от Domain Entity и т.д., варианты есть.

Самое главное, что Вы получите – это изолированный и предсказуемый код в слое бизнес-домена, который позволит беспроблемно пережить смену технологий хранения и даже инфраструктурных фреймворков типа Spring или Jakarta EE.

Если Вы хотите обсудить содержание заметки, задать вопросы или предложить изменения, то со мной можно связаться в Telegram