Основной совокупный вопрос

Разрешено ли клиентскому коду ссылаться на объекты внутри агрегата, который не является корневым? у меня есть Story (Корень), Team (Сущность) и TeamMember (Сущность). Я пытаюсь решить, если AddTeamMember метод принадлежит Team или же Story,

Я думаю, что мой пример был немного ошибочным. Мой реальный вопрос: может ли код клиента ссылаться на некорневые объекты в совокупности?

4 ответа

Решение

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

Простой пример:

    public class MyAggregateRoot
    {
        protected MyEntity entity;

        public void BuildUpAggregate()
        {
            ValidateSomeRule();
            LoadEntityFromDatabase();
        }

        public MyEntity MyEntity
        {
            get 
            {
                VerifySomeOtherRule();
                return entity; 
            }
        }
    }

Как вы можете видеть, при построении и извлечении MyEntity через агрегатный корень мы проверили два правила проверки - если вы позволите клиенту напрямую ссылаться на MyEntity, агрегат может измениться во времени между клиентом, который извлек сущность и выполнил над ней операцию, поэтому проверки больше не будут действительными, но вы не узнаете об этом факте. Другими словами, ваш агрегат будет непоследовательным и потенциально недействительным.

В общем, в DDD существует строгое правило, которое гласит, что весь доступ к объектам в совокупности должен осуществляться путем обхода от корня агрегата - это правило именно для согласованности агрегатов.

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

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

У команды должны быть отношения с членом команды. где история и член команды не имеют прямого отношения друг к другу.

ваш класс должен выглядеть так:

class team
{
list<TeamMember> teamMembers;
void AddTeamMember(TeamMember member){}
}

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

addteamMember(Team T, TeamMember member);

Корень должен предоставлять надлежащие методы для доступа к некорневым элементам (первый уровень вложенности). В вашем случае команда должна иметь метод addTeamMember, а история должна иметь метод (AddMemberToTeam(team t, teammember m)), который вызывает метод addTeamMember указанной команды.

Не зная, как ваша архитектура должна работать, похоже, что она должна продолжаться Team вместо Story, Это при условии, что у вас есть 1 ко многим из Team в Team Memberи что Team Member это дитя Team объект, а не Story,

Другие вопросы по тегам