Эталонная архитектура общего домена Devlime / домена Inter-MS

Эталонная архитектура общего домена Devlime / домена Inter-MS

1. Предпосылки

Возникла необходимость установить связь между двумя доменами, чтобы отображать в Prism компании Devlime информацию об AgileProject Scrum (единице выполнения спринта) вместе с Project компании Crewit (управление контрактами и трудозатратами). Кроме того, информацию о трудозатратах Crewit требовалось отображать на экране для установления связи между двумя доменами.

В связи с этим был добавлен новый слой Shared API. Этот слой использовался для определения связей между доменами двух MS, а домен одной MS был спроектирован как общий домен, чтобы его также могла использовать другая MS.

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

2. Критерии принятия проектных решений

Ситуации, в которых требуется сопоставить домены между MS, классифицируются по трем приведенным ниже типам в зависимости от наличия Shared APIи того, возникают ли проблемы с производительностью, причем для каждого типа требуется свой подход к проектированию. Перед началом фактического проектирования сначала определите, какой случай имеет место.

Сценарий

Условия

Подход к проектированию

1

Когда Shared API отсутствует и требуется сопоставить домены двух MS

Ссылающаяся MS напрямую хранит домен исходной MS в форме сущности

2

Когда Shared API существует и существенных проблем с производительностью не возникает

Ссылающаяся MS хранит только исходный идентификатор и при необходимости получает данные по идентификатору через Shared API

3

Когда Shared API существует и возникают проблемы с производительностью (N+1, повторные вызовы и т. д.)

Ссылающаяся MS хранит VO только для отображения и обновляет его посредством пакетной обработки, отложенной обработки или бизнес-логики

3. Принципы проектирования

Проектирование общего домена

Проектирование ссылок на домены между MS

Пример

Сущность общего домена в Shared API

Ссылка на Project Crewit, хранящаяся в AgileProject Scrum

Расположение

Внутри отдельного сервиса Shared API

Внутри домена ссылающегося сервиса (scrum)

Характер

Проекция домена исходной MS

Независимый объект, отдельный от источника, либо идентификатор домена исходной MS

3-1. Принципы проектирования общих доменов

Общий домен — это домен среди доменов исходной MS, который существует с целью предоставления его другим MS. Он следует приведенным ниже принципам.

1. Иметь тот же жизненный цикл, что и источник

Когда данные сохраняются или изменяются в исходной MS, общий домен также обновляется посредством события. Иными словами, он имеет тот же жизненный цикл, что и источник. Для применения изменений в существующей исходной MS устанавливается триггер на хуке DataEvent → ProjectionHandler.

2. Включать только поля, необходимые для совместного использования

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

3. Не использовать терминологию источника без изменений в именах

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

3-2. Принципы ссылок на домены между MS

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

Следующие два принципа применяются ко всем трем сценариям.

1. Рассматривать переданные данные как объект, отдельный от источника

Информация, хранящаяся на стороне, создающей ссылку (сущность или VO), не является дубликатом полей источника; это самостоятельно определенный объект, содержащий только те атрибуты, которые действительно необходимы ссылающемуся домену.

2. Переопределять имена с точки зрения ссылающегося домена

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

3-2-1. Сценарий 1 — Общего API нет: прямое владение сущностью

Когда необходимо сопоставить два домена MS, но общий API ещё недоступен, ссылающийся MS напрямую хранит информацию исходного домена в собственной сущности. Таким образом, роль общего домена внутренне выполняется ссылающимся MS.

1. Как и для общего домена, обеспечьте возможность трассировки с помощью sourceSystem/sourceKey.

2. По возможности получайте исходные события и обновляйте данные соответствующим образом; если это невозможно, организуйте процесс ручного или пакетного обновления.

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

3-2-2. Сценарий 2 — Общий API существует, проблем с производительностью нет: ссылка на ID (по умолчанию)

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

1. Вместо полей VO храните только одно поле, содержащее ключ ссылки (ID) на домен, на который выполняется ссылка.

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

3-2-3. Сценарий 3 — Общий API существует, возникают проблемы с производительностью: VO (исключение)

Если при работе по Сценарию 2 фактически подтверждены проблемы с производительностью, вызванные повторяющимися запросами, ссылающийся MS хранит предназначенный только для отображения VO и заполняет его посредством пакетной повторной синхронизации или отложенных обновлений.

1. Не предпринимайте сами попытки синхронизации

Принудительная синхронизация в реальном времени (немедленное обновление при каждом изменении источника) не применяется.

2. Устраняйте проблемы согласованности с помощью бизнес-логики

Используйте VO только для отображения. В логике, требующей принятия фактических бизнес-решений, не доверяйте значениям VO; вместо этого повторно запрашивайте источник (или общий домен) с использованием ключа ссылки.

