私のプロジェクトでは、DBALに5つのサブレイヤーを指定しました。
これで、各レイヤーは、その下にあるそれ自体のレイヤーのクラスにのみアクセスできるはずであるという制限を設定しました(レイヤー4はレイヤー4および3にアクセスできますが、レイヤー5にアクセスしてはならず、レイヤー1および2にはアクセスしてはなりません)。しかし、クエリビルダーに関しては(データベース接続を必要としないため、 https://github.com/nilportugues/php-sql-query-builder を使用することにしました)疑問に思いますこれをどのレイヤーに割り当て、どこで使用するか。
明らかに、結果のクエリはレイヤー3に渡される必要があるため、クエリビルダーは3または4のいずれかである必要がありますが、ほとんどの場合、レイヤー4オブジェクトを作成するときに、レイヤー5でクエリを作成する必要があるようです。
現在、クエリビルダーを使用するコードは次のようになっています(レイヤー5)。
$builder = $this->entityFactory->getDbRequest()->getQueryBuilder();
// build the $query
$entity = $this->entityFactory->makeEntityByQuery( Entityname::class, $query);
したがって、私は現在QueryBuilderをレイヤー3に配置していますが、それによってレイヤー5からレイヤー3にアクセスすることを余儀なくされています。レイヤー4に移動することもできますが、モデルレイヤーのほとんどのクラスの依存関係にするか、そのEntityFactoryから直接アクセスできるようにする必要があります。ただし、EntityFactoryには2つの責任があり、SRPに違反します。
クエリビルダーを使用するのはどのレイヤーですか。
レイヤー3のように感じますが、データベースリクエストレイヤーにはこれが必要です。まず、正直に言うと、レイヤリングは固定を使用できます。 「レイヤー」と「これはどのレイヤーに入るのですか?」という質問がある場合簡単に答えられない場合は、十分なレイヤーがないか、rightレイヤーがないかのどちらかです。
あなたが持っているもの:
データベース層
データベース接続層
データベース要求層
(これはあなたの問題です)データベースモデルレイヤー
(これもそうです)モデルレイヤー
レイヤー3、データベースリクエストレイヤーは、クエリビルダーが属する場所です。そのレイヤーの外では、アドホッククエリを実行するべきではありません。実際、レイヤー3は、より一般的にはデータアクセスレイヤー(DAL)またはリポジトリレイヤーと呼ばれます。これは、データベースについて知っている層です。エンティティオブジェクトを生成するために必要なファクトリについても知っている必要があります。
DALまたはリポジトリは、データベース要求とエンティティファクトリの2つのサブ層の間の調整層と考えてください。
本当に必要なもの:
+----------+
| Database |
+----------+
/\
||
||
\/
+----------------------------+ +---------------+ +----------------+ +--------+
| Database Request (Gateway) | | Query Builder | | Entity Factory | | Entity |
+----------------------------+ +---------------+ +----------------+ +--------+
/\ /\ /\ /\
|| || || ||
|| || || ||
|| || ++===============++ ||
|| || || ||
|| || || ||
|| \/ \/ ||
|| +------------+ ||
++==================> | Repository | <=============================++
+------------+
/\
||
||
\/
+------------------+
| Your Application |
+------------------+
エンティティクラスには、永続性に関する知識がまったくないはずです。クエリビルダーを含め、その下にある複数のレイヤー間を調整するのはリポジトリ/ DALです。
リポジトリ/ DALの外部には、クエリビルダーでanythingを実行してはなりません。これは、SQLクエリの構築を容易にするためのユーティリティです。代わりに、検索を実行する必要がある場合、すべての可能な基準値を含むデータ転送オブジェクト(DTO)を提供できます。リポジトリ/ DALには、この基準DTOを引数として受け入れ、DTOを使用してクエリビルダーを使用するクエリを作成するメソッドが必要です。
それから、クエリビルダーオブジェクトをデータベースへのリクエストに変換する必要があります。 Query Builderを変換してDBリクエストを作成するQuery Executorと呼ばれる別のクラスを作成するか、または単にクエリビルダーをDBリクエストオブジェクトに直接渡すことができます。ええ、あなたは「層の分離を打ち破る」が、あなたはどのくらいのカップリングを実際に導入しましたか?それはあなた次第です。
アプリケーションがこのような質問がある複雑さに到達したら、優れたオブジェクトリレーショナルマッパー(ORM)の学習と構成に時間を費やす必要があります。
では、これらのリポジトリクラスをアプリケーションクラスに挿入しますか?
はい。ただし、通常はコントローラーとサービスクラスのみがリポジトリオブジェクトにアクセスする必要があります。
それらのリポジトリー・クラスはコントローラー(またはそれぞれDIコンテナー)で手動で作成されていますか?
少しは「はい」、少しは「いいえ」です。
Dependency Injectionを使用する場合、コントローラーをチャーンするDIコンテナーまたはファクトリオブジェクトがリポジトリーをインスタンス化します。
リポジトリのインターフェースを導入する場合、次の両方を使用できます。
interface IFooRepository
{
public function find($id);
}
class FooRepository implements IFooRepository
{
}
class FoosController
{
public function __construct(IFooRepository $repository = null) {
if (!isset($repository)) {
$repository = new FooRepository();
}
$this->repository = $repository;
}
}
コントローラーのコンストラクターは、IFooRepository
を引数として受け入れます。 null
の場合、デフォルトでFooRepository
具象実装になります。これで、IFooRepository
インターフェースのモックまたはスタブを提供することで、コントローラーを実際にユニットテストできます。
クエリビルダーがL3にあり、L5からアクセスする場合は、L4にクエリビルダーのシンラッパーを追加して、L5からL4ラッパーにアクセスし、L4ラッパーがL3にアクセスするようにします。例外として、L4は単一のテーブルを表すクラス用であるため、クエリビルダーのラッパーはここに適合しないようです...
したがって、すべてがこれらのレイヤーにきちんと収まるわけではないかもしれませんが、場合によってはそれでよいこともあります。レイヤー間の関係がもう少し複雑な場合、レイヤーを厳密な階層に配置することの問題かもしれません。
たぶん、あなたはコードの構造についてどう思うかtoo制限的ですか?