Распределенный DDD в .NET: общий доступ к доменным объектам с клиентом - PullRequest
4 голосов
/ 02 мая 2011

Я разрабатываю трехуровневое приложение (не трехуровневое!) С клиентским приложением, работающим на одном уровне (физический кластер), которое взаимодействует с приложением-службой, работающим на другом уровне, и сервером базы данных на еще одном уровне. В приложении много бизнес-правил, логики процессов и т. Д., Которые, по моему мнению, должны быть доступны как на уровне приложений, так и на уровне служб, чтобы улучшить взаимодействие с пользователем, сократить количество обращений в службу, а также устранить избыточное кодирование.

Давайте использовать этот пример: в моем доменном слое у меня есть объект Document. Этот объект содержит свойство AllowPublish, которое проверяет внутреннее состояние объекта и возвращает истину / ложь, если состояние позволяет опубликовать документ. Объект также имеет метод Publish, который изменяет внутреннее состояние объекта, отражая тот факт, что он публикуется, устанавливая для флага IsPublished значение true и вызывая событие опубликованного домена.

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

В моем сервисном приложении мой DocumentService имеет метод PublishDocument, который принимает идентификатор документа, извлекает документ из хранилища с использованием идентификатора, проверяет свойство AllowPublish и, если true, вызывает Publish, а затем сохраняет обновленный объект с использованием хранилища.

У меня немного другое поведение на клиенте. В этом случае я использую свойство AllowPublish для включения / выключения командных кнопок. При включении и нажатии я вызываю сервисный агент, который предоставляет метод PublishDocument, принимающий идентификатор документа. Агент передает вызов в одноименный метод DocumentService приложения-службы.

Чтобы исключить дублирование кода, поделиться бизнес-логикой, правилами проверки и т. Д. Я поместил объекты домена в отдельную сборку, совместно используемую как клиентским приложением, так и приложением-службой. Это означает, что клиентское приложение теперь имеет доступ к методу Publish моего класса Document, даже если оно является релевантным и должно использоваться только моим приложением-службой. Это заставляет меня пересмотреть весь подход, который я использую.

Хотя я понимаю использование DTO для передачи состояния между клиентом и сервером, я использую .NET 3.5 и, насколько мне известно, совместное использование сборки - это единственный способ поделиться бизнес-правилами и правилами проверки с клиентом. приложение. У меня есть некоторые идеи о том, в каких других направлениях я могу идти, но я надеялся получить некоторые предложения, прежде чем идти по новому пути.

С другой стороны, моя текущая реализация для клиента использует то, что я считаю цикличным подходом к авторизации, который может быть просто индикатором того, что другая модель будет лучше. Так же, как у меня есть AuthorizationService в моем приложении службы на стороне сервера, которое DocumentService использует для выполнения авторизации, у меня есть аналогичный агент, который использует мой клиентский код. Это означает, что мне нужен еще один уровень косвенности в моем клиентском коде для поддержки авторизации, возможно, Controller или ViewModel. Что хорошо, если вариант использования действителен.

EDIT

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

Ответы [ 3 ]

5 голосов
/ 12 мая 2011

Ради закрытия и всех, кто встретит этот пост в будущем, я подумал, что поделюсь тем, чем я закончил, отдавая должное Юлиану за то, что он помог направить меня в правильном направлении.

Проще говоря, я понял (с помощью Юлиана), что у меня действительно было два разных варианта использования между клиентским приложением и приложением службы на стороне сервера.В результате я прикусил губу и создал отдельные доменные модели для каждой.

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

К сожалению, это приводит к компромиссу.Тот, который я надеюсь минимизировать, поскольку мы продвигаем технологию для использования служб RIA, возможно, которая позволит нам передавать аннотации данных с сервера на клиент.Однако сейчас я использую простые объекты DTO для передачи информации о состоянии между приложениями и интерфейсом службы RESTful, который предоставляет API-интерфейс в логику основного домена.

Используя приведенный мной пример, у меня есть следующая настройка:

Когда пользователь нажимает кнопку «Опубликовать» в пользовательском интерфейсе, клиентское приложение вызывает метод Publish для моего класса агента службы.Сервисный агент осуществляет связь с серверной службой.В этом случае DocumentService предоставляет метод Publish, который принимает идентификатор документа для публикации (а также информацию о пользователе и т. Д.)

DocumentService извлекает объект домена Document из DocumentRepository и вызывает метод Publishна объекте, который обновляет внутреннее состояние документа.Затем служба вызывает метод Update в DocumentRepository, передавая обновленный объект Document, и изменения сохраняются в базе данных.

Компромисс заключается в том, что мне нужно иметь логику / правила, определяющие,и когда Документ может быть опубликован как на клиенте, так и на сервере (потому что мы не можем предполагать, что запрос всегда действителен).Опять же, рассмотрение решения как двух отдельных приложений с их собственным набором вариантов использования помогло сделать это более разумным (по моему мнению).Как видите, мне не нужен метод Publish в клиентской версии Document, но мне нужно отслеживание изменений для расширенного пользовательского опыта.Мне не нужен такой же тип отслеживания изменений на сервере, потому что нет обновления пользовательского интерфейса при изменении объекта.В последнем случае ORM реализует отслеживание изменений, чтобы сделать сохранение более оптимальным.

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

НТН

3 голосов
/ 03 мая 2011

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

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

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

Я приветствую главную идею: не объединяйте понятия ради СУХОГО.Как обычно Udi Dahan как статья об этом: Ошибка ReUse

0 голосов
/ 02 мая 2011

Рассмотрите возможность использования атрибута InternalsVisibleTo .

...