Граница абстракций
Все плюс минус знакомы с концепцией сервисов, где бизнес-операции вынесены в метод, так чтобы их можно было переиспользовать и описывать независимо от фреймворка (относительно). Кто-то даже знает понятие Service Layer как паттерн. Но вот что происходит внутри сервиса - большой вопрос. Например что делать с повторяющейся логикой, а если строить логику на событиях, а можно ли вызывать сервис из сервиса? Хочу сфокусироваться на последнем пункте.
Если внимательно посмотреть на картинку, то видно, что сервис показан как кольцо. Я знаю что большинству кажется очевидным такая графика. Мол мы просто делаем методы внутри которых работаем с нашими сущностями. Но это не все. В реальности слой поверх слоя ставит больше ограничений. Внутри сервиса нельзя вызывать другие сервисы. Почему?
Потому что внешняя операция приложения начинает использоваться как внутренняя строительная деталь. В итоге в одном месте рядом оказываются вызовы сервисов, моделей, репозиториев, событий и другой внутренней логики - и код начинает работать сразу на нескольких уровнях абстракции (нарушение принципа одного уровня абстракции).
Это приводит к практическим проблемам. Внутренний вызов начинает использоваться уже в немного другом контексте, о котором приходится думать отдельно. Со временем требования меняются: операция, которая изначально задумывалась как законченная бизнес-операция от и до, начинает использоваться как промежуточный шаг внутри другой операции. В этот момент она перестает быть чистой границей приложения и превращается в частично переиспользуемый кусок логики.
Постепенно это меняет сам характер сервисного слоя. В сервисах начинают появляться не только законченные бизнес-операции, но и вспомогательные методы, которые нужны лишь как подшаги для других сервисов. Эти методы начинают жить своей жизнью, их приходится поддерживать, учитывать их контекст использования и ограничения. В результате слой, который должен описывать понятный набор бизнес-операций приложения, постепенно превращается в свалку методов разного уровня абстракции.
Когда вызов допустим: если нижележащий компонент это не еще один application service, а отдельная зависимость с более низким уровнем абстракции: доменный сервис, policy-объект, репозиторий, gateway, publisher событий и так далее. Какой-нибудь сервис отправки писем хороший пример. Я, кстати, по этой причине, вообще бы не называл такие штуки сервисами, иначе происходит жесткая путаница.
А что делать, если логика действительно повторяется? В таких случаях обычно есть два пути. Первый - вынести общий кусок в отдельный компонент более низкого уровня абстракции: доменный сервис, policy-объект или другой вспомогательный объект, который не представляет собой бизнес-операцию, а реализует часть логики. Второй строить композицию через события, когда одна операция завершает свою работу и публикует событие, а другие части системы реагируют на него независимо. Хотя я был бы с этим аккуратнее, тут если доводить до крайности может получиться еще хуже.
p.s. А какой подход используется у вас?
Telegram | YouTube | Сообщество
Post #452
12.3K

- 👍 35
- ❤ 17
- 🤔 9
- 👎 2
- 🔥 2
- 😢 2
- 👨💻 1
- 👀 1