Обычная практика как по мне так это есть сущности.
Для этой сущности есть слой dao(репозитории, сторадж не важно), где идёт непосредственно работа с базой (crud), само собой через интерфейс.
Также есть слой сервиса, где идут различного рода проверки на null, какая то ещё логика и вызовы методов из dao. Само собой интерфейс для сервиса
Различные части логики взаимодействуют между собой через вот такие вот сервисы. Т.е. хочешь получить Book по id, делаешь вызов bookService.findById(id); а логика внутри уже этого сервиса делает различного рода проверки на валидность того же id и дёргает соответствующую таблицу в базе