web-dev-qa-db-ja.com

MVCフレームワークにDDDを実装する-PHP

mvcでは、モデルはレイヤーであり、すべてのドメインビジネスロジックが含まれています。
ドメイン駆動設計では、ビジネスロジックは次のようなさまざまな構成要素に分割できます。

ドメイン駆動設計では、ドメインモデルはです。

ドメインモデルは、知識、影響、または活動の領域(ドメイン)の選択された側面を記述する抽象化のシステムです。その後、モデルを使用して、そのドメインに関連する問題を解決できます。

開発者はドメイン駆動設計を読んだか、Doctrine2またはHibernateを使用しており、通常はDDDのドメインモデルに重点を置いています。mvcフレームワークでは、モデルレイヤーはDDDのドメインモデルと重複しています。つまり、のモデルフォルダーにドメインモデルを実装できます。 mvcフレームワーク

このような実装を以下に示します。モデルフォルダの構造を示します

   Model(this can model or domain)
   | 
   |----Entities
   |    |---BlogPost.php
   |    |---Comment.php
   |    |---User.php
   | 
   |----Repositories
   |    |---BlogPostRepository.php
   |    |---CommentRepository.php
   |    |---UserRepository.php
   | 
   |----Services
   |    |---UserService.php
   | 
   |----factories
   |    |---userfactory.php
   | 
   |----dataMappers
   |    |---userDataMapper.php // this inherit from Eloquent model
   | 
   |----ValueObject


  • 私の最初の仮定(mvcフレームワークのモデルフォルダーにドメインモデルを実装できる)が正しいことを知りたいですか?
  • entities、services、repositoriesのように、DDDのすべてのビルディングブロックがモデルフォルダー(上記のように)に実装するのは正しい設計ですか?
  • または、この実装に関してあなたが持っている他の提案。
  • これが間違っている場合、MVCフレームワークでエンティティ、サービス、リポジトリなどのDDDのビルディングブロックを実装する正しい方法は何ですか?
13
Susantha7

mvcでは、モデルはレイヤーであり、すべてのドメインビジネスロジックが含まれています。

MVCパターン自体がドメインについて何か特別なことを宣言しているとは思えません。モデルをプロパティのバッグとして操作し、モデルがどのように作成され、どのように不変条件を保護するかを気にしません。

同時に オニオンアーキテクチャ は、ドメインをアプリケーションサービス(MVCフレームワーク)から分離することが重要であると述べています。だから私はエンティティ値オブジェクト、を含むドメインレイヤーを配置するのが好きですドメインイベントおよびAggregatesを別のモジュールまたはトップレベルフォルダーに。

enter image description here

ドメインをMVCのものとは別に配置するもう1つの理由は、各コンテキストに独自のモジュール/フォルダーが必要なため、複数の 制限付きコンテキスト を簡単に管理できるようになることです。

this ASP MVC project 構造。有名なDDDエキスパートによって設計されました。ドメインの他に、MVC部分がどのようになっているのかを確認することをお勧めします。組織化されています。これは、最近ますます人気が高まっている 機能スライス アプローチを利用しており、非常に便利です。

8
Zharro

私はDDDの世界にまったく慣れていませんが、アプリケーションを徐々にDDD指向の構造に移行する過程で、ディレクトリ構造の問題にも直面しました。完全に概念的ではないことがわかった情報をつなぎ合わせて、次の簡略化されたディレクトリ構造(CRUD指向のLaravelアプリケーション)内に存在)を思いつきました。十分に:

app/
    ProjectName/
        Core/
            Application/
            Domain/
            Infrastructure/
        User/
            Application/
                Services/
                    CreateUserService.php
                    FindUserService.php
                    UpdateUserService.php
            Domain/
                Role.php
                RoleDAO.php
                User.php
                UserDAO.php
                UserNotCreated.php
                UserNotFound.php
                UserNotUpdated.php
                UserWasCreated.php
                UserWasUpdated.php
            Infrastructure/
                EloquentRoleDAO.php
                EloquentUserDAO.php

特定の懸念事項に対処するために、リポジトリインターフェイスとエンティティは、アプリケーションの分離可能な各コンポーネント(例:ユーザー)の下のドメインフォルダ内に配置されました。さらに、これは私がドメインイベントと例外を配置した場所です。リポジトリの実装は、各インフラストラクチャフォルダの下に配置されました。アプリケーションサービスは、アプリケーションディレクトリの下のサービスディレクトリ内に配置されます。

私自身のアプリケーションのハイブリッドな性質(ORMに依存するDAO /エンティティ、トランザクションスクリプトを使用し、いくつかの転換を示すために値オブジェクトを回避している)は別として、これは潜在的なDDDディレクトリの大まかなアイデアとして役立つ可能性がありますMVCアプリ内の構造。

4
alaric

@Zharroの提案に同意します。

良い構造は以下のようなものです:

  • ビュー(小枝、html、アセットなどのビュー部分のみが含まれます)
  • コア(コントローラー、フォーム、リスナー、ヘルパー)
  • BusinessLogic(サービスを含む)
  • エンティティ(エンティティ、コマンド、バリデーター)

1)ビューパーツアクセスはCoreBundleのみであり、データベースの動作は変更されません。

