Есть ли какая-то хорошая практика по интеграции функциональности из независимых библиотек для последующего использования?
Добрый день!
В реальности суть проблемы намного проще, чем заголовок вопроса. По этой же причине честно искал что-то похожее на SO и тут, но даже не знаю, как это корректно сформулировать.
Есть условный "внутренний корпоративный фреймворк" для разработки приложений на нём. И есть в нём две совершенно отдельных сущности: лицензирование (DRM, защита от копирования, вот это вот всё) и сервисы Windows. Ни одна из библиотек не знает друг о друге, но обе ссылаются на какие-то общие более низкоуровневые библиотеки. Конечное приложение может добавить любую из них, либо обе. Получается что-то вроде плагинов.
Ситуация: при обновлении лицензии через пользовательский интерфейс нужно перезапустить сервис. И это нужно для всех приложений, где используется и лицензирование и сервисы.
Совершенно не хочется делать так, чтобы какая-либо из библиотек начала знать о другой. Понятно, что самый простой способ реализовать этот сценарий - в конечном приложении, куда добавлены они обе. Но очень не охота делать это каждый раз в каждом приложении. Хочется один раз прописать такую зависимость и забыть.
Мне видятся следующие способы решения данной проблемы:
1. Вся функциональность живёт в одной толстой библиотеке, и всё знает обо всём. По сути сейчас такой подход и используется, но очень уж хочется отделить всё это друг от друга.
2. Заводится три библиотеки: Licensing, Service и Licensing.Service. Но это путь к безумию, так как по сути подобную библиотеку придётся заводить на каждую возможную комбинацию используемой функциональности.
3. В условную Core-библиотеку добавляются интерфейсы, описывающие всю возможную функциональность, реализуемую в отдельных библиотеках. Связка делается где-то на этом же уровне через DI, например.
4. Наоборот заводится библиотека, которая ссылается на все-все-все модули и в ней идёт оркестровка взаимных зависимостей между ними.
5. Использовать что-то вроде общей шины данных, по которой будут бегать команды вроде "LicenseChanged", а модуль сервиса может на них среагировать. По сути все снова знают обо всех, но типизация становится строковой.
Читаю Хабр довольно давно, но не припомню, чтобы встречал какие-то размышления именно на этот счёт. Буду благодарен, если кто-то сможет ткнуть носом в статью/книгу. Либо я упускаю что-то очевидное, либо в общем виде оно не решается. :) Вообще язык C#, но думаю, что это не принципиально.
Вы можете использовать общую шину, подключить туда две сущности: Licensing и Service, а между ними поставить сущность, которая знает о событии LicenceChanged и знает, что при этом событии нужно перезапустить сервисы, а так же, как именно это можно сделать (обращается к Service). Если обращаться к Service по той же шине, то в ситуации, когда Service недоступен (не подключен например), ничего происходить и не будет -- то есть, не надо будет явно обрабатывать кейс с отсутствием Service внутри сущности-обвязки.
Плюс такого решения -- обвязка не зависит от кода конкретных реализаций License и Service. Строковую типизацию вы можете и не делать, если шина умеет в разные типы сообщений так или иначе -- в крайнем случае, хотя бы сериализацию для событий какую-то можно придумать. К тому же события все равно несут какую-то информацию помимо названия, так что какая-то структура этих событий не помешает. Но здесь я уже увлекся, ибо никаких таких вводных не было. :)
Я рассуждаю в рамках акторной системы, где такие сценарии мне обдумывать проще, но кажется, что и в C# внутри какого-то другого подхода это реализуемо. :)
Благодарю за ответ! Я уж и не думал, что кто-то его увидит. :)
Честно говоря, с шинами пока явно не сталкивался в проектах, поэтому могу чего-то не понимать. Фраза "между ними поставить сущность, которая знает о событии LicenceChanged" меня смутила. А куда эту сущность добавлять? Где она будет жить? В конечном проекте, который будет использовать эти библиотеки? Так это дублировать в каждом таком проекте придётся. Собственно у меня основной вопрос и заключается в терзаниях на этот счёт.
С типизацией мне тоже этот момент неясен. Чтобы типизация была не строковой, то нужно где-то описать тип этого события. Логично поселить его в библиотеку Licensing, так как это её событие. Но чтобы сущность, знающая о нём и перезапускающая сервис понимала этот тип, нужно добавить ссылку на эту самую библиотеку Licensing к ней. Собственно опять же вопрос: куда, где будет жить эта обвязка?