これは、あなたの質問の下での私の最初のコメントのより整形式の転写です。OPが対応する質問への回答は、この回答の下部に記載されています。また、同じ場所にある重要なメモを確認してください。
現在説明しているSipoは、Active recordと呼ばれるデザインパターンです。すべてと同様に、これもプログラマーの間でその位置を見つけましたが、1つの単純な理由、スケーラビリティのためにリポジトリとデータマッパーパターンを支持して破棄されました。
つまり、アクティブなレコードはオブジェクトであり、次のことを行います。
- ドメイン内のオブジェクトを表します(ビジネスルールを含み、ユーザー名を変更できるかどうかなど、オブジェクトに対する特定の操作を処理する方法を知っています)、
- エンティティを取得、更新、保存、削除する方法を知っています。
現在の設計に関するいくつかの問題に対処し、設計の主な問題は最後の6番目の点で対処します(最後ではありますが、私は推測します)。コンストラクタを設計するクラスがあり、コンストラクタが何をすべきかさえ知らない場合、クラスはおそらく何か間違ったことをしているでしょう。それはあなたの場合に起こりました。
しかし、エンティティー表現とCRUDロジックを2つ(またはそれ以上)のクラスに分割することにより、設計の修正は実際には非常に簡単です。
これは、現在の設計の外観です。
Employee-従業員の構造(その属性)およびエンティティを変更する方法に関する情報(変更可能な方法を選択した場合)、EmployeeエンティティのCRUDロジックが含まれ、Employeeオブジェクトのリストを返すことができEmployee、必要に応じてオブジェクトを受け入れる従業員を更新し、次のEmployeeようなメソッドを介してシングルを返すことができますgetSingleById(id : string) : Employee
うわー、クラスは巨大なようです。
これが提案されたソリューションになります。
Employee -従業員の構造(その属性)およびエンティティを変更する方法に関するメソッドが含まれています(変更可能な方法を選択した場合)
EmployeeRepository- EmployeeエンティティのCRUDロジックが含まれ、EmployeeオブジェクトのリストをEmployee返すことができ、従業員を更新するときにオブジェクトを受け入れ、次のEmployeeようなメソッドで単一を返すことができますgetSingleById(id : string) : Employee
懸念の分離について聞いたことがありますか?いいえ、あなたは今します。これは、単一責任原則の厳密性の低いバージョンであり、クラスには実際に1つの責任のみを持たせるか、ボブおじさんが次のように述べています。
モジュールには、変更する唯一の理由が必要です。
最初のクラスを2つのクラスに明確に分割でき、それらのインターフェイスがまだ丸みを帯びている場合は、おそらく最初のクラスが多すぎて多すぎることは明らかです。
リポジトリパターンの優れた点は、データベース(何でも、ファイル、noSQL、SQL、オブジェクト指向のもの)の中間層を提供する抽象化として機能するだけでなく、具体的である必要さえありません。クラス。多くのオブジェクト指向言語では、インターフェイスを実際のinterface(またはC ++の場合は純粋な仮想メソッドを持つクラス)として定義し、複数の実装を持つことができます。
これにより、リポジトリが実際の実装であるかどうかの決定が完全に解除されますinterface。これは、キーワードを持つ構造に実際に依存することにより、単にインターフェイスに依存しているだけです。そして、リポジトリはまさにそれです。これは、データ層の抽象化、つまりデータをドメインにマッピングしたり、その逆を行ったりするための、凝った用語です。
(少なくとも)2つのクラスに分けることのもう1つの素晴らしい点は、Employeeクラスが独自のデータを明確に管理し、非常にうまく処理できるようになったことです。
質問6:では、コンストラクターは新しく作成されたEmployeeクラスで何をすべきでしょうか?それは単純だ。引数を取る必要があり、それらが有効かどうか(年齢がおそらく負であったり、名前が空であってはならない)をチェックし、データが無効で、検証が引数をプライベート変数に割り当てる場合にエラーを発生させる必要がありますエンティティの。データベースと通信することはできません。これを行う方法がわからないからです。
質問4:答えは、あなたが本当に必要としているものに大きく依存しているため、一般的には答えられません。
質問5:肥大化したクラスを2つに分離したEmployeeので、のようchangeUsernameにクラスに複数の更新メソッドを直接持つことができますmarkAsDeceased。これは、RAMでのみEmployeeクラスのデータを操作し、リポジトリクラスへの作業単位パターン。これにより、このオブジェクトがプロパティを変更し、メソッドを呼び出した後に更新する必要があることをリポジトリに通知します。registerDirtycommit
明らかに、更新の場合、オブジェクトにはIDが必要であるため、すでに保存されている必要があり、これを検出して基準が満たされない場合にエラーを発生させるのはリポジトリの責任です。
質問3:作業単位パターンを使用する場合、createメソッドはになりますregisterNew。そうでない場合は、おそらくsave代わりに呼び出します。リポジトリの目的は、ドメインとデータレイヤー間の抽象化を提供することです。このため、このメソッド(registerNewまたはsave)がEmployeeオブジェクトを受け入れ、リポジトリインターフェイスを実装するクラス次第であることが推奨されます。彼らはエンティティから取り出すことにしました。オブジェクト全体を渡す方が良いので、多くのオプションのパラメーターを持つ必要はありません。
質問2:両方の方法は、リポジトリインターフェイスの一部になり、単一の責任原則に違反しません。リポジトリの役割は、EmployeeオブジェクトにCRUD操作を提供することです。これは、オブジェクトが行うことです(読み取りと削除に加えて、CRUDは作成と更新の両方に変換されます)。もちろん、リポジトリを分割するなどしてさらにリポジトリを分割することもできますがEmployeeUpdateRepository、その必要はほとんどなく、通常は単一の実装にすべてのCRUD操作を含めることができます。
質問1:あなたは、Employee(他の属性の中でも)id を持つ単純なクラスになりました。IDが入力されているか空(またはnull)かは、オブジェクトが既に保存されているかどうかによって異なります。それにもかかわらず、idはまだエンティティが所有する属性であり、エンティティの責任Employeeはその属性を処理することであり、したがってそのidを処理します。
エンティティにIDがあるかどうかは、通常、永続ロジックを実行しようとするまで問題になりません。質問5の回答で述べたように、すでに保存されているエンティティを保存しようとしていないか、IDなしでエンティティを更新しようとしていないことを検出するのは、リポジトリの責任です。
重要な注意点
関心の分離は素晴らしいものの、実際に機能的なリポジトリ層を設計するのは非常に退屈な作業であり、私の経験では、アクティブレコードアプローチよりも適切に取得するのが少し難しいことに注意してください。しかし、最終的にははるかに柔軟でスケーラブルなデザインになります。これは良いことかもしれません。
Employee、抽象化を提供することを目的とする質問4.と5.は一般unanswerableあり、ニーズに依存し、次の2つのクラスに構造およびCRUD操作を分離した場合、それはかなり明確だ、のコンストラクタは、Employeeデータをフェッチすることはできませんdbから、それで6に