DTO в JavaScript

Дата публикации: 2023-02-06

Информационные системы обрабатывают данные, поэтому DTO (Data Transfer Object) — важное понятие современной разработки. В классическом смысле DTO — простой объект без логики, описывающий структуру данных, передаваемых по сети между удалёнными процессами. При передаче между слоями одного процесса говорят о локальном DTO.

У DTO две основные задачи:

Ниже — принципы, которых я придерживаюсь в JavaScript-приложениях.

Простой DTO

В простом случае DTO — плоская структура, где каждый атрибут имеет примитивный тип, например строку или число:

class Simple {
  aBool;
  aNumber;
  aString;
}

Это решает задачу структурирования. Для преобразования нужно принять входные данные и привести типы:

class Simple {
  constructor(data) {
    this.aBool = Boolean(data?.aBool);
    this.aNumber = Number.parseFloat(data?.aNumber);
    this.aString = String(data?.aString);
  }
}

Сложный DTO

Сложный DTO состоит из других DTO — простых и сложных — и примитивов:

class Complex {
  constructor(data) {
    this.aDto = new Simple(data?.aDto);
    this.aString = String(data?.aString);
  }
}

Преобразование JSON в DTO выглядит так:

const dto = new Complex({
  aDto: {aBool: true, aNumber: 16, aString: 'simple'},
  aString: 'complex',
});

Каскадное приведение типов

Для каскадного приведения типов в сложных объектах исходники связываются через import-export:

export default class Simple1 {}
import Simple from './simple.mjs';
export default class Complex {}

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

Отсечение или приведение

Пусть JSON содержит name, age и weight, а DTO Person готов обработать только первые два свойства. Можно явно оставить известные свойства:

this.name = String(data?.name);
this.age = Number.parseInt(data?.age);

или сначала скопировать всё, а затем привести известные поля:

Object.assign(this, data);
this.name = String(data?.name);
this.age = Number.parseInt(data?.age);

Если код — конечный обработчик DTO, лишние данные лучше отсечь. Если это промежуточный обработчик, лучше привести типы свойств, с которыми он работает, а незнакомые данные сохранить.

Итог

Дополнительные фрагменты исходного кода

{
} … and DTO for this data:
class Person {
name;
age;
}
constructor(data) {
this.name = String(data?.name);
this.age = Number.parseInt(data?.age);
}
constructor(data) {
Object.assign(this, data);
this.name = String(data?.name);
this.age = Number.parseInt(data?.age);
}