2)コアパーツアクセスBuisnessLogicおよびエンティティ

3)BuisnessLogicパーツアクセスのみエンティティ

4)エンティティ部分はデータベースにのみアクセスします。

1
Bhavin Nakrani

最初の仮定は正しくありません。MVCは実際にはDDDに適合しません。より良いアプローチは、CQRSパターンを使用することです。

2番目の仮定も正しくありません。ビルディングブロックがすべてドメインモデルフォルダーにあるわけではありません。実際、プロジェクトに適した構造は次のとおりです。

ProjectName/
    Application/
        Blog/
            Command/
                CommentToBlogPostCommand.php
                ChangeCommentContent.php
                DescribeBlogPostCommand.php
                NewBlogPostCommand.php
                ...
            Data/
                BlogPostData.php
                BlogPostCommentsData.php (POPO containing BlogPost infos and the comments array)
                CommentData.php (Single comment infos)
            BlogPostApplicationService.php
            BlogPostQueryService.php
            CommentApplicationService.php
            CommentQueryService.php
        Identity/
            Command/
                AuthenticateUserCommand.php
                ChangeEmailAddressCommand.php
                ChangeUserPasswordCommand.php
                ChangeUserPersonalNameCommand.php
                DefineUserEnablementCommand.php
                RegisterUserCommand.php

            UserApplicationService.php (this defines the actions that can be done by your application related to user domain, injected in presentation layer responding to user events)
            UserQueryService.php (this will usually be injected in your presentation layer)
    Domain/
        Model/
            Blog/
                BlogPost.php
                BlogPostClosed.php (this could be a list of possible events)
                BlogPostDescriptionChanged.php
                BlogPostModeratorChanged.php
                BlogPostReopened.php
                BlogPostStarted.php
                BlogPostRepository.php (interface)
                Comment.php (this is an Entity, or Aggregate Root)
                CommentContentAltered.php (this could be an event)
                CommentAuthor.php (this could be a ValueObject, containing the username)
                CommentRepository.php (interface)
                CommentedToBlogPost.php (this could be another event when adding a comment to a blogpost)
                ...
            Identity/
                ContactInformation.php (VO or Person)
                Enablement.php (VO of User)
                EmailAddress.php (VO of ContactInformation)
                FullName.php (VO or Person)
                Person.php (ValueObject of User)
                User.php (constructor visibility might be package-protected)
                UserFactory.php
                UserRepository.php (this is an interface)
                UserService.php (this is a domain service)
    Infrastructure/
        Persistence/
            LavarelBlogPostRepository.php (this implements BlogPostRepository)
            LavarelCommentRepository.php (this implements CommentRepository)
            LavarelUserRepository.php (this implements UserRepository)
    Interfaces/
        ...

そうすれば、疑似MVCを維持できますが、ViewとControllerがInterfacesパッケージにあり、RichModelがdomain/modelパッケージにあるという違いがあります。モデルを操作できるのは、アプリケーションサービスを介して、およびクエリサービスを介してモデルをクエリすることだけです。クエリサービスは、モデル表現への高速アクセスを提供し、コマンドはアプリケーションサービスに送信されてコントローラーとして動作します。

CommentAuthorクラスは、データベースのユーザーIDではなく、一意のユーザー名を含む値オブジェクトである可能性があることに注意してください。ユーザー集約ルートは別のパッケージからのものであるため、これはドメインPointOfViewから意味があります。これをID(またはユーザー名)と呼ぶことができます。これは、理想的にはUserテーブルの一意の列にマップされますが、Commentテーブルのインデックス値になります。

別のオプションは、ブログである同じ概念の一部としてブログパッケージのユーザーを使用することですが、DDDはこのアプローチを推奨していません。実際には、IdentityとAccessを別の制限されたコンテキストに配置することをお勧めしますが、作成しているアプリケーションのコンテキストでは、コメントの一部としてユーザーをマッピングしても問題ない可能性があります。

インフラストラクチャ層では、永続性プロバイダーを定義します。そうすれば、Doctrineに切り替えたい日に、このパッケージの実装のみを変更する必要があります。

アプリケーション層は、セキュリティの管理、トランザクションコンテキストのスパン、および高レベルのイベントのログ記録を担当します。

いくつかの説明が必要な場合は、クラスの内部についてより多くの洞察を提供できます。また、これを機能させるには、インフラストラクチャまたはサポートフレームワークが必要になる場合があります。

  • 依存性注入、
  • エンティティで利用可能なイベントディスパッチャ、
  • これらのイベントを消費する予定がある場合は、ある種のイベントバス。
0
Sylvain Lecoy

MVCのモデルは、ViewModelとコマンドの組み合わせであると考えています。着信要求は、適切な集計を取得してその上で適切なメソッドを呼び出し、トランザクションをコミットする「書き込み」レイヤーにディスパッチされるコマンドにマップされます。

ViewModelは、ユーザーインターフェイス用のプレゼンテーション対応形式でデータを伝送するためだけに存在します。データベースビューまたはテーブルにクエリを実行し、結果をクライアントに返す非常に単純な「読み取り」レイヤーを作成できます。これにより、ゲッターとセッターを介してすべての状態を公開しないドメインモデルを作成できます。

0
pnschofield