DDD, где разместить бизнес-правило типа «У участника может быть только два списка сегментов» - PullRequest
0 голосов
/ 15 октября 2018

Привет, я сейчас играю с проектом DDD (PHP) и сталкиваюсь со следующим вопросом.

Скажем, есть бизнес-правило, которое гласит: член может иметь только двасписки ведра.Объект-член широко используется в системе.

Мой вопрос: куда поместить это бизнес-правило?

Пара вариантов и мои мысли и опасения:

Поместите его в службу приложений:

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

Поместить его в Factory: (уровень домена как простойфабрика или абстрактный класс)

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

Поместите его в доменную службу:

Служба будетпроверьте правило и затем вызовите фабрику, чтобы построить объект.Но опять же можно было бы обойти сервис.

Поместите его в репозиторий: (доменный слой как абстрактный класс)

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

Поместите его в объект:

Проверкадолжен идти в конструкторе объектов или в методе setMemberId, который вызывается в нем.Это позволит убедиться, что недопустимый объект даже не может быть создан, что мне кажется наилучшей ситуацией, но у сущности будет зависимость от BucketListMustBeUniqueForMemberSpecification, которая кажется мне неправильной.

Поместите его в значениеОбъект:

MemberId используется многими другими частями системы, поэтому я бы не стал размещать его там, но я мог бы создать объект значения OwnerId, поместить туда правило и передать его membersId->id () в качестве входных данных конструктора.Правило будет применено, но опять-таки будет зависимость от BucketListMustBeUniqueForMemberSpecification, которая будет внедрена в конструктор: /

class SQLBucketListMustBeUniqueForMemberSpecification implements ...
{

    public function isSatisfiedBy($memberId)
    {
        // sql to try to find a bucket list of the members id

        return $bucketList ? false : true
    }
}

1 Ответ

0 голосов
/ 15 октября 2018

"У участника может быть только два списка сегментов"

Почему бы не поместить правило в сам Member?Если вы не ожидаете очень большого числа ассоциаций, это простое решение для защиты инварианта.

class Member {
    ...
    final Set<BucketListId> bucketListIds;

    public Member(...) {
       bucketListIds = new HashSet<BucketListId>();
    }

    associateBucketLists(Set<BucketListId> bucketListIds) {
        if (bucketListIds.size() > 2) throw ...;
        bucketListIds.clear();
        bucketListIds.addAll(bucketListIds);
    }
}

Вы также можете сделать так, чтобы BucketList создавалось из Member s.

class Member {
    int bucketListCount = 0;

    public BucketList newBucketList(final BucketListId id, final BucketListDetails details) {
        if (bucketListCount == 2) throw ...;

        bucketListCount++;

        return new BucketList(id, this.id, details);
    }
}

class BucketList {
    final BucketListId id;
    final MemberId ownerId;
}

class MemberApplicationService {
    public void newBucketListFor(memberId, ...) {
        Member member = memberRepository.memberOfId(memberId);
        BucketList bucketList = member.newBucketList(...);
        bucketListRepository.add(bucketList);
    }
}
...