Я читал о DDD уже несколько дней и мне нужна помощь в разработке этого образца. Все правила DDD меня очень смущают из-за того, как я вообще должен что-либо строить, когда объектам домена не разрешено показывать методы на уровне приложения; где еще организовать поведение? Хранилища не могут быть внедрены в сущности, и поэтому сами сущности должны работать на состояние. Тогда объект должен знать что-то еще из домена, но другие объекты объекта также не могут быть внедрены? Некоторые из этих вещей имеют смысл для меня, но некоторые нет. Я еще не нашел хороших примеров того, как создать целую функцию, так как каждый пример касается заказов и продуктов, повторяя другие примеры снова и снова. Я учусь лучше всего, читая примеры, и постарался создать функцию, используя информацию, которую я получил о DDD.
Мне нужна ваша помощь, чтобы указать, что я делаю неправильно и как это исправить, наиболее предпочтительно с помощью кода, так как «я бы не рекомендовал делать X и Y» очень трудно понять в контексте, где все уже смутно определено. Если я не могу внедрить сущность в другую, было бы легче понять, как это сделать правильно.
В моем примере есть пользователи и модераторы. Модератор может запретить пользователям, но с бизнес-правилом: только 3 в день. Я попытался настроить диаграмму классов, чтобы показать отношения (код ниже):
interface iUser
{
public function getUserId();
public function getUsername();
}
class User implements iUser
{
protected $_id;
protected $_username;
public function __construct(UserId $user_id, Username $username)
{
$this->_id = $user_id;
$this->_username = $username;
}
public function getUserId()
{
return $this->_id;
}
public function getUsername()
{
return $this->_username;
}
}
class Moderator extends User
{
protected $_ban_count;
protected $_last_ban_date;
public function __construct(UserBanCount $ban_count, SimpleDate $last_ban_date)
{
$this->_ban_count = $ban_count;
$this->_last_ban_date = $last_ban_date;
}
public function banUser(iUser &$user, iBannedUser &$banned_user)
{
if (! $this->_isAllowedToBan()) {
throw new DomainException('You are not allowed to ban more users today.');
}
if (date('d.m.Y') != $this->_last_ban_date->getValue()) {
$this->_ban_count = 0;
}
$this->_ban_count++;
$date_banned = date('d.m.Y');
$expiration_date = date('d.m.Y', strtotime('+1 week'));
$banned_user->add($user->getUserId(), new SimpleDate($date_banned), new SimpleDate($expiration_date));
}
protected function _isAllowedToBan()
{
if ($this->_ban_count >= 3 AND date('d.m.Y') == $this->_last_ban_date->getValue()) {
return false;
}
return true;
}
}
interface iBannedUser
{
public function add(UserId $user_id, SimpleDate $date_banned, SimpleDate $expiration_date);
public function remove();
}
class BannedUser implements iBannedUser
{
protected $_user_id;
protected $_date_banned;
protected $_expiration_date;
public function __construct(UserId $user_id, SimpleDate $date_banned, SimpleDate $expiration_date)
{
$this->_user_id = $user_id;
$this->_date_banned = $date_banned;
$this->_expiration_date = $expiration_date;
}
public function add(UserId $user_id, SimpleDate $date_banned, SimpleDate $expiration_date)
{
$this->_user_id = $user_id;
$this->_date_banned = $date_banned;
$this->_expiration_date = $expiration_date;
}
public function remove()
{
$this->_user_id = '';
$this->_date_banned = '';
$this->_expiration_date = '';
}
}
// Gathers objects
$user_repo = new UserRepository();
$evil_user = $user_repo->findById(123);
$moderator_repo = new ModeratorRepository();
$moderator = $moderator_repo->findById(1337);
$banned_user_factory = new BannedUserFactory();
$banned_user = $banned_user_factory->build();
// Performs ban
$moderator->banUser($evil_user, $banned_user);
// Saves objects to database
$user_repo->store($evil_user);
$moderator_repo->store($moderator);
$banned_user_repo = new BannedUserRepository();
$banned_user_repo->store($banned_user);
Должно ли право пользователя иметь 'is_banned'
поле, которое можно проверить $user->isBanned();
? Как снять бан? Я понятия не имею.
источник
Ответы:
Этот вопрос несколько субъективен и ведет к большему количеству дискуссий, чем к прямому ответу, который, как указал кто-то другой, не подходит для формата stackoverflow. Тем не менее, я думаю, что вам просто нужны некоторые закодированные примеры того, как решать проблемы, поэтому я попробую, просто чтобы дать вам некоторые идеи.
Первое, что я бы сказал:
Это просто неправда - мне было бы интересно узнать, откуда вы это прочитали. Прикладной уровень является связующим звеном между пользовательским интерфейсом, инфраструктурой и доменом и поэтому, очевидно, должен вызывать методы на объектах домена.
Я написал зашифрованный пример того, как я буду решать вашу проблему. Я прошу прощения, что это на C #, но я не знаю PHP - надеюсь, вы все равно получите суть структуры.
Возможно, я не должен был этого делать, но я немного изменил ваши доменные объекты. Я не мог удержаться от ощущения, что он был слегка ошибочным, поскольку в системе существует понятие «BannedUser», даже если срок действия запрета истек.
Для начала, вот служба приложений - это то, что пользовательский интерфейс назвал бы:
Довольно прямо вперед. Вы выбираете модератора, делающего бан, пользователя, которого модератор хочет забанить, и вызываете метод 'Ban' для пользователя, передавая модератора. Это изменит состояние как модератора, так и пользователя (поясняется ниже), которое затем необходимо сохранить через соответствующие репозитории.
Пользовательский класс:
Инвариант для пользователя заключается в том, что он не может выполнять определенные действия, когда его забанят, поэтому мы должны иметь возможность определить, заблокирован ли пользователь в данный момент. Для этого пользователь ведет список запретов на обслуживание, которые были выданы модераторами. Метод IsBanned () проверяет любые запреты на обслуживание, срок действия которых еще не истек. Когда вызывается метод Ban (), он получает модератора в качестве параметра. Затем он просит модератора выдать бан:
Инвариант модератора заключается в том, что он может выдавать только 3 бана в день. Таким образом, когда вызывается метод IssueBan, он проверяет, что у модератора нет 3 выпущенных запретов с сегодняшней датой в его списке выпущенных запретов. Затем он добавляет недавно выпущенный бан в свой список и возвращает его.
Субъективно, и я уверен, что кто-то не согласится с подходом, но, надеюсь, он даст вам представление о том, как он может сочетаться друг с другом.
источник
Переместите всю свою логику, которая изменяет состояние, на уровень сервиса (например, ModeratorService), который знает как об объектах, так и о репозиториях.
источник