2016-06-17 2 views
2

最近私はDDDを探し始め、このパターンに古い個人プロジェクトをリファクタリングしています。私はエヴァンスの青い本の途中から来ていて、どこにいてもそこの答えが見つからないようです。DDD Repo-repo.findByChildId(id)AND repo.transferChild(to、from、child)

基本的に私のアプリケーションは在庫追跡システムです。インベントリにはアイテムのコレクションが含まれ、アイテムはインベントリ間の移転可能なエンティティです。在庫はtransferIn() transferOut()のようないくつかの検証論理を含む方法、すなわち在庫がまだ満杯でないか、または項目が移送可能状態にあるかをチェックする方法を有する。これらの制約は、在庫が総ルートであり、そのアイテムが実体であると私に信じさせます。

1)ある時点で、ユーザーが在庫に対して特定の商品エンティティをリクエストした場合、その商品を現在持っている在庫を返すinventoryRepo.findByItemId(id)があります。私ができるように:

サービスを通じて

2)のような何かを:

基本的に在庫のクラス(リッチドメインモデル)で私の検証ロジックを書い
boolean requestItemTransfer(destInvId, itemId){ 
    Inv from = invRepo.findByItemId(itemId); 
    Inv to = invRepo.findById(destInvId); 
    from.transferOut(itemId); 
    to.transferIn(from.getItem(itemId)); 
    return invRepo.transferChild(to, item); //Edited 
} 

と例外がない場合、私はレポを使用.transfer()メソッドを使用して変更を保持します。

私はDDDに違反しますか?より良い選択肢はありますか?

私が読んで理解したことから、これは慣習的でない場合に有効と思われます。私が見つけたすべての例は、1つのルートインスタンス内にしか存在できないエンティティを示しています。銀行口座振替の例もありますが、価値オブジェクトである金額を扱うものと、振替リポジトリがあるのは、転送が特定のシナリオで記録されるため、私のものではないからです。

EDIT: ユースケースは以下の通りです:

1)ユーザーが自分の在庫とその項目のリストを要求します。

2)ユーザーは1つの在庫から1つ以上のアイテムを選択し、別の在庫に送るように要求します。これは、私のTransferServiceが来て、指定されたインベントリからtxInとtxOutを調整し、それらの変更をリポジトリを通じて保持する場所です。多分それはインフラストラクチャサービスでなければなりませんか?それは私が明確ではない1つのことです。

3)ユーザは、それらのアイテムが現在存在する在庫の在庫に関して、在庫に転送できるようにしたいアイテムのセットを事前定義します。TransferServiceは、ユースケース2のように、 。

EDIT2: repo.transferについて これは実際には制約/最適化ですか?データ側から、私が言ったことは、アイテムを検索し、それが指し示すインベントリIDを変更することだけです。これは、アイテムを一度に2つのインベントリに入れることができないためです。だからrepo.update(fromInvInNewState)repo.update(toInvInNewState)の代わりに、repo.moveChild(toInv, child)があります。私たちは在庫の全状態(移動していないすべてのアイテムと、それ以外の状態はそれが持っているアイテムから派生しているためポイント)、いくつかのアイテムを動かすだけです。

答えて

1

まず最初に、シナリオで定義されたドメインモデルを要約したいと思います。

あなたは次の仕様とインベントリトラッカーを構築していることを言った:

  • ユーザー棚卸を持っています。
  • Iventoryはアイテムで構成されています。
  • ユーザーは、1つの在庫から別の在庫にアイテムを転送することができます。 「ユーザーが在庫とその商品のリストをリクエストしています。ユーザーが1つの在庫から1つ以上の商品を選択し、別の在庫に送信するよう依頼しています...」というメッセージが表示されます。一方

、ご指摘の不変は、以下のとおりです。InventoryBがない場合にのみ

  • 項目が別のインベントリ(InventoryB)に、それは既にあるインベントリー(InventoryA)から転送することができますすでに満員です。 Itemを転送できない場合は、InventoryAに保管する必要があります。
  • 私がよく理解していれば、ユーザーは自分のリポジトリ間でアイテムを転送します。

ような何か:今

class TransferItemService { 
    public function execute(TransferItemRequest request) 
    { 
     user = userRepository.findOfId(request.userId()); 
     user.transferItem(request.itemId(), request.fromInventoryId(), request.toInventoryId()); //Checks invariant -> the given Item is in one of his Inventories, the destination Inventory is owned by him, the destination Inventory is not full and finally transfers the Item 
     userRepository.save(user); 
    } 
} 

、集約ルート/秒を定義するために、私は私のビジネスは結果整合性に対処できるかどうかを確認する必要があります。つまり、Itemの移動が原子的に(1つの要求のみ)実行されなければならない場合、または(2つ以上の要求が)時間がかかる場合があります。

場合の事業ではありません結果整合性

は結果整合性は、あなたのドメインが一貫性を維持し、不変に並ぶようにしたい場合は、ユーザーは、彼のようにユニークAggregateRootになり、ここでは許可されていないことを言います彼の棚卸資産間のつながりです。この場合、すべてのインベントリをアイテムと共にロードするため、パフォーマンス上の問題に直面する可能性があります。 UserInventoryItem

