2012-05-13 4 views
5

私はEvans、Nilsson、McCarthyを読んで、ドメイン駆動型デザインの背後にある概念と推論を理解しています。しかし、私は実際のアプリケーションにこれらをまとめて入れるのは難しいと思っています。完全な例がないと、私の頭が傷ついてしまった。私は多くのフレームワークと簡単な例を見つけましたが、DDDの後に実際のビジネスアプリケーションを構築する方法を実際に示すものはこれまでにありません。ドットをDDDと接続する

例として典型的な受注管理システムを使用して、受注取り消しのケースを取る。私の設計では、オーダー番号と理由をパラメータとして受け入れるCancelOrderメソッドでOrderCancellationServiceを見ることができます。その後、以下の「手順」を実行することがあります。

  1. 現在のユーザーがいることを確認しOrderRepository
  2. から指定された順序番号と
  3. をOrderエンティティを取得注文
  4. をキャンセルするために必要な権限を持っていることを確認します(サービスがルールを評価するために注文の状態を問い合わせるか、または注文がルールをカプセル化するCanCancelプロパティを持っているかどうか確認する必要があります)
  5. Order.Cancelを呼び出すことによってOrderエンティティの状態を更新します)
  6. 更新を維持するすでに
  7. は、このすべては、トランザクション内で起こるべき、もちろん操作

