Доменная модель - Репозитории - Связь через подсистемы - PullRequest
3 голосов
/ 07 апреля 2011

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

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.Однако я понятия не имею, как это будет работать, поскольку к объекту «Клиент» нужно будет обращаться из другого хранилища данных.

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

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

1 Ответ

3 голосов
/ 07 апреля 2011

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

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

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

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

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

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

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

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

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


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

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

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

...