Должна ли бизнес-логика находиться в базе данных или приложении

Для одного из моих предыдущих проектов я создал серверное приложение. Первоначально мой коллега написал большую часть бизнес-логики в одном SQL-запросе. Мало того, что это было трудно интерпретировать, поскольку это не соответствовало принципу единой ответственности, но когда мне нужно было внести изменения в запрос, было очень сложно проверить, работает ли он так, как ожидалось. Мне нужно было либо изменить конфигурацию, чтобы указать на базу данных, работающую в среде разработки, либо на образ Docker, работающий на моей локальной машине. Кроме того, мне нужно было убедиться, что нужные данные доступны в базе данных.

Я решил перенести логику в код приложения и сделать запросы максимально простыми. Это позволило мне использовать шаблон репозитория для написания модульных тестов для проверки логики без экземпляра базы данных.

Всякий раз, когда оформлялся новый заказ, мне нужно было записывать данные в две таблицы. Первоначально я продолжал использовать триггер базы данных для обновления обеих таблиц в одной транзакции. Однако я не мог легко проверить правильность логики вставки. Поняв, что я могу использовать объект сеанса для сохранения данных в обеих таблицах в одной транзакции, я также перенес логику на прикладной уровень.

Перенесемся на несколько недель вперед, я все еще использовал задание cron, запланированное с использованием pg_cron для истечения срока действия заказов. Когда срок действия заказа истек, я обновлял доступность в других таблицах с помощью SQL. После запуска в производство я заметил, что общая доступность не соответствует заказам. Я не нашел простого способа определить, было ли это связано с ошибкой в ​​задании cron. В результате я перенес эту логику и на прикладной уровень. Если вы похожи на меня, вы не знали, что AWS Lambda поддерживает запланированные триггеры.

Вопросы производительности

И последнее замечание… В нашем приложении была сложная логика фильтрации.
Если бы я использовал библиотеку ORM для фильтрации, то это было бы сделано на стороне базы данных. Это гарантирует, что по сети передаются только те данные, которые нам нужны; ускорение работы приложения и снижение затрат.

def find_all(self, **kwargs): 
 return self._session.query(Availability).filter_by(**kwargs).all()

Однако я хотел написать модульные тесты для логики фильтрации. Поэтому я решил прочитать все данные и отфильтровать их на прикладном уровне.

Если кто-нибудь знает способ обойти это, я был бы очень признателен, если бы вы могли оставить комментарий.

Хотя это противоречит передовой практике, я подумал, что, возможно, базы данных OLTP могут находиться на том же сервере, что и приложение. Таким образом, данные все равно будут в памяти, и им не нужно будет передаваться по сети.

Заворачивать

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