の監査エントリを追加します。処理されたすべてのクレジットカードの使用を元に戻すために、データストア

  • 問い合わせCreditCardServiceにDの注文独立して動作することを許されるべきではない。つまり、注文をキャンセルした場合、キャンセルすることはできません。この手順を実行しないと、クレジットカードの取引を元に戻す必要があります。これは、より良いカプセル化を示唆していますが、私は自分のドメインオブジェクト(Order)のCreditCardServiceに依存したくないので、これはドメインサービスの責任のようです。

    私は、これがどのように「組み立てられるか」、どのようにコード例が示されるかを探しています。コードの背後にある思考プロセスは、自分ですべての点を結び付けるのに役立ちます。どうも!

  • 答えて

    2

    ドメインサービスは次のようになっています。ドメイン・サービスを薄くして、できるだけ多くの論理をエンティティーに保持したいということに注意してください。また、クレジットカードまたは監査人の実装(DIP)に直接の依存性はないことに注意してください。私たちはドメインコードで定義されているインターフェースにのみ依存しています。実装は後でアプリケーション層に注入することができます。アプリケーション層はまた、番号順の検索、さらに重要なことに、トランザクション内での「キャンセル」コールの折り返し(例外時のロールバック)を担当します。

    class OrderCancellationService { 
    
        private readonly ICreditCardGateway _creditCardGateway; 
        private readonly IAuditor _auditor; 
    
        public OrderCancellationService(
         ICreditCardGateway creditCardGateway, 
         IAuditor auditor) { 
         if (creditCardGateway == null) { 
          throw new ArgumentNullException("creditCardGateway"); 
         } 
         if (auditor == null) { 
          throw new ArgumentNullException("auditor"); 
         } 
         _creditCardGateway = creditCardGateway; 
         _auditor = auditor; 
        } 
    
        public void Cancel(Order order) { 
         if (order == null) { 
          throw new ArgumentNullException("order"); 
         } 
         // get current user through Ambient Context: 
         // http://blogs.msdn.com/b/ploeh/archive/2007/07/23/ambientcontext.aspx 
         if (!CurrentUser.CanCancelOrders()) { 
          throw new InvalidOperationException(
           "Not enough permissions to cancel order. Use 'CanCancelOrders' to check."); 
         } 
         // try to keep as much domain logic in entities as possible 
         if(!order.CanBeCancelled()) { 
          throw new ArgumentException(
           "Order can not be cancelled. Use 'CanBeCancelled' to check."); 
         } 
         order.Cancel(); 
    
         // this can throw GatewayException that would be caught by the 
         // 'Cancel' caller and rollback the transaction 
         _creditCardGateway.RevertChargesFor(order); 
    
         _auditor.AuditCancellationFor(order); 
        } 
    } 
    
    +0

    なぜサービス内の変更を維持するために「ルックアップ」、トランザクション管理、呼び出しが必要なのでしょうか?それは毎回適切な使用を保証すると思われる。 – SonOfPirate

    +0

    ルックアップ - 多分。トランザクション管理はドメインサービスに属しません。通常、アプリケーション層(ドメインサービスの呼び出し元)で実装されます。私たちが既存のオブジェクトを変更しているので、NHibernateのケースで明示的な呼び出しが必要ではないので、 '変更を永続化するための呼び出し'はORMまたはUnitOfWorkによって処理されます。考え方は、ドメインコードを永続性に不可欠なものにすることです。 – Dmitry

    +0

    さて、OrderRepositoryとUoWを使用して、ドメインを永続性に不可欠なものにすることは可能ですが、Orderエンティティへの変更を永続させることなくアプリケーションコードがキャンセルサービスを呼び出すことはできません。私がNHibernateを使用していないことは今まで重要視されていなかったので、ORMに基づく仮定は有効ではありません。 – SonOfPirate

    2

    Aその上にわずかに異なるテイク:

    //UI 
    public class OrderController 
    { 
        private readonly IApplicationService _applicationService; 
    
        [HttpPost] 
        public ActionResult CancelOrder(CancelOrderViewModel viewModel) 
        { 
         _applicationService.CancelOrder(new CancelOrderCommand 
         { 
          OrderId = viewModel.OrderId, 
          UserChangedTheirMind = viewModel.UserChangedTheirMind, 
          UserFoundItemCheaperElsewhere = viewModel.UserFoundItemCheaperElsewhere 
         }); 
    
         return RedirectToAction("CancelledSucessfully"); 
        } 
    } 
    
    //App Service 
    public class ApplicationService : IApplicationService 
    { 
        private readonly IOrderRepository _orderRepository; 
        private readonly IPaymentGateway _paymentGateway; 
    
        //provided by DI 
        public ApplicationService(IOrderRepository orderRepository, IPaymentGateway paymentGateway) 
        { 
         _orderRepository = orderRepository; 
         _paymentGateway = paymentGateway; 
        } 
    
        [RequiredPermission(PermissionNames.CancelOrder)] 
        public void CancelOrder(CancelOrderCommand command) 
        { 
         using (IUnitOfWork unitOfWork = UnitOfWorkFactory.Create()) 
         { 
          Order order = _orderRepository.GetById(command.OrderId); 
    
          if (!order.CanBeCancelled()) 
           throw new InvalidOperationException("The order cannot be cancelled"); 
    
          if (command.UserChangedTheirMind) 
           order.Cancel(CancellationReason.UserChangeTheirMind); 
          if (command.UserFoundItemCheaperElsewhere) 
           order.Cancel(CancellationReason.UserFoundItemCheaperElsewhere); 
    
          _orderRepository.Save(order); 
    
          _paymentGateway.RevertCharges(order.PaymentAuthorisationCode, order.Amount); 
         } 
        } 
    } 
    

    注:コマンド/ユースケースが関与する場合、私は唯一のドメインサービスの必要性を参照してください一般的に

    • 複数の集合の状態変化。たとえば、Customer集約とOrderでメソッドを呼び出す必要がある場合は、両方の集約のメソッドを呼び出すドメインサービスOrderCancellationServiceを作成します。
    • アプリケーション層は、インフラストラクチャ(支払いゲートウェイ)とドメインの間を調整します。ドメイン・オブジェクトと同様に、ドメイン・サービスはドメイン・ロジックにのみ関係し、支払いゲートウェイなどのインフラストラクチャーは認識していません。たとえ自分のアダプターを使って抽象化したとしても。
    • パーミッションに関しては、これをロジック自体から抽出するためにaspect oriented programmingを使用します。私の例で見てきたように、CancelOrderメソッドに属性を追加しました。現在のユーザー(Thread.CurrentPrincipalに設定されている)がそのアクセス許可を持っているかどうかを調べるには、そのメソッドでインターセプターを使用できます。
    • 監査に関して、単に「操作の監査」と言っただけです。一般的な監査(つまり、すべてのアプリサービス呼び出し)の場合は、メソッドに対してインターセプタを使用し、ユーザーをログに記録し、どのメソッドが呼び出されたか、どのパラメータを使用するかを指定します。しかし、あなたが注文/支払いの取り消しのために特別に監査することを意味していたなら、ドミトリーの例に似た何かをしてください。
    +0

    +1アプリケーションとドメインの間の明確な区別 –

    +1

    その特定のリソース(orderId)のuserpermissionはどうですか? –

    +0

    私はビジネスルールのように扱い、他のルールと同様にコード内のチェックを行います –

    関連する問題