← Назад к вопросам

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

2.0 Middle🔥 191 комментариев
#REST API и микросервисы#SOLID и паттерны проектирования

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Паттерн преобразования данных между сервисами

Вопрос касается нескольких связанных паттернов и практик, которые я активно использую в микросервисной архитектуре.

1. Mapper (Маппер) — основной паттерн

Mapper — это паттерн преобразования объекта из одной модели в другую.

// Модель из внешнего API
public class ExternalUserDto {
  public String firstName;
  public String lastName;
  public String email;
}

// Наша внутренняя модель
public class User {
  private String name;
  private String email;
}

// Mapper — преобразует DTO в нашу модель
public class UserMapper {
  public static User toDomain(ExternalUserDto dto) {
    User user = new User();
    user.setName(dto.firstName + " " + dto.lastName);
    user.setEmail(dto.email);
    return user;
  }
}

// Использование
ExternalUserDto externalUser = fetchFromExternalService();
User internalUser = UserMapper.toDomain(externalUser);

2. DTO (Data Transfer Object) — дополняющий паттерн

DTO — это объект, который переносит данные между слоями/сервисами.

// Слой API (Controller)
public class UserController {
  @PostMapping("/users")
  public UserResponse createUser(@RequestBody CreateUserRequest request) {
    User user = userService.create(request.getName(), request.getEmail());
    return UserMapper.toResponse(user); // DTO для ответа
  }
}

// DTO для входящих данных
public class CreateUserRequest {
  private String name;
  private String email;
}

// DTO для исходящих данных
public class UserResponse {
  private Long id;
  private String name;
  private String email;
}

3. Adapter (Адаптер) — когда нужна более сложная логика

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

// Интерфейс нашего сервиса
public interface PaymentService {
  void pay(String accountId, double amount);
}

// Интерфейс внешнего платёжного сервиса (другой)
public interface ExternalPaymentGateway {
  PaymentResult processPayment(PaymentRequest request);
}

// Adapter адаптирует внешний интерфейс под наш
public class PaymentGatewayAdapter implements PaymentService {
  private final ExternalPaymentGateway gateway;
  
  @Override
  public void pay(String accountId, double amount) {
    PaymentRequest request = new PaymentRequest();
    request.setMerchantId("our-merchant-id");
    request.setTransactionId(generateId());
    request.setAmount(amount);
    request.setAccountId(accountId);
    
    PaymentResult result = gateway.processPayment(request);
    
    if (!result.isSuccess()) {
      throw new PaymentException(result.getErrorCode());
    }
  }
}

4. Transformer (Трансформер) — для сложных преобразований

Transformer преобразует данные, часто с агрегацией или разделением.

// Модели из разных микросервисов
public class OrderDto {
  public String id;
  public String customerId;
  public double totalAmount;
}

public class CustomerDto {
  public String id;
  public String name;
  public String email;
}

public class InvoiceDto {
  public String id;
  public double amount;
}

// Трансформер агрегирует несколько DTO в одну модель
public class OrderTransformer {
  public static OrderReport transform(OrderDto order, CustomerDto customer, InvoiceDto invoice) {
    return OrderReport.builder()
      .orderId(order.id)
      .customerName(customer.name)
      .customerEmail(customer.email)
      .totalAmount(order.totalAmount)
      .invoiceId(invoice.id)
      .status(calculateStatus(order, invoice))
      .build();
  }
  
  private static String calculateStatus(OrderDto order, InvoiceDto invoice) {
    if (invoice.amount == order.totalAmount) return "PAID";
    if (invoice.amount == 0) return "UNPAID";
    return "PARTIALLY_PAID";
  }
}

5. Repository Pattern — для данных из БД

Когда данные из базы нужно преобразовать в model:

// Entity из БД
@Entity
public class UserEntity {
  @Id
  private Long id;
  private String firstName;
  private String lastName;
  private String email;
  @Enumerated(EnumType.STRING)
  private UserStatus status;
}

// Доменная модель
public class User {
  private Long id;
  private String fullName;
  private String email;
  private boolean isActive;
}

