Ответ 1
Наличие некоторых пакетов среди разных слоев не является чем-то необычным, однако обычно это делается только для сквозных задач, таких как ведение журнала. Ваша модель не должна делиться разными слоями, или изменения в модели потребуют изменений во всех этих слоях. Как правило, ваша модель представляет собой более низкий уровень, близкий к слою данных (более, под или переплетенный, в зависимости от подхода).
Объекты передачи данных, как следует из их имени, являются простыми классами, используемыми для передачи данных. Таким образом, они обычно используются для связи между уровнями, особенно когда у вас есть архитектура SOA, которая обменивается сообщениями, а не объектами. DTO должны быть неизменными, поскольку они просто существуют для передачи информации, а не для ее изменения.
Ваши объекты домена - это одно, ваши DTO - это другое дело, а объекты, которые вам нужны в вашем слое презентации, - это еще одна вещь. Однако в небольших проектах может не стоить усилий по внедрению всех этих разных наборов и конвертации между ними. Это зависит только от ваших требований.
Вы разрабатываете веб-приложение, но оно может помочь вашему дизайну спросить себя: "Могу ли я переключить свое веб-приложение на настольное приложение? Неужели мой сервисный уровень не знает о моей логике представления?". Мышление в этих условиях поможет вам улучшить архитектуру.
На ваши вопросы:
Предположим, что слой persistence будет использовать класс myproject.persistence.domain.UserEntity(объект на основе JPA) для хранения и загрузки данных в/из базы данных. Чтобы показать данные в представлении, я бы предоставил еще один класс myproject.service.domain.User. Где я их конвертирую? Будет ли служба для пользователей отвечать за преобразование между двумя классами? Это действительно поможет улучшить сцепление?
Уровень сервиса знает свои классы (DTO) и слой под ним (пусть говорят, упорство). Так что да, служба несет ответственность за перевод между сохранением и самой.
Как должен выглядеть класс User? Должны ли они содержать только геттеры, чтобы быть неизменяемыми? Разве это не было бы громоздким для взглядов на редактирование существующих пользователей (создание нового пользователя, использование геттеров существующего объекта User и т.д.)?
Идея DTO заключается в том, что вы используете их только для передачи, поэтому операции, такие как создание нового пользователя, не требуются. Для этого вам нужны разные объекты.
Должен ли я использовать те же DTO-классы (Пользователь) для отправки запроса службе, чтобы изменить существующего пользователя/создать нового пользователя или мне нужно реализовать другие классы?
Способы обслуживания могут выражать операцию, а DTO - ее параметры, содержащие только данные. Другим вариантом является использование команд, которые представляют операцию, а также содержат DTO. Это популярно в архитектурах SOA, где ваша служба может быть простым командным процессором, например, с одной единственной операцией Execute
с интерфейсом ICommand
в качестве параметра (в отличие от одной операции для каждой команды).
Не будет ли уровень представления сильно зависеть от уровня сервиса, используя все DTO в myproject.service.domain?
Да, слой поверх служебного слоя будет зависеть от него. Это и есть идея. Положительный момент состоит в том, что только этот слой зависит от него, ни верхний, ни нижний уровни, поэтому изменения влияют только на этот уровень (в отличие от того, что происходит, если вы используете классы домена из каждого слоя).
Как обрабатывать мои собственные исключения? Мой текущий подход перескакивает большинство "серьезных" исключений, пока они не обрабатываются уровнем представления (обычно они регистрируются, и пользователю сообщают, что что-то пошло не так). С одной стороны, у меня проблема, что я снова обмениваю общий пакет. С другой стороны, я до сих пор не уверен, что это можно считать "лучшей практикой". Любые идеи?
Каждый слой может иметь свои собственные исключения. Они перетекают от одного уровня к другому, заключенного в следующий вид исключения. Иногда они обрабатываются одним слоем, который будет что-то делать (например, протоколирование) и, возможно, затем выдает другое исключение, которое должен обрабатывать верхний уровень. В других случаях они могут быть обработаны, и проблема может быть решена. Подумайте, например, о проблеме, связанной с базой данных. Это будет исключение. Вы могли бы справиться с этим и решили повторить попытку через секунду, и, может быть, тогда будет успех, поэтому исключение не будет течь вверх. Если повторная попытка также завершится неудачей, исключение будет повторно выбрано, и оно может полностью пройти до уровня презентации, где вы изящно уведомите пользователя и попросите его повторить слой.