Этот пост объясняет, почему я выбрал Node.js в качестве платформы времени выполнения и JavaScript в качестве языка программирования для своей книги Реализация DDD, CQRS и Event Sourcing. Описанные причины включают в себя личный опыт, желаемую целевую аудиторию, а также характеристики платформы и языка. Кроме того, кратко обсуждаются преимущества и последствия статических типов. Наконец, пост завершается обзором будущих дополнений к существующей книге.

Личный опыт

Одной из причин использования Node.js и JavaScript в моей книге является то, что я работал с обоими почти 8 лет. Что касается JavaScript как языка программирования, то у меня около 14 лет опыта. Кроме того, эти технологии использовались в двух проектах, в которых я применил CQRS и Event Sourcing. Поэтому это казалось мне естественным выбором, когда я планировал написать техническую книгу.

Широкая аудитория

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

Что касается сходства синтаксиса, вот примерный класс Application Services в JavaScript:

class NoteApplicationServices {
  #noteRepository;
  constructor({noteRepository}) {
    this.#noteRepository = noteRepository;
  }
  async createNote({noteId, content, category}) {
    const note = new Note({id: noteId, content, category});
    await this.#noteRepository.save(note);
  }
  /* .. */
}

Эквивалентная (блокирующая) реализация в Java отличается только аннотациями типов:

public class NoteApplicationServices {
  private NoteRepository noteRepository;
  
  constructor(NoteRepository noteRepository) {
    this.noteRepository = noteRepository;
  }
  public void createNote(UUID id, string content, string category) {
    Note note = new Note(id, content, category);
    this.noteRepository.save(note);
  }
  /* .. */
}

Это сходство синтаксиса также дано для других языков программирования, таких как C# или PHP.

универсальный язык

В моей книге есть отдельная глава, посвященная пользовательскому интерфейсу. Там я также использую JavaScript для всего клиентского кода. Это связано с тем, что он изначально поддерживается в браузере и, вероятно, является наиболее часто используемым языком для веб-интерфейсов. Кроме того, существует некий универсальный код инфраструктуры, который используется как сервером, так и клиентом. При таком подходе читателю нужно знать только один язык, чтобы понимать как внутренний, так и внешний код.

Простота платформы

На протяжении многих лет я работал с несколькими языками, средами выполнения и серверами для бэкендов. Профессионально я использовал PHP и Apache, а также C#, CLR и IIS. Когда я начал работать с Node.js, меня впечатлили минимальные накладные расходы для определенных вариантов использования. В то же время он также подходил для создания сложного программного обеспечения производственного уровня, особенно с некоторыми сторонними библиотеками. В целом, он хорошо подходит для описанных в моей книге реализаций, которые часто служат иллюстративным целям.

В качестве примера рассмотрим упрощенную фабрику для создания интерфейса файловой системы HTTP:

const createHttpFilesystemInterface = pathResolver =>
  async (request, response) => {
    const filePath = pathResolver(request);
    try {
      await stat(filePath);
      const fileExtension = path.extname(filePath);
      const contentType =
        contentTypeByExtension[fileExtension] || 'text/plain';
      response.writeHead(200, {'Content-Type': contentType});
      createReadStream(filePath).pipe(response);
    } catch (error) {
      response.writeHead(404);
      response.end();
    }
  };
const contentTypeByExtension = {
  '.js': 'application/javascript',
  '.html': 'text/html',
  '.css': 'text/css',
};
// example usage
const httpFilesystemInterface = createHttpFilesystemInterface(
  request => `${rootDirectory}/${url.parse(request.url).pathname}`);
http.createServer(httpFilesystemInterface).listen(50000);

Это не доказывает, что Node.js обычно упрощает работу. Однако есть много платформ, где используется намного больше стандартного кода.

Лаконичный и компактный код

Примеры кода, показанные в моей книге, должны быть компактными. Особенно в версии PDF, где длина строки ограничена 85 символами. В некоторых аспектах JavaScript позволяет писать лаконичный и компактный код. Часто это относится к простым вещам из-за минимальных накладных расходов или церемоний. Кроме того, существуют определенные языковые конструкции, которые помогают сделать код коротким. Некоторыми примерами являются выражения стрелочных функций, литералы объектов, сокращенные имена свойств и присваивания деструктурирования.

В приведенном ниже примере показана реализация простого типа Value Object:

const Money = function({value, currency}) {
  Object.freeze(Object.assign(this, {value, currency}));
};

На первый взгляд пример может даже показаться чрезмерно сложным. Можно также реализовать стрелочную функцию, которая создает анонимный объект и замораживает его. Однако приведенная выше реализация сравнима с полноценным классом в языке, основанном на классах. Функция-конструктор возвращает экземпляр, тип которого можно проверить. Например, выражение new Money({value: 10, currency: 'USD'}) instanceof Money оценивается как true.

Что насчет типов?

JavaScript — это язык с динамической типизацией. С другой стороны, в нем отсутствует статическая типизация. Это можно рассматривать как преимущество или недостаток. Что касается DDD и моделирования сложных доменов, то это, пожалуй, недостаток. Реализация сложной модели предметной области выигрывает от статической типизации и мощной системы типов. Для примеров в моей книге отсутствие типов на самом деле выгодно, так как это делает код компактным.

В качестве примера преимуществ мощной статической системы типов посмотрите на следующую реализацию модели предметной области:

type Author = {id: UUID, firstName: string, lastName: string};
type Grade = 'A' | 'B' | 'C' | 'D' | 'E' | 'F';
type ExamResult = {author: Author, grade: Grade};

Тип Grade выражает специфичную для предметной области концепцию оценки на уровне типа. Для обеспечения правильных значений не требуется никаких проверок во время выполнения. Помимо уровня домена, другие части программного обеспечения могут не так сильно выигрывать от статических типов. Кроме того, тривиальные проблемы предметной области могут быть адекватно решены с помощью простого JavaScript. Кроме того, есть возможности для проверки типов во время выполнения. Как правило, использование TypeScript не имеет недостатков. В то же время простого JavaScript тоже может быть достаточно.

Почему не что-то другое?

Было бы много альтернатив языку программирования и среде выполнения для моей книги. Node.js и JavaScript, как правило, не лучше подходят для применения DDD, CQRS или Event Sourcing. Как уже упоминалось, простой JavaScript может даже не подходить для решения сложных проблем предметной области из-за отсутствия статических типов. Тем не менее, эти два оказались подходящим выбором для моей книги. В конце концов, большинство примеров реализации в любом случае можно легко сопоставить с другими технологиями.

Планы на книгу

Во время работы над книгой меня иногда беспокоило то, что я не использую TypeScript или даже выбор технологий в целом. Чтобы успокоиться, я подумал о будущих редакциях с использованием TypeScript или даже Rust без Node.js. На данный момент я не думаю, что это необходимо. Тем не менее, есть потенциал для полезных дополнений в будущем. Мой ближайший план состоит в том, чтобы либо написать приложение о преимуществах типов, либо аннотировать существующий затронутый контент.

Купить книгу * Обсудить в Twitter

Первоначально опубликовано на https://www.alex-lawrence.com 21 декабря 2020 г.