Модель предметной области - Репозитории - Обмен данными между подсистемами

В настоящее время я занимаюсь проектированием системы, которая будет использовать несколько источников данных для получения необходимых данных. Я пытаюсь смоделировать концепции, показанные ниже (разместил бы изображение, но не набрал бы достаточно очков!), Где покупатель может иметь связь с рядом продуктов. Клиент будет храниться в «Подсистеме клиента», а Продукт и Продукт клиента будут храниться в вашей «Подсистеме продукта».

public class Customer
{
    public string FirstName { get; set; }

    public Guid ID { get; set; }
}
public class CustomerProduct
{
    public Guid ID { get; set; }

    public Customer Customer { get; set; }

    public Product Product { get; set; }
}
public class Product
{
    public string Name { get; set; }

    public double Price { get; set; }

    public Guid ID { get; set; }
}

Сущность «Клиент» будет физически сохранена в системе, доступ к которой должен осуществляться через веб-службу. Сущности ConsumerProduct и Product будут сохраняться в базе данных SQL с использованием NHibernate в качестве ORM.

В рамках разработки я планировал использовать репозитории, чтобы абстрагировать технологии сохранения данных от модели предметной области. Поэтому у меня было бы 3 интерфейса репозитория: ICustomerRepository, ICustomerProductRepository и IProductRepository. Затем я бы создал конкретную реализацию NHibernate для репозиториев CustomerProduct и Product и конкретную реализацию доступа к веб-сервисам для репозитория Customer.

Я борюсь с тем, как будут взаимодействовать сущности, которые хранятся в разных подсистемах. В идеале я хотел бы иметь расширенную модель предметной области, в которой объект CustomerProduct имел бы физическое свойство «Customer», которое возвращает объект Customer. Однако я понятия не имею, как это будет работать, поскольку к объекту Customer нужно будет получить доступ из другого хранилища данных.

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

Буду признателен за любые предложения по решению этой проблемы.


person watsite    schedule 06.04.2011    source источник


Ответы (1)


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

Во-первых, вы можете решить эту проблему разными способами, а также спросить себя о нефункциональных требованиях, таких как обслуживание, время безотказной работы и поддержка. Будут ли обе системы всегда работать одновременно, или случится так, что вы отключите систему. Подсказка, которую я ищу, - это синхронизация или асинхронность (организация очереди сообщений?) С подсистемами. Этого можно добиться с помощью NServiceBus.

Но чтобы сосредоточиться на своем Домене, вы должны сделать так, чтобы Домен выглядел так, как будто у него есть только одна модель. Это можно сделать разными способами:

1) Пусть ваш ICustomerRepository (интерфейсный контракт, который действует как работа с коллекцией объектов) будет реализован репозиторием, связанным с инфраструктурой, который использует вашу веб-службу в вашей подсистеме. Подсказка состоит в том, что вы должны использовать GUID в качестве ключей, чтобы возникали ключевые конфигурации. Такой подход не позволит вам иметь какие-либо отношения / ассоциации с клиентом из других ваших организаций. Они будут, но только через репозиторий (это решение, которое Джимми Нильссон использует в своей книге (http://jimmynilsson.com/blog/), чтобы не затягивать модель множеством двунаправленных отношений).

2) Зависит от того, как ваши варианты использования будут нацелены / использовать модель, но вы можете создать уровень обслуживания для всего приложения, который находится в одном физическом месте, но использует CustomerService, CustomerProcuctService и ProductService. Чтобы предотвратить утечку логики домена на уровень приложения, некоторая координация между этими объектами может быть инкапсулирована в обработчики событий домена, которые координируют некоторые события между различными службами.

3) вы также можете создать класс CustomerAdapter, который имеет в качестве ключа другие подсистемы CustomerGUID (он не может генерировать ключи, поскольку веб-служба клиента контролирует это). Но вы можете отобразить его в Nhibernate и установить связь между CustomerProduct и CustomerAdapter. Но когда вы отображаете CustomerAdapter, вы загружаете только GUID. Затем убедитесь, что у вас есть ICustomerAdapterService, введенный в свойство с помощью Spring.Net Windsor или какого-либо другого инструмента DI. Тогда вы не должны отображать свойства (например, имя клиента, адрес и т. Д.) Для customerAdapter в Nhibernate. Но когда вы получаете / читаете Adress от CustomerAdapter, он получает его от ICustomerAdapterService и также устанавливает все другие значения. Это не рекомендуемое решение, так как оно нарушит некоторые правила DDD, например отсутствие служб в модели предметной области. Но если вы посмотрите на это с этой точки зрения: на самом деле это можно считать доменной службой, поскольку она решает проблемы в вашем распределенном домене. Однако он включает в себя вещи, связанные с инфраструктурой, такие как реализация службы WCF, и, следовательно, реализация службы должна быть на другом уровне / сборке инфраструктуры.

