これは、ここからの私の質問のフォローアップです。
まず、プロジェクトでDDDを使用していません。
3つのレイヤーを持つWCFサービスがあります。
- サービスレイヤー(操作のみを保持し、BLメソッドを呼び出します)
- すべてのビジネスロジッククラスとメソッドを保持するビジネスロジックレイヤー
- DbContext (LINQ-TO-EF)およびPOCOエンティティを保持するデータアクセス層
WCFサービスはDTOオブジェクトを返す必要があり、POCOエンティティをDTOに変換する「Translator」クラスを配置するのに最適な場所を見つけるのに苦労しています。
これには2つのオプションがありました。
。
。
方法A
ビジネスロジックメソッドがエンティティをサービスレイヤーに返すようにします。サービスレイヤーには、エンティティをDTOに変換するトランスレータークラスが含まれています。
長所:
- ビジネスロジックレイヤーは、必要なことを実行します-検証とCRUD操作
- ビジネスロジック層は、DTOについてまったく知る必要はありません
短所:
- サービス層は、ビジネスロジック層からエンティティを受け取るため、「データアクセス層」アセンブリへの参照を含める必要があります。これは、サービス層がBL層のみを参照する必要があり、BL層がDALのみを参照する必要があるという3層の概念を破っているようです。
- これは最悪の問題です。トランスレータクラスはエンティティオブジェクトからDTOを作成する必要があります。DbContextが破棄された後、BLからエンティティオブジェクトを受け取るため、「Include」拡張機能がロードされていないものにはアクセスできません。つまり、BLメソッドは、DTOを作成するためにトランスレータが必要とするすべてのものとともに、エンティティをサービスレイヤーに戻す必要があります。BLはトランスレータが何を必要としているかを知る必要があるため、これは問題です。次に、データベースから多くの不要なデータをフェッチします。(おそらく、トランスレータは、フィールドの1つが「注文の総数」である「UserDto」オブジェクトを返す必要があります。「Count()」を作成するためだけに、データベースからすべての注文をフェッチする必要があるのはなぜですか。
。
。
方法B
エンティティオブジェクトからDTOに変換する「Translator」クラスを「ビジネスロジックレイヤー」自体に配置します。このメカニズムでは、BLメソッドはすでにDTOを返します。
長所:
- BLメソッドはBLコードを実行してから、「translate_to_dto」適切なメッセージを呼び出して、結果をDTOに変換して返します。これはすべて「DbContext」内で実行されます。つまり、トランスレータクラスを呼び出してエンティティを変換する場合でも、子オブジェクトにアクセスでき、「Include」を呼び出す必要はありません。これは、DTOの作成に必要なデータのみがデータベースからフェッチされることを意味します。
短所:
- これで、「ビジネスロジック層」にある「translator」クラスがDTOについて知る必要がありますが、DTOについてのみ知るのはサービス層の責任です。
- BLの各メソッドは、純粋なBL(有効性チェック、CRUD操作など)を実行し、さらにDTOを返すためにtranslatorメソッドを呼び出します。これは、(BL内の)メソッドが1つの特定のことだけを実行する必要があるという「単一責任ルール」に違反します。
。
'Entity ==> DTO'変換を実行するのに適切な場所を教えてもらえますか?
。
[更新-例を追加]
ビジネスロジック層には、次のようなBLメソッドを持つUserManagerというマネージャークラスがあります。
public UserTasksDto GetUserInfoWithTasks(Guid userId)
{
if (userId == Guid.Empty)
throw new ArgumentException("User ID cannot be empty");
using (IMyDBEntities entities = _contextFactory.GetContext())
{
// Get POCO Object from DbContext
User user = entities.Users.Find(userId);
if (user == null)
throw new EntityNotFoundException("User was not found in the database");
if (user.Tasks.Count() == 0)
throw new Exception("User does not have any tasks !");
// Call 'Translator' static method to translate POCO to DTO
Translator.TranslateUserToUserTasksDto(user);
}
}
上記のように、BLメソッドは「translator」メソッドを呼び出してPOCOをDTOに変換します。これは「エンティティ」コンテキスト内で行われるため、翻訳者は引き続きユーザーの「タスク」の子にアクセスできます。
'Translator'メソッドは次のようになります。
class Translator
{
public static UserTasksDto TranslateUserToUserTasksDto ( User userPoco )
{
UserTasksDto dto = new UserTasksDto
{
UserId = userPoco.Id,
Username = userPoco.Username,
CreationDate = userPoco.CreationDate,
// Accessing a related entity, this is why this 'translate' method
// needs to be called inside the DbContext, otherwise it will except
// (or we load all related entities using 'Include' just for the 'Count' purpose)
Supervisor = userPoco.Supervisor.Username,
NumOfTasks = userPoco.Tasks.Count(),
FirstTaskDate = userPoco.Tasks.OrderBy(task => task.Date).Take(1),
}
return dto;
}
}
上記のように、「Translate」メソッドは「User」POCOオブジェクトから「UserTasksDto」を構築します。これは、「User」オブジェクトとその関連エンティティからDTOにいくつかのフィールドをマッピングすることによって行われます。このメソッドがBLメソッドのObjectContext内になかった場合、コンテキストなしでエンティティにアクセスしようとしているという例外が発生します。
私の問題が今より明確になることを願っています...