На выполнение задания отводится 2 часа:
- Первый час — реализация основного решения задачи
- Второй час — полировка и доведение кода до препродакшен состояния: тесты, дефекты, чистка кода (НЕ настройка CI/CD, автоматизации сборки или деплоя)
- Важно: Не забудьте ответить на вопросы в конце задания.
Разработать backend-прототип на TypeScript с акцентом на проектирование и реализацию расширяемого и удобного API работы с транзакциями БД, используя Kysely. Важна не только корректность, но и архитектурная продуманность, масштабируемость при росте кодовой базы и читаемость.
- Создать макет TypeScript-приложения.
- Можно использовать готовые шаблоны. Структура проекта и его организация должны отражать зрелый подход к проектированию backend-приложений. Предполагается, что в будущем кодовая база приложения значительно вырастет.
- Реализовать два HTTP-эндпоинта:
- Создание сообщения
- Получение списка сообщений
- При создании сообщения нужно также создавать запись в логах.
- Реализовать универсальную функцию
withTransaction, которая:
- При первом вызове — запускает новую БД транзакцию (выполняет переданную функцию fn внутри неё).
- При повторном вызове в рамках уже начатой транзакции — переиспользует её.
- Передает транзакцию автоматически между асинхронными вызовами.
- Покрыта тестами.
- Продумать возможные расширения API, которые сделают его удобным и масштабируемым в большом TypeScript-проекте.
- Внутри проекта основная бизнес-логика реализована через классы.
- Методы классов вызывают друг друга, и не передают транзакцию явно.
- Разработчики хотят оборачивать методы в транзакции "автоматически", без потери читаемости или предсказуемости поведения.
- Если хватает времени, реализуйте придуманные расширения.
Пример возможного использования (упрощенный, API проектируете вы, важно предоставить хороший DX):
class MessageService {
constructor(private logService: LogService) {}
async createMessage(content: string) {
return withTransaction(async (trx) => {
await trx.insertInto('messages').values({ content }).execute()
// вызываем LogService, он не знает о текущей транзакции напрямую
await this.logService.log('Создано сообщение')
})
}
}
class LogService {
async log(message: string) {
return withTransaction(async (trx) => {
await trx.insertInto('logs').values({ message }).execute()
})
}
}- TypeScript
- Kysely
- Любой HTTP-сервер (Express, Fastify и др.)
- Любая БД (SQLite, Postgres и т.п.)
- Любая другая на ваш выбор.
- Сталкивались ли вы с подобным раньше? Напоминает ли что-то такой подход из других тех. стэков?
- Как улучшить текущую реализацию проекта?
- Ссылка на репозиторий или архив с решением
- Скринкаст с демонстрацией работы и объяснением решения (обязательно со звуком, проверьте доступ к видео перед отправкой)
- В репозитории должны быть только файлы, относящиеся к решению (удалите лишние файлы и мусор)