Добавление базового класса к моим сущностям и DTO, чтобы я мог написать универсальный интерфейс преобразователя и реализовать его для каждого класса, просто не нужен (пока?) Для меня.

читал несколько статей и сообщений Stackoverflow для преобразования объектов домена в DTO и опробовал их в своем коде. Когда дело доходит до тестирования и масштабируемости, я всегда сталкиваюсь с некоторыми проблемами. Мне известны следующие три возможных решения для преобразования доменных объектов в DTO. Большую часть времени я использую Spring.

Решение 1. Частный метод в сервисном слое для конвертации

Первое возможное решение - создать небольшой «вспомогательный» метод в коде уровня обслуживания, который конвертирует извлеченный объект базы данных в мой объект DTO.

@Service
public MyEntityService {

  public SomeDto getEntityById(Long id){
    SomeEntity dbResult = someDao.findById(id);
    SomeDto dtoResult = convert(dbResult);
    // ... more logic happens
    return dtoResult;
  }

  public SomeDto convert(SomeEntity entity){
   //... Object creation and using getter/setter for converting
  }
}

Плюсы:

легко реализоватьне требуется дополнительный класс для преобразования -> проект не взрывается с сущностями

Минусы:

проблемы при тестировании, какnew SomeEntity() используется в частном методе, и если объект глубоко вложен, я должен предоставить адекватный результат моегоwhen(someDao.findById(id)).thenReturn(alsoDeeplyNestedObject) чтобы избежать NullPointers, если преобразование также разрушает вложенную структуру

Решение 2. Дополнительный конструктор в DTO для преобразования сущности домена в DTO

Второе решение - добавить в конструктор DTO дополнительный конструктор для преобразования объекта в конструктор.

public class SomeDto {

 // ... some attributes

 public SomeDto(SomeEntity entity) {
  this.attribute = entity.getAttribute();
  // ... nesting convertion & convertion of lists and arrays
 }

}

Плюсы:

не требуется дополнительный класс для преобразованияпреобразование скрыто в объекте DTO -> сервисный код меньше

Минусы:

использованиеnew SomeDto() в служебном коде и поэтому я должен предоставить правильную структуру вложенных объектов в результате моегоsomeDao насмешливый.

Решение 3: Использование конвертера Spring или любого другого внешнего компонента для этого преобразования

Если недавно увидел, что Spring предлагает класс по причинам конвертации:Converter<S, T> но это решение обозначает каждый внешний класс, который выполняет преобразование. С помощью этого решения я внедряю конвертер в мой сервисный код и вызываю его, когда хочу преобразовать сущность домена в мой DTO.

Плюсы:

легко проверить, так как я могу посмеяться над результатом во время тестаразделение задач -> выделенный класс делает работу

Минусы:

не "масштабируется" так сильно, как растет моя модель предметной области. С большим количеством сущностей мне нужно создать два конвертера для каждой новой сущности (-> преобразование полномочий DTO и полномочий DTO)

У вас есть больше решений для моей проблемы и как вы справляетесь с этим? Вы создаете новый конвертер для каждого нового доменного объекта и можете «жить» с количеством классов в проекте?

Заранее спасибо!

 M. Deinum17 дек. 2017 г., 11:25
Вариант 4 с использованием чего-то вродеMapStruct генерировать код сопоставления.

Ответы на вопрос(4)

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

ия или класс внешнего преобразователя, а просто добавил небольшой боб, который имеетconvert методы от каждой сущности до каждого DTO мне нужны. Причина в том, что отображение было:

или глупо просто, и я просто скопировал бы некоторые значения из одного поля в другое, возможно, с помощью небольшого вспомогательного метода,

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

Это означает, что я могу просто позвонить.map(converter::convert) на любой коллекции сущностей, чтобы вернуть поток моих DTO.

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

Добавление базового класса к моим сущностям и DTO, чтобы я мог написать универсальный интерфейс преобразователя и реализовать его для каждого класса, просто не нужен (пока?) Для меня.

Решение 3: Использование конвертера Spring или любого другого внешнего компонента для этого преобразования

И я создаюDtoConverter таким образом:

Маркер класса BaseEntity:

public abstract class BaseEntity implements Serializable {
}

Абстракция к маркеру класса:

public class AbstractDto {
}

Интерфейс GenericConverter:

public interface GenericConverter<D extends AbstractDto, E extends BaseEntity> {

    E createFrom(D dto);

    D createFrom(E entity);

    E updateEntity(E entity, D dto);

    default List<D> createFromEntities(final Collection<E> entities) {
        return entities.stream()
                .map(this::createFrom)
                .collect(Collectors.toList());
    }

    default List<E> createFromDtos(final Collection<D> dtos) {
        return dtos.stream()
                .map(this::createFrom)
                .collect(Collectors.toList());
    }

}

Интерфейс CommentConverter:

public interface CommentConverter extends GenericConverter<CommentDto, CommentEntity> {
}

Реализация класса CommentConveter:

@Component
public class CommentConverterImpl implements CommentConverter {

    @Override
    public CommentEntity createFrom(CommentDto dto) {
        CommentEntity entity = new CommentEntity();
        updateEntity(entity, dto);
        return entity;
    }

    @Override
    public CommentDto createFrom(CommentEntity entity) {
        CommentDto dto = new CommentDto();
        if (entity != null) {
            dto.setAuthor(entity.getAuthor());
            dto.setCommentId(entity.getCommentId());
            dto.setCommentData(entity.getCommentData());
            dto.setCommentDate(entity.getCommentDate());
            dto.setNew(entity.getNew());
        }
        return dto;
    }

    @Override
    public CommentEntity updateEntity(CommentEntity entity, CommentDto dto) {
        if (entity != null && dto != null) {
            entity.setCommentData(dto.getCommentData());
            entity.setAuthor(dto.getAuthor());
        }
        return entity;
    }

}
Решение Вопроса

Я полагаюРешение 1 не будет работать хорошо, потому что ваши DTO ориентированы на домен и не ориентированы на обслуживание. Таким образом, вероятно, что они используются в разных сервисах. Таким образом, метод отображения не принадлежит одному серверу и поэтому не должен быть реализован в одном сервисе. Как бы вы повторно использовали метод сопоставления в другом сервисе?

1. Решение будет работать хорошо, если вы используете выделенные DTO для каждого метода обслуживания. Но об этом в конце.

Решение 2. Дополнительный конструктор в DTO для преобразования сущности домена в DTO

В общем, хороший вариант, потому что вы можете видеть DTO как адаптер для сущности. Другими словами: DTO - это другое представление сущности. Такие конструкции часто обертывают исходный объект и предоставляют методы, которые дают вам другое представление обернутого объекта.

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

Решение 3: Использование конвертера Spring или любого другого внешнего компонента для этого преобразования

Решение 3 - это решение, которое я бы тоже предпочел. Но я бы создалMapper<S,T> интерфейс, который отвечает за отображение от источника к цели и наоборот. Например.

public interface Mapper<S,T> {
     public T map(S source);
     public S map(T target);
}

Реализация может быть выполнена с использованием картографической структуры, такой какmodelmapper.

Вы также сказали, что конвертер для каждой сущности

не "масштабируется" так сильно, как растет моя модель предметной области. С большим количеством сущностей мне нужно создать два конвертера для каждой новой сущности (-> преобразование полномочий DTO и полномочий DTO)

Я повторю, что вам нужно только создать 2 конвертера или один преобразователь для одного DTO, потому что ваш DTO ориентирован на домен.

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

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

Ваш ответ на вопрос