web-dev-qa-db-ja.com

データベーステーブルと列名について何も知らないリポジトリパターンの実装

私はインターネットとGithubの周りを見てきました。データベースのテーブルと列の名前について知っているデザインパターンリポジトリの実装です。データベースをプラグインとして使用したい場合は、コードの残りの部分で別のOpen/Closedのプラグを外してプラグインできると思っていましたが、私のリポジトリは使用しているデータベースの列名を知らないはずです。では、データベースのテーブルと列の名前を知らなくても、データベースからの結果をドメインのエンティティに変換できるようにこのパターンを実装するにはどうすればよいでしょうか。

私の主な言語はPHPなので、Doctrine\ORMでさまざまなyamlsまたはxmls構成ファイルを簡単に渡して、列名をエンティティのプロパティ名にマッピングできることを見ましたが、...ライブラリ実装の人質にはなり得ません。生のPDOを実装したい場合、またはこのハイドレーションをすぐに実行できないリポジトリ用の他のライブラリを実装したい場合は、実装で行う必要があります。

更新
Illustration

更新2
ご覧ください: http://leocavalcante.github.io/patterns/2014/07/11/repository-pattern-and-database-schema.html

6
Leo Cavalcante

リポジトリーの目的は、データベースにアダプターを提供することです。 「データベースについて知っている」ので、コードの残りの部分は必要ありません。

したがって、データベーススキーマを変更した場合は、リポジトリを変更(または完全に置き換える)する必要があることは、私にはかなり理にかなっているようです。重要なのは、実装はインターフェースによってアプリケーションから隠されているため、リポジトリを置き換える必要があるのはのみであるということです。

多数のアグリゲートとそれぞれのリポジトリーがあるため、大規模なアグリゲートの交換が困難な場合は、使用しているテクノロジーによっては、ORMから支援を受けることができる場合があります。一般的なRepository<T>は、たとえばC#ではかなり一般的なようです。

もちろん、ORMを使用している場合でも、データベーススキーマについて知っているコードがどこかにある必要があります。しかし、この方法は通常、XMLマッピングファイルに入れられます(もちろん、独自の利点と欠点があります)。


編集コメントに応じて:

あなたが何を達成しようとしているのか本当にわかりません。あなたの設計では、 'Hydration'オブジェクトがデータベースとの通信とドメインオブジェクトを返す責任を負っているように見えます。したがって、データベーススキーマを変更した場合でも、すべてのHydrationオブジェクトを変更する必要があります。あなたの提案はあなたの知覚された問題を解決しません:データベーススキーマについて知っているどこかにコードがなければなりません。

しかし、設計は非常に複雑です。基本的に何もしない追加のレイヤー(リポジトリ自体)があります。

public class FooRepository
{
    private IFooHydration _fooHydration;

    public FooRepository(IFooHydration fooHydration)
    {
        _fooHydration = fooHydration;
    }

    public Foo Get(int id)
    {
        return _fooHydration.Get(id);
    }

    public void Save(Foo entity)
    {
        _fooHydration.Save(entity);
    }
}

IRepositoryを使用して、さまざまな永続化メソッドに対してさまざまな実装を作成する方がはるかに簡単です。つまり、データベースを変更するときにプラグインするリポジトリです

4

ここで話しているのは 依存関係の逆転 とカプセル化です。コードを使用する場合、ドメインエンティティを取得するいくつかの手段に依存関係を保持する必要がありますが、それがどのように行われるかを知る必要はありません。それは、いくつかの入力を与え、いくつかの出力を得ることが期待されるブラックボックス上の一連のメソッドを必要とします:インターフェース。

データアクセスの具体的な実装にコードを依存させる代わりに、抽象化であるインターフェイスに依存しています。呼び出しコードが抽象化についてのみ知っている限り、正しく実行する限り、そのインターフェイスをどのように実行するかは問題ではありません。

質問からDoctrineおよび生のPDOの例を取り上げると、インターフェイスを次のように定義できます。

interface FooRepositoryInterface {
    public function getAFoo($id);
}

インターフェースが整ったら、必要に応じて任意の方法で実装できます。

public class DoctrineFoo implements FooRepositoryInterface {
    public function __construct(EntityManager $em){ ... }
    public function getAFoo($id){ 
        return $this->em->find("\Entity\Foo", $id);
    }
}

public class PDOFoo implements FooRepositoryInterface {
    public function __construct(PDO $pdo){ ... }
    public function getAFoo($id){ 
        $pdo->prepare("sql ...");
        $row = $pdo->fetchOne();
        return $this->makeFooFromRow($row);
    }     
}

これにより、他のモジュールがインターフェイスの点でデータアクセスにうまく依存できるようになります。

class FooBazer {
    public function doBaz(FooRepositoryInterface $repository, $id) {
        $foo = $repository->getAFoo($id);
        $foo->baz();
    }
}
3
Andy Hunt