Разработка модульного сервисного приложения

11

Я смотрю на разработку нового решения, которое по своей природе является очень модульным, и мне хотелось бы создать структуру, поддерживающую этот дизайн, чтобы облегчить расширение в будущем, четкое разделение проблем, лицензирование по модулям и т. Д. В Интернете я обнаружил, что модульные или составные приложения ориентированы на пользовательский интерфейс, ориентированы на Silverlight, WPF и т. д. В моем случае я разрабатываю приложение службы WCF, которое будет использоваться другими разработчиками, работающими над различными проектами пользовательского интерфейса.

Фон

Цель моего проекта - создать централизованный источник бизнес-логики / предметной логики для поддержки нескольких наших бизнес-приложений, которые в настоящее время дублируют процессы, правила и т. Д. И хотя не все преимущества модульности будут достигнуты, я хотел бы воспользоваться возможность создать структуру, которая будет использовать те части, которые мы можем использовать в своих интересах.

Я начал разработку с рассмотрения API, предоставляемого сервисным приложением. Понятно, что сервисы можно разделить по модульным линиям, которые я рассматривал. Например, у меня будут сервисы FinanceService, InventoryService, PersonnelService и т. Д., Которые группируют мои сервисные операции для обеспечения высокой согласованности в API, сохраняя низкую связь, поэтому клиенты должны использовать только те услуги, которые имеют отношение к их приложению.

Для меня имеет смысл иметь отдельные модули для каждой из этих служб, такие как MyApp.Finance, MyApp.Inventory, My.Personnel и так далее. Сквозные проблемы и общие типы будут в общей сборке MyApp. Отсюда я немного связан.

(О, я должен упомянуть, что я буду использовать IoC-контейнер для Dependency Injection, чтобы оставить приложение свободно связанным. Я не буду упоминать какой, потому что я не хочу открывать ящик Пандоры!)

В MyApp.ServiceHost я создам файл хоста службы (.svc), соответствующий каждому модулю, например FinanceService.svc. Хосту службы необходимо имя службы, которое соответствует информации в файле конфигурации, который содержит интерфейс, определяющий контракт на обслуживание. Затем конфигурация IoC используется для сопоставления конкретной реализации используемого интерфейса.

1. Должен ли сервисный уровень реализовывать API и делегировать модулям, или модули должны быть автономными (в том смысле, что они содержат все, что связано с этим модулем, включая реализацию сервиса)?

Одним из способов решения этой проблемы является наличие «модуля» MyApp.Services, который содержит реализацию контрактов на обслуживание. Каждый класс обслуживания просто делегирует другому классу соответствующий модуль, который содержит логику домена для операции. Например, WCF-класс FinanceService в MyApp.Services делегирует другой интерфейс, который реализован в модуле Finance для выполнения операции. Это позволило бы мне поддерживать тонкий фасад службы и «подключать» реализацию к реализации службы, а также избавить модули от необходимости беспокоиться о WCF, например.

С другой стороны, может быть, предпочтительнее иметь каждый модуль в отдельности, так как он имеет интерфейс и реализацию. Хост обслуживания относится к интерфейсу контракта на обслуживание, найденному в модуле, и IoC также настроен на использование соответствующей реализации из модуля. Это означает, что добавление нового модуля может быть выполнено без каких-либо изменений на уровне службы, кроме добавления нового файла .svc и информации о конфигурации IoC.

Я думаю о влиянии, если я переключусь со стандартного WCF на интерфейс службы RESTful или, возможно, перейду к службам RIA или что-то в этом роде. Если каждый модуль содержит реализацию контракта на обслуживание, тогда мне придется вносить изменения в каждый модуль, если я меняю технологию или подход к обслуживанию. Но если фасад является собственным модулем, мне нужно только поменять эту часть, чтобы внести изменения. Модули должны были бы реализовать другой набор контрактов (интерфейсов), возможно, определенных в общей сборке ???

2. Каков наилучший способ управления совместным использованием ресурсов между модулями и / или зависимостями между модулями?

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

С одной стороны, я ожидал бы использовать некоторый тип доменных событий / сообщений для сообщения об операции. Модуль Inventory вызывает GoodsReceivedEvent, который обрабатывается финансовым модулем, чтобы сгенерировать квитанцию ​​и инициировать процесс оплаты. Однако это означает, что Финансовому модулю необходимо знать об элементах инвентаря, которые были получены. Я могу просто ссылаться на них по идентификатору, но что, если мне потребуется дополнительная информация для квитанции, такая как имя и / или описание, стоимость единицы и т. Д.? Имеет ли смысл для каждого модуля иметь собственную версию предмета инвентаря, разработанную для удовлетворения потребностей этого модуля? В этом случае Финансовый модуль должен будет выполнить собственный поиск элементов инвентаря при обработке GoodsReceivedEvent.