Наиболее простым является решение 2 Если вы можете справиться с тем фактом, что доступ к объекту клиента будет осуществляться только службой на уровне приложения. Однако этот служебный уровень приложения может быть хорошим антикоррупционным слоем между двумя подсистемами. Вероятно, есть причина, по которой сегодня у вас две подсистемы. Но пример потока взаимодействия (без детального знания того, каков ваш домен):

  • GUI вызывает метод Application Service CustomerProductService BuyNewProduct (CustomerDTO customer, ProductDTO newProduct)
  • CustomerProductService имеет ICustomerProductRepository и IProductRepository, введенные в конструктор. Он также будет иметь инфраструктуру Service ICustomerFacadeService (измените имя сейчас :-)), которая вводится в Property CustomerFacadeService. Создание этой службы выполняется фабрикой, у которой есть два метода создания: Create () и CreateExtendendedWithCustomerService (). Последний также будет внедрять CustomerServiceFacade
  • Метод BuyNewProduct (...) теперь соберет CustomerDTO и использует CustomerGUID для загрузки Customer из CustomerFacadeService, который вызовет веб-службу в другой подсистеме.
  • Загруженный клиент гарантирует, что он действительно существует, но теперь мы загружаем продукт с помощью IProductRepository.
  • Со значением CustomerGUID и Product Entity мы создаем новую сущность CustomerProduct (которая на самом деле является просто классом сопоставления между продуктами и GUID клиента) и сохраняем ее через ICustomerProductRepository.
  • Теперь вы можете позвонить в другую инфраструктурную службу, чтобы отправить электронное письмо вашему клиенту, который будет уведомлен о том, что у него есть доступ к новому продукту. Или вы можете создать события домена в сущности CustomerProduct, которая делегирует это уведомление обработчику событий (на уровне службы приложения), который имеет IEmailService, введенный в ctor. Затем вы инкапсулируете знания предметной области об отправке уведомлений, когда вы подключаете нового клиента к продукту.

Надеюсь, это поможет вам смоделировать вашу область с меньшими усилиями. Потому что делать DDD больно. Требуется много обсуждений с коллегами, экспертами в предметной области и самим собой перед зеркалом :) Это правильный путь? Посмотрите на DDDsample.net события домена или найдите Udi Dahan и события домена.


Пишу здесь ответ, побольше места:

Что касается CustomerAdadpter, также называемого CustomerFacadeService в примере потока взаимодействия, вот мое мнение: как реализовать, зависит от вашего приложения. Будет ли большинство пользователей вызывать основную систему, вызывая вашу «облачную подсистему», которая будет иметь хорошее время безотказной работы: - Тогда вам, возможно, не понадобится очередь, и у вас будет служба WCF в облаке. Ваш CustomerFacadeService будет сервисной оболочкой, которая просто предоставляет метод, который нужен вашему уровню приложения, а также собирает все необходимые объекты DTO. Если ваша облачная система также будет обращаться к вашей основной системе, вам необходимо предоставить некоторые из ваших методов как службу. ЗАТЕМ у вас есть возможность предоставить конечную точку NServiceBus как службу WCF. Это дает вам возможность отключить основную систему без потери информации. Но всегда есть много "но" ... Вам, конечно, нужно иметь службу WCF на другом компьютере, если ваши инфра-технические ребята хотят установить исправления / перезагрузить веб-сервер основной системы. Если у вас есть клиент, ожидающий ответа, в то время как основная система не работает, как долго они будут ждать? Думаю, не слишком долго. Итак, один сценарий, который я вижу в этом, заключается в том, что у вас есть пакеты / отчеты, которые необходимо выполнить, и если одна часть системы не работает, отчетность будет продолжена, когда она снова заработает.

Вот несколько примеров NServiceBus, представленного как служба WCF. Но у меня нет опыта в этом, я просто знаю, что «это можно сделать». http://docs.particular.net/nservicebus/architecture/nservicebus-and-wcf

