Что мы используем на бэке? - quilin/dm GitHub Wiki
Короткий ответ: очень много всякого.
Базы данных
Postgres
Она бесплатная, достаточно функциональная, хорошо реплицируется, умеет в jsonb, не умеет хранить смещение дат, но нам повезло с доменной зоной, где это не имеет значения. Мы используем ее там, где нужна релияционность, не имеет значения шардируемость и есть четко определенная структура данных.
Для общения с БД используется EntityFramework. У нас с ним долгие и сложные отношения, поэтому про него есть отдельная статья. Настоятельно рекомендуется с ней ознакомиться, прежде чем общаться с EF.
В EF есть замечательный механизм миграций, им нужно стараться пользоваться при любых обстоятельствах. Ситуации, когда что-то нужно провернуть без EF-миграций бывают, но они редки и нужно совершенно точно понимать, почему это та самая ситуация.
Mongo
Снова бесплатная (как будто это специально так), куча функционала, отличный движок для C#, тоже сложности с хранением смещений дат. Из коробки умеет в шардирование. Мы используем ее там, где данных много и они не очень ценны (например, чат), где важно уметь шардировать данные (например, чат), где схема данных плавающая (например, схемы характеристик).
ElasticSearch
БЕСПЛАТНАЯ, но в этом режиме не особо функциональная обертка над Lucene, с отличным движком для C#. Используем ее для хранения логов, а также для полнотекстового поиска. В последних версиях Postgres также есть индексы полнотекстового поиска, но несмотря на их бодрую производительность, автор был гораздо лучше знаком с эластиком, и Lucene все равно и производительнее, и функциональнее Postgres. По крайней мере, пока.
Брокер сообщений
RabbitMQ
Угадайте, сколько стоит. Движок на шарпах очень низкоуровневый, но у нас есть своя обертка над ним, достаточно стабильная и адекватная, с пулингом подключений, keepalive, простым апи и вот этим вот всем. Необходимо помнить, что RabbitMQ не хранит акнутые сообщения, так что это не БД, и он совсем не похож на Kafka своей концепцией. Подробнее про его устройство можно (и нужно) прочитать в официальной доке, она восхитительная. Дока по нашему клиенту - в отдельной статье.
Библиотеки
Сторонние либы
- Autofac - IoC-контейнер, был выбран в те времена, когда ему особо не было равных. Сейчас по производительности немножко проигрывает, но зато на несколько корпусов обставляет по функциональности прочие решения.
- FluentValidation - библиотека валидации, очень помогает в жизни, позволяет декларативно и очень элегантно описывать правила валидации моделей.
- Automapper - библиотека для преобразования POCO-объектов. Достаточно производительная, дает возможность декларативного описания преобразований.
- Polly - это сейчас промышленный стандарт для политик отказоустойчивости. Используем сейчас при работе с RabbitMQ, при микросервисной архитектуре идеально подходит для работы с внешними сервисами.
- SixLabors.ImageSharp - библиотека для работы с изображениями.
- NEST - клиент для работы с ElasticSearch. Классика.
- RabbitMQ.Client - один из базовых клиентов для работы с RabbitMQ. Низкоуровневый, и тем хорош.
- MongoDB.Driver - клиент для MongoDB. Тоже классика.
- MailKit - SMTP-клиент. Простой как пробка.
- AmazonS3 - клиент для S3-совместимых файловых хранилищ. Мы используем его в связке с Minio или CloudCube, на выбор.
Для тестов
- FluentAssertions - это отличная библиотека, которая позволяет писать тесты почти на английском языке.
- Moq - дефолтное решение для моков в тестах.
- 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;
}
Свои наработки
- Библиотека для работы с RabbitMQ -
DM.Services.MessageQueuing
. - Крошечная поделка
IUpdateBuilder
. BBCodeParser
- библиотека для парсинга BB-кодов.