個人的に、ドメイン駆動設計ほど銀の弾丸は存在しないを理解させてくれる設計だったりします。
なんの策もなく実装をするのは危険ですが、あまりこだわりすぎてしまうと、逆にややこしくしてしまうのがドメイン駆動設計なのです。
その意見は、この本を読んでも変わりません。
ドメインオブジェクトを公開すると多くの危険性があるので、DTOを用いる
まあ、ドメインオブジェクトを公開すると影響範囲が広くなるので、DTOを使うというものです。
public class UserApplicationService { // 略 public UserData Get(string userId) { var targetId = new UserId(userId); var user = _userRepository.Find(targetId); // ドメインオブジェクトを直接公開すると多くの危険性があるのでDTOを用いてやりとりする return new UserData(user); } } public class UserId { // 略 } public class UserName { // 略 } public class UserData { // 略 }
と解説されていますが、個人的な経験上、こういうことに厳密になると、似たようなクラスがいっぱいになってしまい、新しいプロパティを追加するのに、1つではなく、2つ、3つクラスを修正しなければならないので、あまり好きではないです。
本書では、その手間を省くためにドメインオブジェクトを指定するとDTOとなるクラスコードを生成するツール作るとよいとしています。
コマンドオブジェクト
ユーザーを更新するとき、Idは必須で、名前やメールアドレスは任意であることを示す場合。
引数で表現をすると煩雑であるため、コマンドオブジェクトを用いる戦術。これは初耳でしたね。
public class UserUpdateCommand { public UserUpdateCommand(string id) { Id = id; } public string Id { get; set; } public string Name { get; set; } public string MailAddress { get; set; } } public class UserApplicationService { // 中略 public void Update(UserUpdateCommand command) { var targetId = new UserId(command.Id); var user = _userRepository.Find(targetId); if (user == null) { throw new UserNotFoundException(targetId); } if (command.Name != null) { var newUserName = new UserName(command.Name); user.Name.ChangeName(command.Name); if (_userService.Exists(user)) { throw new CanNotRegisterUserException(user, "ユーザーはすでに存在しています。"); } } _userRepository.Save(user); } }
ソリューション構成
ドメイン層とアプリケーション層だけはプロジェクトを一緒にして、インフラストラクチャ層などは別々にするそうです。
私の経験したプロジェクトではドメイン層とアプリケーション層も分けてました。
一緒にできるなら一緒にした方がいいのが個人的な感想です。プロジェクトが多すぎると管理が煩雑ですので。