Сети: как моделировать их, используя совокупные корни? - PullRequest
0 голосов
/ 25 ноября 2011

Модель предметной области определяет a.o. отношения между сущностями и мы определяем совокупные корни, чтобы обеспечить инкапсуляцию и границы транзакций. Хорошо известными отношениями являются отношения «один к одному» (объект или объект значения содержится в совокупном корне), отношения «один ко многим» (совокупный корень содержит коллекцию дочерних объектов) и отношения «многие ко многим». Последние являются сложными, потому что отношения «многие ко многим» между агрегатными корнями создают проблемы с границей транзакции. Таким образом, во многих случаях одно направление отношения «многие ко многим» рассматривается как более важное, и только это отношение моделируется как отношение «один ко многим».
Теперь сделайте еще один шаг вперед. Сети. Отношения многие ко многим между эквивалентными партнерами. Как вы можете смоделировать это, не нарушая границы транзакции в ваших совокупных корнях?
Посмотрите на этот широко применимый пример:
У меня есть сеть с узлами. Каждый узел имеет ограниченное количество портов. Один порт может быть подключен только к одному порту на другом узле. Я должен иметь возможность добавлять и удалять соединения между узлами, используя порты.
Интуитивным подходом к этому было бы моделирование узлов как совокупных корней, содержащих порты. Соединения кажутся объектами значений, и один порт может иметь одно соединение. Я мог бы реализовать метод Node.ConnectTo (nodeId, portId), который добавляет соединение (между портом X на узле A и портом Y на узле B) к совокупному корню, узлу A. Предпочтительно, я бы вызывал этот метод дважды, один раз на Узел A и один раз на узле B и заверните его в транзакцию. Однако это нарушило бы границу транзакции, поэтому я решил сохранить ее только на узле A.
Чтобы увидеть соединение на узле B на клиенте приложения, потребуется отдельная модель чтения. Но это не проблема, архитектура CQRS предоставляет нам эти возможности. Таким образом, добавление, удаление и просмотр подключений не является проблемой.
Проблема возникает, когда я хочу проверить, свободен ли порт, прежде чем добавить соединение к порту. Результатом соблюдения нашей границы транзакции является то, что (в модели записи) тот факт, что порт уже подключен, может быть неизвестен объединенному корню, но может храниться в любом другом объединенном корне.
Конечно, вы можете доверять проверке вашего клиента, продолжить и добавить соединение, если это нормально для узла, к которому вы добавляете его, и полагаться на процесс, выполняющий проверки согласованности для выполнения компенсирующих действий для недействительных соединений. Но это, кажется, имеет большое значение по сравнению с переносом транзакции вокруг двух вызовов ConnectTo ...
Это заставило меня подумать, что возможно, мои совокупные корни были выбраны неправильно . И я начал думать о Узлах и Сетях как об объединенных корнях, где Сеть - это коллекция Соединений. Хорошая вещь о сетевом агрегате состоит в том, что вы всегда можете проверить добавление или удаление соединений. За исключением случаев, когда новое соединение приведет к объединению двух существующих сетей ... И ваша совокупность может стать большой, возможно, в результате только в одной огромной сети. Также невозможно.
Итак, как вы думаете, это должно быть смоделировано? Видите ли вы решение, в котором корни агрегатов считаются границами транзакций, вы можете проверить сеть и не рискуете сохранить всю сеть как один агрегат? Или я запрашиваю все 3 CAP здесь и это просто невозможно?

Ответы [ 2 ]

0 голосов
/ 01 июня 2012

Я думаю, что ваш «новый путь» несовершенен, поскольку модель View не должна создавать исключение, которое «каким-то образом» распространяется обратно на модель предметной области. Модель предметной области должна решить эту проблему сама.

Итак, в этом случае (привязка 1-к-1) вы можете использовать события в рамках модели предметной области, чтобы

  1. NodeA.connect ("port1") .to (NodeB) .on ("port3");

  2. Узел A резервирует "порт1" для себя.

  3. NodeA отправляет «portConnectionRequest» на NodeB.

  4. Узел B связывает «port3», если доступно.

  5. NodeB отправляет "portConnectionConfirmed" или "portConnectionDenied".

  6. NodeA получает событие и действует соответственно.

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

0 голосов
/ 26 ноября 2011

Хорошо, я прочитал и подумал еще немного об этом, и я думаю, что это 'правильный' способ сделать это:

  1. Перед выполнением метода ConnectTo на узле Aвы проверяете, свободен ли порт на узле B, используя в конечном итоге непротиворечивую модель представления в качестве источника данных (а не модель домена, которая не может эффективно это проверить, см. выше).
  2. ConnectTo запускается только на узле A, таким образом, граница транзакции не нарушается.
  3. Если модель представления не может подключить порт на узле B, поскольку он уже используется, произошло исключение истинного параллелизма, и об этом должно быть сообщено.Необходимо предпринять некоторые действия (либо вмешательство Мануэля, либо автоматизированный процесс должен его забрать).Вероятность этого исключения параллелизма обычно будет очень низкой.
...