3. Сделайте актуальность видимой с помощью syncedAt

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

Метод обновления

Метод

Описание

Характеристики

Пакетная повторная синхронизация

Планировщик периодически повторно запрашивает данные (например, один раз в день), чтобы обновить VO

Минимизирует потребность в дополнительной инфраструктуре; задержки согласованности до длительности пакетного интервала считаются нормальным явлением

Отложенное обновление

Когда scrum обрабатывает AgileProject (например, при создании или изменении), в этот момент повторно запрашивайте данные, если syncedAt устарел

Пакетная инфраструктура не требуется. Однако если к данным некоторое время не обращаются, они могут оставаться устаревшими неограниченно долго.

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

4. Пример проектирования (Сценарий 2)

① Источник (Project в crewit)

public class Project extends StageEntity {
    ...
    private String code;                        // 코드
    private LangStrings langNames;               // 이름
    private LangStrings langDescription;         // 설명
    private LangStrings langCustomers;           // 고객사
    private LangStrings langLocations;           // 위치
    private Member manager;                      // PM
    private ProjectStatus projectStatus;         // 프로젝트상태
    private ProjectPeriod projectPeriod;         // 프로젝트기간
    private String skillSet;                     // 기술세트
    private LangStrings langVendors;              // 협력업체
    private LangStrings langRiskNotes;            // 리스크
    private double budget;                        // 예산, ex, 21억5천만원
    private int totalEffort;                      // 총공수, 230 M/M
    private boolean active;
    transient List<ProjectManMonthPlan> projectManMonthPlans;   // 구성원별
    transient List<ProjectMonthPlan> projectMonthPlans;         // 월별
    transient List<ManOutput> manOutputs;
    transient List<ManRatio> manRatios;
    transient List<ManOutputMonthSum> manOutputMonthSums;
    transient List<ManRatioMonthSum> manRatioMonthSums;
    ...
}

② Общий домен (Staffing Project общего API — проекция Project)

public class StaffingProject extends StageEntity {
    ...
    private String id;                    // PK
    private String sourceSystem;          // 원본 MS 또는 도메인 추적용
    private String sourceKey;             // crewit Project의 Id
    private String code;                  // 코드
    private LangStrings name;             // 이름
    private LangStrings description;      // 설명
    private LangStrings customerNames;    // 고객사
    private ProjectStatus status;         // 프로젝트상태
    private ProjectPeriod projectPeriod;  // 프로젝트기간
    private Member manager;               // 담당자
    private double budget;                // 예산, ex, 21억5천만원
    private int totalEffort;              // 총공수, 230 M/M
    private boolean active;
    private LocalDateTime syncedAt;       // 마지막 동기화 시각
    ...
}
  • Обновление через хук DataEvent/ProjectionHandler и событие при сохранении или изменении Project в crewit (тот же жизненный цикл, что и у источника)

  • Исключите поля, управляемые внутри системы, такие как skillSet/budget/langVendors

  • Название StaffingProject основано на обсуждении, согласно которому основной характеристикой является информация о трудозатратах, а не информация о контракте

  • Управляйте временем последней синхронизации с помощью syncedAt (оно поддерживается для целей отслеживания, поскольку даже общий домен не гарантирует полного отражения в реальном времени)

③ Ссылающийся сервис (AgileProject в scrum)

public class AgileProject extends StageEntity {
    ...
    private String staffingProjectId;   // StaffingProject 참조키. ID만 보유.
    ...
}
  • AgileProject не хранит в своих полях информацию о crewit/общем домене — он ссылается только на один ID.

  • Вызывайте общий API и получайте информацию всякий раз, когда отображается экран.

5. Ссылка (Сценарий 3) — подготовка к проблемам с производительностью

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

public class StaffingProject implements ValueObject {
    ...
    private String staffingProjectId;    // staffing project id
    private LangStrings name;            // 이름
    private LangStrings customerNames;   // 고객사
    private ProjectStatus status;        // 프로젝트상태
    private ProjectPeriod projectPeriod; // 프로젝트기간
    private Member manager;              // 담당자
    private double budget;               // 예산, ex, 21억5천만원
    private int totalEffort;             // 총공수, 230 M/M
    private LocalDateTime syncedAt;      // 마지막 동기화 시각
    ...
}
  • Включите его в AgileProject и определите взаимосвязь

  • Управляйте им как отдельным независимым объектом, принадлежащим scrum, а не как копией crewit

  • Не отражайте источник в реальном времени; заполняйте его посредством пакетной повторной синхронизации, отложенных обновлений (или другой бизнес-логики)

  • Ограничьте момент перехода ситуацией, когда проблемы с производительностью подтверждены на основе фактических шаблонов запросов

Bignow

Site footer