Представь: ты пишешь микросервис на Ktor, всё летает на корутинах, код минималистичный и всё прекрасно... До того момента, когда ты касаешься базы данных. Все мечты разбиваются об Spring Data и Koin, либо об тонны бойлерплейта на Exposed или Ktorm DSL. Знакомо?
У меня есть решение: KRepo — это лёгкий, полностью suspend-ориентированный слой репозиториев, который даёт вам магию Spring Data (интерфейсы, findBy…, CRUD из коробки), но без DI-контейнеров, без рефлексии в рантайме и тонн мусора.
interface UserRepository : Repository<User, Long> { suspend fun findByEmail(email: String): User? suspend fun findByAgeGreaterThan(age: Int): List<User> }
— и всё. Никаких имплементаций или инъекций. Под капотом — динамический прокси, кэширование метаданных и независимость от фреймворков: плагины под Exposed, JDBC или MongoDB реализуются отдельно.
Как и какие пробелы закрывает моё детище
Blocking I/O и отсутствие suspend-поддержки из коробки:
Exposed/JDBC — синхронные по умолчанию, нужно вручную оборачивать в
withContext(Dispatchers.IO),что приводит к контекст-свитчингу и снижению производительности в Ktor. Spring Data JPA — чисто blocking.У KRepo 100% приостанавливаемая реализация CRUD и кастомные методы через динамические прокси. Все операции —
suspend fun, интегрируются напрямую в корутины Ktor без обёрток.Это идеально подходит для async-роутов Ktor: нет "пробок", выше пропускная способность под нагрузкой. Закрывает потребность в reactive Spring (R2DBC) и/или бойлерплейта.
Тяжёлый DI и автоконфигурация:
Spring Data требует Spring Boot/Koin для инъекции репозиториев — overhead для легковесного Ktor. В Exposed — ручная регистрация в DI, без "магии".
В KRepo RepositoryContext — простой registry. Нет Spring/Koin. Репозитории генерируются на лету через фабрики (
RepositoryFactory).Легче тестировать (мокаешь DataAccessor) и деплойить (меньше зависимостей).
Boilerplate-код для репозиториев:
В Exposed/Ktorm — пишешь SQL/DSL вручную для каждого метода. Нет динамического парсинга имён методов (типа
findByName).Proxy-based генерация без ручной имплементации. Определяешь интерфейс, прокси сам реализует через
DataAccessor.Экономит часы на CRUD: пишешь интерфейс — и готово.
Жёсткая привязка к одному ORM/БД:
Абстрактный интерфейс для любого бэкенда (Exposed, MongoDB, JDBC, in-memory). Модули реализации для Exposed, чистого SQL, MongoDB можно создавать самому.
Runtime-overhead из-за рефлексии:
Метаданные (методы, entity-структура) собираются один раз на compile-time/reflection-at-startup и кэшируются.
Отсутствие "Spring-like" удобства в Ktor:
Spring Data-подобный API, но Ktor-native. Интерфейсы с suspend-методами, автоматическая генерация. Интеграция в Ktor: просто регистрируешь в контексте и используешь в роутах.
Сложности с кастомными запросами и пагинацией:
Поддержка кастомных suspend-методов через прокси.
findByAgeGreaterThan→ автоматический запрос. Легче масштабировать на сложные запросы без потери асинхронности.
Состояние проекта и ссылки
На данный момент полностью реализован core-модуль со всей необходимой абстракцией (методы, парсеры, прокси, аннотации и т.д). Осталась только реализация паттерна для конкретных БД и драйверов: SQL-диалекты, Mongo и in-memory.
Структура сейчас:
./core/src/main/kotlin ├── exception │ ├── QueryParseException.kt │ └── RepositoryException.kt ├── query │ ├── LogicalOperator.kt │ ├── MethodNameParser.kt │ ├── Operator.kt │ ├── ParsedMethod.kt │ ├── QueryAction.kt │ ├── QueryBuilder.kt │ └── QueryCondition.kt ├── repository │ ├── access │ │ └── DataAccessor.kt │ ├── annotations │ │ ├── Column.kt │ │ ├── Entity.kt │ │ ├── Id.kt │ │ └── Transient.kt │ ├── CrudRepositoryDelegate.kt │ ├── CrudRepository.kt │ ├── DefaultRepositoryFactory.kt │ ├── KtorRepository.kt │ ├── metadata │ │ ├── ColumnProperty.kt │ │ ├── EntityColumn.kt │ │ ├── EntityMetadata.kt │ │ └── PropertyExtensions.kt │ ├── RepositoryConfig.kt │ ├── RepositoryContext.kt │ ├── RepositoryFactory.kt │ ├── RepositoryInvocationHandler.kt │ ├── RepositoryMetadata.kt │ └── RepositoryMethod.kt └── utils └── ReflectionUtils.kt
Ссылки и контакты:
На этом всё, ждите новостей!
