2012-02-11 5 views
0

内のエンティティの状態へのアクセス私は:)DDD - リポジトリ

をすれば、私に知らせて、私は良いデザインの経験が不足していると私は以上の事を複雑かもしれないという感じはの例を見てみましょうユーザーエンティティとユーザーリポジトリ

  1. そのPHP(タフでなければなりません:リポジトリ

    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; 
        } 
    } 
    

    ので、いくつかの注意事項、私は質問をする前と 病気開始C++ \ C#\ Java)誰もが理解しやすい

  2. 私は省略しますこの例を簡単にするために、基本クラス(抽象リポジトリとUserRepositoryとUserEntityが継承する抽象エンティティのような)を実装しました。

  3. 私はファクトリオブジェクト/クラスパターンを放棄しましたが、代わりにエンティティ自体の中にファクトリメソッドパターンを使用することを好みます。

  4. ファクトリメソッドは、この例では冗長に見えると

    $ユーザー= UserEntity(配列( 'ユーザ名' => $ユーザ名、 'パスワード' => $パスワード))と置換することができます。

しかし、現実にはその工場は、(例えばPOSTフォームから)「生」のデータを受け取り、その後、が有効なユーザーエンティティをを作成するために必要なすべてのエンティティやバリューオブジェクトを作成しますので、より複雑なビット(そうエンティティ内のパスワードは実際のパスワードではなく、パスワードではなくパスワードのハッシュを含む値オブジェクトです。今の質問に

私は世界に私の私は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; 
    } 
} 

のようなものをすべてを通過させることである私は、エンティティの内部状態を公開していないここので、それは私に、「より正確」に見えるとリポジトリだけがそれについて知っています。

あなたはどう思いますか?エンティティの内部状態が公開されていますか?あるいは、2番目の例は、そのようなシステムを設計するより良い方法でしょうか?あるいはもっと良い方法を知っているかもしれませんか?

ありがとうございました!

答えて

3

まず、エンティティとリポジトリに単一の基本クラスを使用しないでください。彼らは関係がありませんし、あなたはそれを行うことによってすべての単一の原則を破るだろう:)###

これについては、さまざまな方法があります。最初に気になるのは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は永続性の単一の責任を持ちます。リポジトリパターンは、ストレージインフラストラクチャを抽象化するために使用できますが、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(); 
} 

これはhakreと同じように思えるかもしれませんが、RepositoryパターンとDAOパターンには大きな違いがあります。

私は、PHPのメンバー可視性オプションについて知っていました(public/private/protected?internal以外のスコープを持っていますか?)おそらく、リポジトリパターンを使用して実装する方法について助言を与えることができます。ここでそれを行う必要があるように思えます...

編集:私はメソッドシグネチャで誤解を招くような情報を与えたことがありました。 DAOはエンティティを返さないでください。エンティティはコンストラクタにDAOを取得し、そこから新しいものを作成する必要があります。

編集2:異なるアプローチは、値オブジェクトとしてUserEntityを扱うことです。州のためにセッターを公開するのではなく、パブリックゲッターを公開してください。リポジトリはそれを使用してデータを取得し、エンティティを取得するときにはすべての属性をコンストラクタに渡します。しかしこれは、あなたがユーザーオブジェクトをどのように使いたいかということにいくつかの意味を持ちます。なぜなら、私はそれを以前に提案しなかったからです。

+0

非常に興味深いアプローチです!私はDTOについて知りませんでした(私のStateクラスはあなたのDTOに似ています)。 DAOについては、DAOが特定のストレージにアクセスするためのインターフェイスであることを確信していました。リポジトリ。 DDDにはリポジトリが存在する必要があるので、実装にリポジトリがないという事実は完全ではありません。 –

+0

DAOは同様の抽象化を提供していますが、集約を格納するのには適していません。実際には、青い本で伝えられている記憶域を抽象化するための単なる使用可能なパターンです。例:グループを持つことができます。グループは複数のユーザーの集約ルートになります。グループを持続させるためのGroupRepositoryを持つことができ、そのグループは個々のユーザーを永続させるためにユーザーDAOの束を使いました。 DAOは集約で使用すると本当に乱雑になります。 – Tobias

+1

ああ、DDDから離れていくための主なポイントは、ビジネスドメインが、あなたが取り組んでいる特定のインフラストラクチャではなく、デザインを推進すべきだということです。リポジトリ、およびDAOパターンは、インフラストラクチャを抽象化して、ドメインのユビキタス(スペル?)言語を定義し、テクノロジに悩まされることなくその周辺をデザインすることができます。 – Tobias

-1

あなたは各エンティティおよびリポジトリ自体の両方がエンティティゲッター/セッターがのメンバーを保護しているのと同じ基本クラスを持つ作る、という場合、これらのメソッドは、リポジトリ

内部で使用する必要があります。これは、これ以上保護され、公開されていません。

これにより、リポジトリとエンティティがより緊密に結びついていきます。これは、リポジトリがエンティティを生み出すため、あなたのケースでは悪くない可能性があります。

+0

これは実際に興味深いアプローチです。しかし、それは "ハッキング"に見えます。私はクラスAがBから継承するかBを含むべきかを決定するために「is-a」と「has-a」の関係に従う傾向があり、EntityとRepositoryが継承する基本クラスを概説することはできません。面白い方法をありがとう、それは他の場所で非常に有用かもしれないが、ここでは、申し訳ありません。 –

+0

PHPでは「パッケージ」や「友人」といったものはありません。そのため、特定のクラスのグループのみがそのメンバーにアクセスできるようにします。 – hakre

+0

私はPHPに「パッケージ」や「友人」がないことを知っています。これは他の解決策を探している理由です。これは、不足している動作を模倣するためにハック/トリックを使用すべきではありません。リポジトリとエンティティは基底クラスから継承できませんでしたが、少なくともそのようなクラスの概要を説明することはできません。間違いかもしれません。その場合、批評や提案を受け入れることができます。 –

1

なぜホイールを再発明するのですか? Doctrine2と呼ばれるエンタープライズレベルのライブラリが既に存在するようにしようとしているのですが、使いやすく、ドメインで遭遇するかもしれない野生の集約ルーツを処理できる、より多くの機能を備えています。

ARは非常に手間がかかり、手作業が多いので時間がかかるので、私は「自宅で作られた」ソリューションを控えることをお勧めします。