Как разделить сущность доктрины на более доменные сущности в разных ограниченных контекстах?

Я пробую свои первые шаги в DDD (домен-управляемая конструкция поле). Мне нравится мудрое правило, что вы должны разделить свои сущности на множество меньших, контекстно-специфичных сущностей (например User сущность имеет тенденцию перерастать почти в каждом не-ddd или плохо разработанном приложении). Но каковы общие варианты того, как это сделать (эффективно) в php с использованием доктрины?

предположим, у меня есть два ограниченных контекста:Store и Invoicing

каждый из них имеет двух сущностей предметной области : для магазина BC это FirstTimeCustomer и RecurrentCustomer для выставления счета BC это VatCustomer и NonVatCustomer

теперь, на моем уровне инфраструктуры, я хочу, чтобы все они были сохранены в одной (по крайней мере, базовой) таблице, чтобы иметь возможность ссылаться на них general UserId (Uuid). Проблема для меня в том, как это сделать, чтобы я мог использовать преимущество doctrine automapping в своих реализациях репозитория.

Я читал о наследовании одной таблицы или Наследование Таблицы Классов что может быть решением, но:

мне нужно иметь возможность работать с User того же Uuid, что и другой тип в каждом контексте.

мне нужно иметь возможность вернуть некоторые AbstractStoreCustomer ака FirstTimeCustomer или RecurrentCustomer on $storeCustomerRepository->find(1); в магазине BC.

и вернуть AbstractInvoicingCustomer ака VatCustomer или NonVatCustomer on $invoicingCustomerRepository->find(1); в выставлении счетов BC.

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

Я также читал о Сопоставлены Суперклассов, это тоже похоже на вариант, но:

это означает, что ассоциации "один ко многим" невозможны на сопоставленном вообще суперкласс.

а мне нужно Customer чтобы иметь отношение к заказам, которые должны быть доступны в обеих сущностях (также, возможно, не хотите, чтобы он был доступен, это какой-то новый тип User сущность в новом BC).

I пришел к выводу, что я должен подозревать, что я что-то упускаю, не так ли? Как я должен достичь разбиения сущности Бога на более мелкие контекстно-специфические?

1 ответов


я начну с некоторых общих правил. Думая о DDD, вы должны отбросить любые мысли о постоянстве (таблицы и первичные ключи и тому подобное). Вы должны создавать свои агрегатные корни и сущности так, как если бы вы хранили их в файле. Когда вы разрабатываете модели, Если вам приходит в голову мысль о таблицах или ORM, вы не можете сделать DDD; сделайте шаг назад и перезапустите.

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

это, как говорится, давайте поговорим о вашем бизнесе. В Authentication BC, есть Users который может аутентифицироваться с некоторыми учетными данными. В Store до н. э. Есть Customers что покупать вещи. В Invoice до н. э., Есть также Customers но другую модель (другой класс, если хотите). В Shipping BC есть также Customers но, опять же, разные модели. Эти Customer модели разделяют некоторые свойства вдоль BCs, как name и id. Итак, вы используете id чтобы идентифицировать человека из реальной жизни, но использовать различные модели для инкапсуляции их поведения в зависимости от контекста.

я думаю, что у вас не должно быть AbstractStoreCustomer, вы должны иметь только Customer и попытаться изолировать то, что дифференцируйте их в абстрактном классе / интерфейсе, например BuyingHistoryProfile С 2 реализация FirstTimeCustomer и RecurrentCustomer. Этот класс должен содержать только поведение относительно профиля покупки, и на него должен ссылаться каждыйCustomer на Store до нашей эры. Аналогично, попробуйте извлечь класс в Invoicing BC для расчета цены.

теперь, когда мы создали модели, мы можем думать о сохраняемости. Для каждого ограниченного контекста создайте Customer таблица, содержащая общие свойства (name и id) и специфические свойства (buyingHistoryProfile на Store BC,priceCalculationStrategy на Invoicing до н. э. и т. д.).

если вам интересно, что есть степень дублирования данных, то вы правы, но это нормально, необходимо отделить модели. Эти повторяющиеся данные синхронизируются, когда User на Authentication BC изменить свое имя: вы обновляете все другие модели прямо тогда. С этой точки зрения, Invoice и Store BC вниз-потоки, они используют данные от Authentication BC; это зависит от вашего бизнеса, когда обновление инициируется.

будучи поклонником событийных архитектур, я рекомендую вам взглянуть на CQRS (и даже на источники событий). Я думаю, что эти архитектуры хорошо вписываются в это приложение. CQRS может сделать ваши модели в 10 раз чище, я говорю вам по своему опыту. С Event Sourcing очень вероятно, что вам даже не придется использовать ORM. Вы могли бы использовать ORM на стороне чтения, если вам действительно нравится они или ты!--48-->не могу оставить их. Я лично не люблю (и не использую) ORMs.