あなたが最終的consitencyで行くことができる場合には、結果整合性

、あなたは次の集計ルーツを持つことができます。だから、アイテムを転送するユースケースをモデル化するために、以前のコードを使用して:

class User { 
    private string id; 
    private List<UserInventory> inventories; 

    public function transferItem(itemId, fromInventoryId, toInventoryId) 
    { 
    fromUserInventory = this.inventories.get(fromInventoryId); 
    if(!fromUserInventory) throw new InventoryNotBelongToUser(fromInventoryId, this.id); 

    toUserInventory = this.inventories.get(toInventoryId); 
    if(!toUserInventory) throw new InventoryNotBelongToUser(toInventoryId, this.id); 

    toUserInventory.addItem(itemId); 
    fromUserInventory.deletetItem(itemId); 
    } 
} 

class UserInventory { 
    private String identifier; 
    private int capacity; 

    public function deleteItem(userId, itemId) 
    { 
     this.capacity--; 
     DomainEventPublisher.publish(new ItemWasDeleted(this.identifier, itemId)); 

    } 

    public function addItem(userId, itemId) 
    { 
    if(this.capacity >= MAX_CAPACITY) { 
     throw new InventoryCapacityAlreadyFull(this.identifier); 
    } 
    this.capacity++; 

    DomainEventPublisher.publish(new ItemWasAdded(this.identifier, itemId)); 
    } 
} 

UserInventoryInventory集約ルートではないことに注意してください:この場合

class TransferItemService { 
    public function execute(TransferItemRequest request) 
    { 
     user = userRepository.findOfId(request.userId()); 
     user.transferItem(request.itemId(), request.fromInventoryId(), request.toInventoryId()); //Checks invariant -> the given Item is in one of his Inventories, the destination Inventory is owned by him, the destination Inventory is not full and finally transfers the Item 
     userRepository.save(user); 
    } 
} 

を、transferItem方法は次のようになります。識別子参照と現在の実際の容量があるVOだけですInventory

さて、あなたはasynchonously各インベントリを更新リスナー持つことができます:私は、我々はすべて私たちの不変を満たしていると思うミスを犯していない限り

class ItemWasRemovedListener() 
{ 
    public function handleEvent(event) 
    { 
    removeItemFromInventoryService.execute(event.inventoryId(), event.itemId()); 
    } 
} 

class ItemWasAddedListener() 
{ 
    public function handleEvent(event) 
    { 
    addItemToInventoryService.execute(event.inventoryId(), event.itemId()); 
    } 
} 

を、私たちは要求し、我々ごとに1つの集約ルートを変更しましたインベントリの操作を実行するためにすべてのアイテムをロードする必要はありません。

何か間違ったことがある場合は、私に知らせてください:D。

+0

ちょうどエヴァンスとヴァーノンの本を終えた。私はあなたの反応に同意しますが、別の質問が浮上しました。私はユーザーがARでありインベントリインスタンスの不変条件を処理することに同意します。問題は今、いくつかのインベントリがユーザ間で共有されていることです。チームには共通の在庫があり、各メンバーにはそれぞれ独自の在庫があるとします。したがって、フローはUser1がアイテムを共通インベントリに転送し、User2がアイテムを共通インベントリから取得します。エンティティインスタンスはそのような集約全体で共有できますか?それとも、私はチームをモデル化すべきですか?誰がtransfer()メソッドをホストしていますか? – jam01

+0

一般的な在庫をユーザー転送方法に渡すことはありますか? – jam01

2

少なくとも1つの集約がありません。永続性で置き換えようとしています。あなたのドメインエキスパートに相談し、、またはがこの転送を行っていることを確認してください。私はあなたがこれが "リポジトリ"または "データベース"によって行われているとは聞かないでしょう。これは何かが集計され、おそらくこのTransferメソッドがあります。この呼び出しは、transferIntransferOutからのログインをカプセル化します。これはトランザクション処理のようで、3つの異なる場所で実行しているためです。トランザクションの境界は集約されていることに注意してください。あなたのリポジトリではない。

+0

これは転送を行うインベントリです。そういうわけで、彼らはイン・アウト・メソッドを持っています。私はコーディネーターとしてサービスを使用しているので、在庫エンティティ内の永続性のために在庫レポを呼び出す必要はありません。レポ転送方式にはビジネスロジックはありません。 編集:ご回答いただきありがとうございます。ご質問にユースケースやワークフローの詳細を追加しました。うまくいけば助けてください。 – jam01

+1

コードがないので見えません。 'Transfer'はビジネス用語のように聞こえ、' Repository.Transfer(item、item) 'はビジネス操作を行う技術的オブジェクトのように見えます。私は間違っているかもしれないが、これはそれがどのように見えるかである。それでも、1つのトランザクションに2つの集約が関わっています。 –

+0

質問にいくつかの詳細を追加しました。そしてそのレポは永続性を扱うだけです。実際には、データ側からの制約です。私は、アイテムをルックアップし、それが指し示すインベントリIDを変更するだけであると言いました。ですから実際にはメソッドはrepo.txChildTo(dest、item) – jam01

関連する問題