Как разделить схему доменных событий между микросервисами? - PullRequest
1 голос
/ 01 февраля 2020

В доменном дизайне мы создаем ограниченный контекст. Это означает, что агрегаты не имеют общей модели.

Это изображение от Microsoft показывает, как агрегаты взаимодействуют с помощью команд и событий ( source ). Communication between aggregates

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

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

1 Ответ

1 голос
/ 02 февраля 2020

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

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

Вы также можете проверить все шаблоны в enterpriseintegrationpatterns.com и получить книгу. Настоятельно рекомендуется.

Редактировать:

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

ПРИМЕЧАНИЕ Для простой пример, всегда трудно обосновать некоторые решения, так как с дополнительными вещами большинство вещей будет намного проще. Реализация сделана таким образом, чтобы помочь с примером.

Прежде чем я продолжу: Наличие CDN будет иметь некоторые накладные расходы при переводе с внутренней на внешнюю модель. Если вы думаете, что вы можете go без него, сделайте это. Не делайте вещи более сложными, чем они должны быть. Проблема здесь в том, что если вам нужно изменить команду или событие, это распространится на все службы и вызовет огромный волновой эффект в вашей системе. В книге «Проектирование под управлением домена» есть раздел, посвященный антикоррупционному слою. Проверьте это.

Почему мы используем CDM? Для Инкапсуляция . Точно так же, как объект инкапсулирует свои данные, сервис инкапсулирует свои внутренние компоненты. CDM предназначен для использования только для связи / интеграции между службами и предназначен для совместного использования между ними.

Совместное использование внутренней модели услуги с другими службами - это плохо, поскольку разработчики не смогут изменить эту Внутреннюю модель . Также иногда детали из одного сервиса могут просочиться в другие сервисы и могут вызвать огромные проблемы.

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

Эта модель должна быть разработана на более высоком уровне абстракции: на уровне системы, с учетом ее процессов и потоков. Следует исключить какие-либо подробности о внутренних функциях отдельных служб.

Вам нужно Перевод между внутренней и внешней моделями. Кроме того, вы можете использовать ContentFilter и ContentEnricher , если вам нужно отфильтровать входящие события или команды и добавить дополнительные данные к исходящим событиям или командам.

// Canonical Data Model

namespace CDM {

public interface ICommand { }
public interface IEvent { }

public class CustomerInfo {

    public Guid Id { get; set; }
    // notice here the CDM uses the two separate properties for the first and last name
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class LineItemData {

    public Guid ProductId { get; set; }
    public Quantity Quantity { get; set; }
    public Money ListPrice { get; set; }
}

public class PlaceOrderCommand : ICommand {

    public CustomerInfo CustomerInfo { get; set; }
    public IReadOnlyList<LineItemData> LineItems { get; set; }
}

public class OrderPlacedEvent : IEvent {

    public Guid OrderId { get; set; }
    public IReadOnlyList<LineItemData> LineItems { get; set; }
}

} // end Canonical Data Model namespace

// Order Service Internals

// the name is done this way to differentiate between the CDM
// and the internal command, do not use it this way into production

public class LineItem {

    // the internal one includes the OrderId { get; set; }
    public Guid OrderId { get; set; }
    public Guid ProductId { get; set; }
    public Quantity Quantity { get; set; }
    public Money ListPrice { get; set; }
}

public class PlaceOrderInternalCommand {

    public Guid CustomerId { get; set; }
    public string CustomerFullName { get; set; } // a single full name here
    public IReadOnlyList<LineItemData> LineItems { get; set; }
}

public class Event { }

public class OrderPlacedInternalEvent : Event {

    public Guid OrderId { get; set; }
    public IReadOnlyList<LineItem> { get; set; }
}

// this is part of the infrastructure, received messages and translates/transforms
//them from the external CDM to the internal model.
// This is the input/receiving part of the service

public class CommandDispatcher {

    public void Dispatch(ICommand cmd) {

        // here we will use a MessageTranslator, check PatternsUsed section

        var translator = TranlatorsRegistry.GetFor(cmd);
        var internalCommand = translator.Translate(cmd);
        Dispatch(internalCommand);
    }
}

public class CommandHandler {

    public void Handle(PlaceOrderInternlCommand cmd) {

        // this will create the OrderCreated event
        var order = CreateOrder(cmd); 
        // this will save the OrderCreated event
        OrderRepository.Save(order);
    }
}

// Another part of the infrastructure that publishes events.
// This is the output/sending part of the service

public class EventPublisher {

    public void Publish(Event event) {

        // another usage of the MessageTranslator pattern, this time on events
        var translator = EventTranslatorRegisty.GetFor(event);
        var cdmEvent = translator.Translate(event);
        // publish to kafka or whatever you are using
        PubilshToMessageMiddleware(cdmEvent);
    }
}

Используемые шаблоны :

Каноническая модель данных MessageTranslator CommandMessage EventMessage

...