person Magnus Backeus    schedule 07.04.2011
comment
Привет, Бачче, большое спасибо за ответ. Сущность Customer будет жить в облаке, время безотказной работы которого должно составлять 99,99%, но я понимаю, что могут быть проблемы с сетью и т. Д., Поэтому в идеале я хотел бы спроектировать с учетом устойчивости. Я начал изучать NServiceBus, но изо всех сил пытаюсь понять, как такая технология будет использоваться в этом подходе (т.е. будет ли CustomerAdapter в вашем примере делать запрос через служебную шину, как это будет работать, если сайт не работает, запрос будет поставлен в очередь, а затем адаптер уведомит, когда сообщение прошло, чтобы заполнить клиента?) - person watsite; 07.04.2011
comment
Также в отношении пункта 2 я действительно хочу иметь уровень обслуживания всего приложения, который будет использовать модель / репозитории предметной области. Когда вы говорите инкапсулировать логику в обработчиках событий домена, не могли бы вы указать мне на какие-либо образцы, поскольку я не очень понимаю концепцию? - person watsite; 07.04.2011
comment
Перейдите на сайт dddsamplenet.codeplex.com, где группа преданных своему делу людей разработала пример DDD из книги Эрика Эванса domaindrivendesign.org/books/evans_2003. В проекте Vanilla вы найдете как примеры того, как выполнять события домена, так и обработчики событий. Также вы найдете Async EventHandlers, который использует NServiceBus. Это довольно просто реализовать и ОЧЕНЬ полезно. Вот класс события домена dddsamplenet.codeplex.com/SourceControl/changeset/view/. - person Magnus Backeus; 08.04.2011
comment
... комментарий продолжается здесь: Пример использования события домена: dddsamplenet.codeplex.com / SourceControl / changeset / view / Посмотрите на метод AssignToRoute. А затем вы обрабатываете его на уровне приложения с помощью EventHandler dddsamplenet.codeplex.com/ SourceControl / changeset / view / Обратите внимание, что обработчик событий не обрабатывает событие в методе AssignToRoute (что вы, вероятно, заметили сами) - person Magnus Backeus; 08.04.2011
comment
Редактирую свой первый ответ. Посмотрите выше свой ответ на свой первый комментарий. - person Magnus Backeus; 08.04.2011
comment
Я думаю, что наконец-то разобрался с адаптерами и фасадами, но мне было интересно, как это будет использоваться в транзакционной манере? Например, если вызову службы приложения BuyNewProduct () необходимо было добавить нескольких клиентов, использующих фасад, как я мог бы откатить процесс, если один из этих вызовов (или вызов службы продукта) завершился неудачно? - person watsite; 31.05.2011
comment
т.е. BuyNewProduct () - ›CustomerServiceFacade.AddCustomer (customer1) -› ProductService.GetProduct (productId) ‹fails›. - person watsite; 31.05.2011
comment
Я знаю, что вы упомянули, что Заказчик должен жить в облаке, платформе Azure? Я не знаю, как это включить в транзакцию. Но я знаю, что NServiceBus может участвовать в распределенной транзакции, и вы, вероятно, можете исправить это вместе с облачной платформой (лазурным?). Но создать систему, использующую два источника данных, непросто. Возможно, вам придется пойти на компромисс и использовать транзакцию только для сохранения очень важных данных. Но мой совет - взглянуть на NServiceBus и транзакции ... - person Magnus Backeus; 02.06.2011
comment
Спасибо Bacce, обязательно загляну в NServiceBus с транзакциями. Облачная платформа действительно будет Azure и будет действовать как CRM, храня данные сторон. Идея заключалась в том, чтобы хранить партийные данные в CRM, а данные о продуктах - в локальной базе данных. Чем больше я думаю об архитектуре, тем больше склоняюсь к использованию событий для связи с каждой системой, а не напрямую. - person watsite; 02.06.2011
comment
то есть - CRM-система будет иметь уменьшенную версию продукта, а продуктовая система - уменьшенную версию клиента. Если пользователь регистрирует продукт для покупателя в системе продукта, событие возникает и обрабатывается в системе CRM, чтобы также создать связь в этой системе между покупателем и продуктом. CRM-система будет там, где редактируются все детали партии, а продуктовая система - в которой управляются ассоциации продуктов. Есть мысли по поводу этого подхода? - person watsite; 02.06.2011