// Repository с встроенным маппингом
@Repository
public class UserRepository {
  @PersistenceContext
  private EntityManager em;
  
  public User findById(Long id) {
    UserEntity entity = em.find(UserEntity.class, id);
    if (entity == null) return null;
    return mapToDomain(entity);
  }
  
  private User mapToDomain(UserEntity entity) {
    User user = new User();
    user.setId(entity.getId());
    user.setFullName(entity.getFirstName() + " " + entity.getLastName());
    user.setEmail(entity.getEmail());
    user.setActive(entity.getStatus() == UserStatus.ACTIVE);
    return user;
  }
}

6. MapStruct — библиотека для автоматизации маппинга

Вместо ручного маппинга в больших проектах используем MapStruct:

// Зависимость: org.mapstruct:mapstruct

@Mapper(componentModel = "spring")
public interface UserMapper {
  
  // Простой маппинг: поля с одинаковыми названиями преобразуются автоматически
  @Mapping(source = "firstName", target = "firstName")
  @Mapping(source = "lastName", target = "lastName")
  User toDomain(ExternalUserDto dto);
  
  // С кастомной логикой
  @Mapping(target = "fullName", expression = "java(dto.getFirstName() + ' ' + dto.getLastName())")
  @Mapping(target = "isActive", source = "status", qualifiedByName = "mapStatus")
  User toWithCustom(ExternalUserDto dto);
  
  @Named("mapStatus")
  default boolean mapStatus(String status) {
    return "ACTIVE".equals(status);
  }
}

// Использование в сервисе
@Service
public class UserService {
  private final UserMapper mapper;
  
  public User importUser(ExternalUserDto dto) {
    return mapper.toDomain(dto);
  }
}

Сравнение паттернов

╔════════════════╦═════════════════════╦════════════════════════════╗
║ Паттерн        ║ Сложность           ║ Когда использовать         ║
╠════════════════╬═════════════════════╬════════════════════════════╣
║ Mapper         ║ Простой маппинг    ║ 1:1 преобразование полей  ║
║ DTO            ║ Обёртка данных     ║ Передача между слоями     ║
║ Adapter        ║ Адаптация интерфейса║ Несовместимые интерфейсы  ║
║ Transformer    ║ Сложная логика     ║ Агрегация/разделение      ║
║ Repository     ║ Работа с БД        ║ Entity → Domain mapping    ║
║ MapStruct      ║ Автоматизация      ║ Больших проекты           ║
╚════════════════╩═════════════════════╩════════════════════════════╝

Практический пример: Полный flow

// 1. Сервис получает данные из внешнего API
@Service
public class UserImportService {
  private final ExternalApiClient client;
  private final UserMapper mapper;
  private final UserRepository repository;
  
  public void importUsers() {
    // Получаем DTO из API
    List<ExternalUserDto> externalUsers = client.fetchUsers();
    
    // Трансформируем в наши модели
    List<User> users = externalUsers.stream()
      .map(mapper::toDomain)
      .collect(Collectors.toList());
    
    // Сохраняем в БД
    users.forEach(repository::save);
  }
}

// 2. Controller возвращает DTO
@RestController
public class UserController {
  private final UserService service;
  private final UserMapper mapper;
  
  @GetMapping("/users/{id}")
  public UserResponse getUser(@PathVariable Long id) {
    User user = service.findById(id);
    return mapper.toResponse(user); // Domain → DTO для API
  }
}

Ключевые принципы

Изоляция моделей — каждый слой имеет свои модели ✅ Явное преобразование — маппинг видимый и контролируемый ✅ DRY — используй MapStruct для автоматизации ✅ Валидация — валидируй на границах (контроллер, интеграция) ✅ Безопасность — не передавай внутренние поля наружу

Ответ на вопрос: Mapper (или DTO если имеется в виду паттерн данных). В сложных случаях — Adapter или Transformer. Для больших проектов — MapStruct для автоматизации.

Как называется паттерн, который преобразует модель данных одного сервиса в Java модель | PrepBro