DDDのトランザクション管理について


DDDではトランザクション管理をアプリケーション層で行うという認識でいます。

例えばUserとCompanyという2つのドメインモデルが存在し、  
それぞれマスタ画面でDBへの登録処理があるとします。  
登録処理をアプリケーションサービスクラスで行うときは

[code]

    class UserRegisterService {
        public function register(User $user)
        {
            // トランザクション開始
            try {
                // ユーザー登録処理
    　          $this->userRepo->register($user);
                commit
            } catch {
                rollback
            }
        }
    }
    
    class CompanyRegisterService {
        public function register(Company $company)
        {
            // トランザクション開始
            try {
                // 会社登録処理
                $this->companyRepo->register($company);
                commit
            } catch {
                rollback
            }
        }
    }
    
[/code]

のように書くのではないかと思っています。

ここで更にUserとCompanyの2つを同じタイミングで登録する業務Xが存在した場合  
Xサービスクラスで「UserRegisterService」と「CompanyRegisterService」  
を呼んでしまうと、Userを登録したあとCompanyの登録でエラーが発生しても  
rollbackするのは、当然CompanyServiceだけということになってしまいます。

かといって、Xサービスクラスで2つのサービスクラスと全く同じ処理を書いても  
冗長なコードになってしまいます・・・。  
こういった場合どうすればよいのでしょうか？

## 回答を受けての追記

あとわいと様、Vietta Toe様

サンプルのコードがだいぶ省略していて大変申し訳ないのですが  
実際のクエリの発行はRepositoryというクラスが行うようになっています。（サンプルコードを編集しておきました）  
つまり以下のような処理の流れになります。  
View → Controller → Service → Repository → DB

サンプルのコードぐらいのシンプルなものだと  
Xサービスクラスのregisterメソッドの中で、userRepoとcompanyRepoを呼び出せばいいだけの話だと思うのですが  
実際の業務では登録をするためのデータの加工などをサービスクラス内で行った後、  
加工したデータを引数にリポジトリを呼び出すということが多々あります。

その場合、Xサービスクラスのregisterメソッドで、データ加工処理を書いてしまうと  
XサービスとUserサービスで同じ処理を書いてしまうことになってしまいます。

なので、できるだけサービスクラスを呼び出すだけにしたいのですが  
このような場合でもサービスクラスの上のレイヤーにトランザクション管理を任せればよいのでしょうか？

その際、Vietta Toe様のご指摘のとおり、コントローラーでトランザクション管理をするのか、  
それとも、コントローラーとサービスの間に1つレイヤーを作り、そこでトランザクションを行うのか  
どうすればよいでしょうか？

説明が下手で申し訳ありません。伝わればいいのですが。。。




ＤＤＤ自体に知見がないので、一般論で

エンティティにたいする処理の分割が不十分なことに起因していませんか？

例では、クエリーの発行とトランザクションの開始、終了が同一のレベルで取り扱われているので、お困りのようなことになります。トランザクションmanagerはトランザクションスコープを管理するべきです。例の場合はregisterの関数ごとにスコープをとらえています。スコープについての方針が間違いなければこれでもいいのですが、実際にお困りなので、スコープが適切ではなかったということです。  
やり様としては、トランザクション管理する一段上のクラスを作ること等があります。(通常、フレームワークではこういう存在が定義されています。)  
トランザクションmanagerで、トランザクションを開始し、query発行をここで言うregisterで行う(呼び出す)、なにも問題なければトランザクションmanagerがコミットする。勿論ロールバックの責任もこいつが一括します。という感じです。

