Что мы используем на бэке? - quilin/dm GitHub Wiki

Короткий ответ: очень много всякого.

Базы данных

Postgres

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

Для общения с БД используется EntityFramework. У нас с ним долгие и сложные отношения, поэтому про него есть отдельная статья. Настоятельно рекомендуется с ней ознакомиться, прежде чем общаться с EF.

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

Mongo

Снова бесплатная (как будто это специально так), куча функционала, отличный движок для C#, тоже сложности с хранением смещений дат. Из коробки умеет в шардирование. Мы используем ее там, где данных много и они не очень ценны (например, чат), где важно уметь шардировать данные (например, чат), где схема данных плавающая (например, схемы характеристик).

ElasticSearch

БЕСПЛАТНАЯ, но в этом режиме не особо функциональная обертка над Lucene, с отличным движком для C#. Используем ее для хранения логов, а также для полнотекстового поиска. В последних версиях Postgres также есть индексы полнотекстового поиска, но несмотря на их бодрую производительность, автор был гораздо лучше знаком с эластиком, и Lucene все равно и производительнее, и функциональнее Postgres. По крайней мере, пока.

Брокер сообщений

RabbitMQ

Угадайте, сколько стоит. Движок на шарпах очень низкоуровневый, но у нас есть своя обертка над ним, достаточно стабильная и адекватная, с пулингом подключений, keepalive, простым апи и вот этим вот всем. Необходимо помнить, что RabbitMQ не хранит акнутые сообщения, так что это не БД, и он совсем не похож на Kafka своей концепцией. Подробнее про его устройство можно (и нужно) прочитать в официальной доке, она восхитительная. Дока по нашему клиенту - в отдельной статье.

Библиотеки

Сторонние либы

  1. Autofac - IoC-контейнер, был выбран в те времена, когда ему особо не было равных. Сейчас по производительности немножко проигрывает, но зато на несколько корпусов обставляет по функциональности прочие решения.
  2. FluentValidation - библиотека валидации, очень помогает в жизни, позволяет декларативно и очень элегантно описывать правила валидации моделей.
  3. Automapper - библиотека для преобразования POCO-объектов. Достаточно производительная, дает возможность декларативного описания преобразований.
  4. Polly - это сейчас промышленный стандарт для политик отказоустойчивости. Используем сейчас при работе с RabbitMQ, при микросервисной архитектуре идеально подходит для работы с внешними сервисами.
  5. SixLabors.ImageSharp - библиотека для работы с изображениями.
  6. NEST - клиент для работы с ElasticSearch. Классика.
  7. RabbitMQ.Client - один из базовых клиентов для работы с RabbitMQ. Низкоуровневый, и тем хорош.
  8. MongoDB.Driver - клиент для MongoDB. Тоже классика.
  9. MailKit - SMTP-клиент. Простой как пробка.
  10. AmazonS3 - клиент для S3-совместимых файловых хранилищ. Мы используем его в связке с Minio или CloudCube, на выбор.

Для тестов

  1. FluentAssertions - это отличная библиотека, которая позволяет писать тесты почти на английском языке.
  2. Moq - дефолтное решение для моков в тестах.
  3. Microsoft.EntityFrameworkCore.InMemory - провайдер БД движка в памяти для EF. Под капотом, по сути, просто словари в памяти. Настоятельно рекомендуется держать в голове тот факт, что тесты БД через этот движок по сути ничего не гарантируют. Ниже пример кода, тест на который будет зеленым на провайдере в памяти, но на Npgsql-провайдере код будет падать 10 раз из 10.
public async Task SearchInParallel(Guid commentId, CancellationToken cancellationToken)
{
    var findForumCommentTask = dbContext.ForumComments
        .Where(c => c.Id == commentId)
        .Select(c => c.Text)
        .FirstOrDefaultAsync(cancellationToken);
    var findGameCommentTask = dbContext.ForumComments
        .Where(c => c.Id == commentId)
        .Select(c => c.Text)
        .FirstOrDefaultAsync(cancellationToken);

    await Task.WhenAll(findForumCommentTask, findGameCommentTask);

    return await findForumCommentTask ?? await findGameCommentTask;
}

Свои наработки

  1. Библиотека для работы с RabbitMQ - DM.Services.MessageQueuing.
  2. Крошечная поделка IUpdateBuilder.
  3. BBCodeParser - библиотека для парсинга BB-кодов.