...

Я использовал этот пример специально, потому что много работал с различными ERP-системами и знаю, что они разработаны с таким модульным типом - я просто не знаю как. Я также не упоминаю об этом выше, но я предпочитаю следовать принципам Domain Driven Design при разработке решения и считаю, что этот тип модульности вписывается в эту область.

Любая помощь, чтобы моя голова обернулась вокруг этого очень ценится.

SonOfPirate
источник
1
Сожалею. Не могу найти вопрос во всем мышлении. Что за вопрос?
С.Лотт
1
Ищите вопросительные знаки. Есть несколько моментов, в которых я предлагаю варианты, но не предполагаю решения, и несколько конкретных вопросов, которые помогут направить процесс по правильному пути.
SonOfPirate
1
@SonOfPirate: Извините. Я надеялся, что вы найдете время, чтобы выделить или подчеркнуть вопросы, чтобы другие люди могли помочь вам.
С.Лотт
4
Я согласен с С. Лоттом. Извините, но это не вопрос, это поток сознания. Мы можем помочь вам намного лучше, если вы сведете это к одному или двум сфокусированным вопросам и попытаетесь отделить архитектурные аспекты от деталей реализации.
Аарона
1
@SonOfPirate: «Архитектура» - это структура и передача этой структуры другим разработчикам. Это начинается с описания. Он должен достигать уровня ясности и прозрачности, который позволяет другим разработчикам полностью «получить» его и следовать принципам архитектурного проектирования во всем, что они делают.
С.Лотт

Ответы:

2

Имеет ли смысл для каждого модуля иметь собственную версию предмета инвентаря, разработанную для удовлетворения потребностей этого модуля? В этом случае Финансовый модуль должен будет выполнить собственный поиск элементов инвентаря при обработке GoodsReceivedEvent. Где провести черту между модульностью и необходимостью обмена информацией?

Вы всегда должны будете принять компромиссы. Это все, что можно сказать на ваши вопросы на самом деле. Хорошей практикой было хранить объекты в отдельной сборке, поэтому многие приложения хранят их в общей библиотеке. Но это означает, что нет (физических) бизнес-логических границ. Выполнение ваших собственных поисков означает много избыточного кода, который я реализовал бы, только если у вас есть особые требования к обоим модулям. Конечно, если смотреть на это с архитектурной точки зрения, ваш финансовый модуль и ваш модуль инвентаризации все равно должны иметь общую зависимость. Финансовый модуль, скорее всего, зависит от инвентарного модуля. Если требования позволяют вам позволить одному модулю зависеть от другого модуля, то я бы сказал, что можно инкапсулировать сущности в модулях, к которым они принадлежат больше всего. Вы' Вам нужно будет найти хороший баланс между физическими различиями (общими зависимостями) и обслуживанием. Слишком много общих зависимостей может вызвать кошмар обслуживания версий. Кроме того, с ORM будут проблемы, если вы хотите отобразить новые отношения на существующие сущности или расширить их из модулей, которые зависят от модуля, содержащего сущность.

Если вы не используете ORM, имейте в виду, что все ваши модули в любом случае, вероятно, совместно используют одну и ту же базу данных, как это часто бывает. Вы можете использовать это в качестве точки соприкосновения.

Вы также можете хранить данные в чистом виде (например, с точки зрения CSV / результирующие наборы) и использовать центральную службу обмена сообщениями, чтобы уведомлять модули, которые зарегистрировались в ней, о новых данных вместе с некоторой метаинформацией. Это означает отсутствие реальных зависимостей, но жертвует типом безопасности и контрактами, и могут возникнуть проблемы с искаженными данными. Я полагаю, именно столько крупных ERP-систем делают это.

Короче говоря, вам придется решить, какой интерфейс вы хотите. Большая модульность ведет к меньшему количеству контрактов и наоборот.

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

сокол
источник
Также согласен с этим - обмен сообщениями + pub / sub и общие библиотеки для контрактов dto (внутреннее репозиторий Nuget). Я задавал те же вопросы, что и @SonOfPirate, до тех пор, пока эта статья не представила их мне в перспективе: msdn.microsoft.com/en-us/library/bb245678.aspx
diegohb