DDD - Доступ к состоянию сущности в репозитории - PullRequest
0 голосов
/ 11 февраля 2012

Мне кажется, что мне не хватает опыта хорошего дизайна, и я могу слишком усложнить ситуацию, дайте мне знать, если я это сделаю:)

Давайте рассмотрим пример сущности User и репозитория User.Я начну с репозитория

class UserRepository {
  public function save(User $user) {
    if($user->getStatus() == User::STATUS_NEW)
      $this->getDataAccessObject()->insert($user->getState());
    else
      $this->getDataAccessObject()->update($user->getState());
    $user->setStatus(User::STATUS_MANAGED);
  }
}

И сам пользователь

class UserEntity {
  const STATUS_NEW = 1;
  const STATUS_MANAGED = 2;

  private $_status = self::STATUS_NEW; 
  private $_state = array();

  public static function create($username, $password) {
    return new UserEntity(array('Username' => $username, 'Password' => $password));
  }

  public function __construct(array $state) {
    $this->_state = $state;
  }

  public function getState() {
    return $this->_state;
  }

  public function getStatus() {
    return $this->_status;
  }

  public function setStatus($status) {
    $this->_status = $status;
  }
}

Так мало примечаний к коду, прежде чем я задам вопрос:

  1. Его php (жесткий должен быть легким для понимания для всех, кто владеет C ++ \ C # \ Java)

  2. Я опустил базовые классы (такие как абстрактный Repository и абстрактный Entity, из которых UserRepository и UserEntityнаследует) для простоты примера.

  3. Я отказался от фабричного шаблона объекта / класса и вместо этого предпочитаю использовать фабричный шаблон метода внутри самого объекта Entity.

  4. Заводской метод выглядит избыточным в этом примере и может быть заменен на

    $ user = UserEntity (array ('Username' => $ username, 'Password' => $ password));

Но на самом деле все немного сложнее, поскольку фабрика принимает "необработанные" данные (например, из формы POST) и создает все необходимые объекты Entity или Value, чтобы затем создать действительный пользовательский объектмеч внутри сущности может быть не реальным паролем, а объектом-значением, который содержит хэш пароля, а не пароля).

Теперь к вопросу:

Я не закончил с моимЯ в том, что я предоставляю миру методы getState () и setStatus ().Эти методы должны использоваться только внутри репозитория, но поскольку они общедоступны, ничто не запрещает мне получать доступ к состоянию сущности и / или изменять его статус в любом месте.Опять же, это может быть из-за реакции и из-за усложнения вещей, но я чувствую, что это неправильно.

Единственное «решение», которое я нахожу, - это передать все через хранилище, что-то вроде

class Entity {
  public static function create($identity, $param1, $param2, Repository $r) {
    $state = $r->getNewState($identity, $param1, $param2);
    return new Entity($state);
  }

  private $_state = null;

  public function __construct(State $state) {
    $this->_state = $state;
  }

  public function getIdentity() {
    $this->_state->getIdentity();
  }
}

class Repository {
  private $_states = array();

  public function getNewState($identity, ...) {
    $state = new State($identity, ...);
    $this->_states[$identity] = $state;
    return $state;
  }

  public function save(Entity $entity) {
    $id = $entity->getIdentity();
    //maybe check if such entity is managed by this repository..
    if($this->_states[$id]->getStatus() === State::STATUS_NEW)
      $this->getDataAccessObject()->insert($this->_states[$id]->toArray());
    else
      $this->getDataAccessObject()->update($this->_states[$id]->toArray());
    $this->_states[$id]->setStatus(State::STATUS_MANAGED);
  }
}

class State {
  const STATUS_NEW = 1;
  const STATUS_MANAGED = 2;

  private $_state = array()
  private $_identity = 'something';

  public function getIdentity() {
    return $this->_state[$this->_identity];
  }

  public function toArray() {
    return $this->_state;
  }
}

Это выглядит «более правильным» для меня, так как здесь я не раскрываю внутреннее состояние сущности, и об этом знает только хранилище.

Так что вы думаете?Является ли раскрытие внутреннего состояния сущности в порядке?Или, может быть, второй пример - лучший способ спроектировать такую ​​систему?Или, может быть, вы знаете лучшие способы?

Большое спасибо!

Ответы [ 3 ]

3 голосов
/ 11 февраля 2012

Прежде всего: не используйте один базовый класс для Entity и Repository;они не связаны между собой, и вы будете нарушать каждый твердый принцип, выполняя это:)

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

UserEntity myEntity = new UserEntity("username","password");
UserDAO myDAO = new UserDAO(myEntity)
myDao.Create();

или

UserDAO myDAO=new UserDAO();
UserEntity myEntity=myDAO.GetByUsername("username");

В этом случае и UserDAO, и UserEntity могут расширять базовый класс DTO, и вы можете сохранить свою бизнес-логику в сущности и код персистентности в DAO, тем самым устраняя все дублирование полей и(дрожь) свойства.Сущность будет нести единоличную ответственность бизнес-логики, а DAO - единоличную ответственность за упорство.Шаблон Repository можно использовать для абстрагирования инфраструктуры хранения, но при использовании DAO он обычно не стоит.

public class UserDTO
{
    protected bool banned;
    protected string username;
    protected string password;
}

public class UserEntity : UserDTO
{
    public void BlockUser();
    public void ChangePassword();
}

public class UserDAO : UserDTO
{
    private int Id;
    public void Create();
    public void Update();
    public UserEntity GetByUsername(string userName);
    public void Delete();
}

Теперь это может показаться тем же, что сказал Хакре, но между репозиторием есть большая разницашаблон и шаблон DAO.

Если бы я знал что-либо о параметрах видимости элементов в PHP (например, есть ли у вас другие области, кроме public / private / protected? internal?), я мог бы дать вам несколько советов о том, какреализовать его, используя шаблон репозитория, но DAO, похоже, должен сделать это здесь ...

Редактировать: Мне пришло в голову, что я дал некоторую вводящую в заблуждение информацию в сигнатурах метода;DAO не должен возвращать объект, у объекта должен быть конструктор, принимающий DAO, создающий из него новый ...

Редактировать 2: Другой подход заключается в том, чтобы рассматривать UserEntity как объект значения,Не выставлять сеттер для государства, но публичный геттер.Хранилище получает данные, используя это, и просто передает все атрибуты через конструктор при извлечении сущности.Однако это влияет на то, как вы хотите работать с пользовательскими объектами, и по этой причине я не предлагал этого ранее.

1 голос
/ 14 февраля 2012

Зачем изобретать велосипед? Для того, что вы пытаетесь сделать, уже есть библиотека уровня предприятия под названием Doctrine2 - она ​​имеет больше функций, проста в использовании и может обрабатывать самые дикие совокупные корни, с которыми вы можете столкнуться в своем домене.

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

0 голосов
/ 11 февраля 2012

Этот метод следует использовать только внутри репозитория

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

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

...