CQRSずむベント゜ヌシングを䜿甚した倉庫管理システム。 サヌビス局





この蚘事では、Magento 2のサヌビスレむダヌず、゚ンティティを管理するためのサヌビスAPIに぀いお説明したす。これらは、倉庫管理システムむンベントリのドメむン゚ンティティの蚭蚈ず割り圓おに関する以前の蚘事で説明したした。



サヌビス局



Magento 2プラットフォヌムで倉庫管理システムを蚘述しおいるため、それに応じお、このプラットフォヌムの機胜を考慮しお、玹介するサヌビスに぀いお説明したす。

Magento 2では、モゞュヌルレベルバりンドコンテキスト内で匱い接続の原則を実装するために、サヌビスレむダヌが導入されたした。これは、クラむアントず他のシステムモゞュヌルの盞互䜜甚に関しお各モゞュヌルで利甚可胜な操䜜のセットを定矩したす。



Magentoのサヌビスレむダヌたたはサヌビスコントラクトは 、モゞュヌルに察しお定矩され、このモゞュヌルのApiフォルダヌにあるPHPむンタヌフェむスのセットです。 サヌビスコントラクトは、デヌタむンタヌフェヌス-ドメむン゚ンティティデヌタを衚すDTOむンタヌフェヌスで構成されおいたす。 およびサヌビスむンタヌフェむス-クラむアントコントロヌラヌ、REST / SOAP Webサヌビス、他のモゞュヌルのPHPコヌドから呌び出すこずができるビゞネスロゞックぞのアクセスを提䟛するむンタヌフェむス。



モゞュヌルのすべおの倖郚クラむアントは、サヌビス局で蚘述された契玄の䞋で動䜜するず想定されおいるため、サヌビス局は実際にはFacadeずしお衚すこずができ、実装の詳现ずビゞネスロゞックの耇雑さを隠したす。

お客様は、明確に定矩されたAPIに䟝存するこずで、モゞュヌルがセマンティックバヌゞョニングに埓うため、システムの次のバヌゞョンぞのアップグレヌドが容易になりたす。



モゞュヌル性ず分離を改善するために、サヌビスコントラクトがサヌビスから別のモゞュヌルに割り圓おられる堎合がありたす。 たずえば、Inventoryの堎合、2぀のモゞュヌルがありたす。1぀はInventoryAPIサヌビスむンタヌフェむスのセットを宣蚀し、2぀目はこれらのむンタヌフェむスの実装を提䟛したす-Inventoryです。 したがっお、基本実装を眮き換えたいサヌドパヌティの開発者は、コヌド内でこの実装に瞛られなくなりたす。 システム内の他のモゞュヌルが䟝存するむンタヌフェむスであるため、圌が必芁ずするのはむンタヌフェむスだけです。



リポゞトリむンタヌフェむス-リポゞトリ



リポゞトリは、゚ンティティに䞀連のCRUD操䜜を提䟛するむンタヌフェむスです。

兞型的なリポゞトリむンタヌフェむスは、次のメ゜ッドのセットで構成されおいたす。

public function save(\Magento\Module\Api\Data\DataInterface $entityData); public function get($entityId); public function delete(\Magento\Module\Api\Data\DataInterface $entityData); public function deleteById($entityId); public function getList(SearchCriteriaInterface $searchCriteria);
      
      





メ゜ッドのセットは、定矩枈みのセットずは異なるセマンティクスを持぀メ゜ッドを远加するこずは掚奚されないため、より狭い堎合がありたす特定の操䜜がドメむン゚ンティティに䞀般的でない堎合。削陀など。 このような方法は、別のサヌビスに配眮するこずをお勧めしたす。



リポゞトリは、゚ンティティを管理するための䞀連のメ゜ッドを組み合わせたファサヌドず考えるこずができたす。



Inventoryモゞュヌルのコンテキストでは、 Source゚ンティティ補品がある物理的な倉庫を衚す責任を負う゚ンティティおよびSourceItem ゚ンティティバンドル、特定の物理ストレヌゞ䞊の特定の補品SKUの量を衚すのリポゞトリが衚瀺されたす。



 /** * This is Facade for basic operations with Source * There is no delete method, as Source can't be deleted from the system because we want to keep Order information for all orders placed. Sources can be disabled instead. * * Used fully qualified namespaces in annotations for proper work of WebApi request parser * * @api */ interface SourceRepositoryInterface { /** * Save Source data * * @param \Magento\InventoryApi\Api\Data\SourceInterface $source * @return int * @throws \Magento\Framework\Exception\CouldNotSaveException */ public function save(SourceInterface $source); /** * Get Source data by given sourceId. If you want to create plugin on get method, also you need to create separate * plugin on getList method, because entity loading way is different for these methods * * @param int $sourceId * @return \Magento\InventoryApi\Api\Data\SourceInterface * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function get($sourceId); /** * Load Source data collection by given search criteria * * @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria * @return \Magento\InventoryApi\Api\Data\SourceSearchResultsInterface */ public function getList(SearchCriteriaInterface $searchCriteria = null); }
      
      





SourceRepositoryの堎合、deleteメ゜ッドはありたせん。Source゚ンティティに察するビゞネスオペレヌションは存圚しないためです。なぜなら、発泚された泚文商品の配送元を含むに関連するすべおの情報を垞に保存する必芁があるからです。 したがっお、将来的にそのようなデヌタが倱われる可胜性を防ぐ必芁がありたす配信が実行された゜ヌスを削陀するこずにより。 代わりに、操䜜が䜿甚されたす-゜ヌスを非アクティブ無効ずしおマヌクしたす。



 /** * This is Facade for basic operations with SourceItem * * The method save is absent, due to different semantic (save multiple) * @see SourceItemSaveInterface * * There is no get method because SourceItem identifies by compound identifier (sku and source_id), * thus, it's needed to use getList() method * * Used fully qualified namespaces in annotations for proper work of WebApi request parser * * @api */ interface SourceItemRepositoryInterface { /** * Load Source Item data collection by given search criteria * * We need to have this method for direct work with Source Items, as Source Item contains * additional data like qty, status (can be searchable by additional field) * * @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria * @return \Magento\InventoryApi\Api\Data\SourceItemSearchResultsInterface */ public function getList(SearchCriteriaInterface $searchCriteria); /** * Delete Source Item data * * @param SourceItemInterface $sourceItem * @return void * @throws \Magento\Framework\Exception\NoSuchEntityException * @throws \Magento\Framework\Exception\CouldNotDeleteException */ public function delete(SourceItemInterface $sourceItem); }
      
      





保存操䜜を䜿甚するための䞻なシナリオは、リポゞトリ内の暙準的な保存芏玄が想定しおいるように、1぀の゚ンティティではなくSourceItemのセットで発生するためです。

シンクのむンポヌトたたは倖郚ERPたたはPIMシステムずの同期䞭に発生する可胜性のある耇数の保存に぀いおは、個別のSourceItemSaveInterfaceコントラクトが導入され、単䞀のサヌビスコヌル内で耇数のSourceItemをアトミックに保存する機胜を提䟛したす。 このようなコントラクトにより、デヌタベヌスぞの単䞀のク゚リを䜿甚しお挿入操䜜を凊理でき、凊理が倧幅に高速化されたす。 単䞀の゚ンティティを䜿甚する基本的な保存操䜜は、実際にはこの堎合サヌドパヌティの開発者が䞡方の保存操䜜単䞀および耇数をプラグむンする必芁があるため、リポゞトリコントラクトに远加されたせん。 したがっお、1぀の拡匵ポむントのカスタマむズが垞に望たしいようです。



耇数の保存コマンドコントラクトは、 Magento \ InventoryApi \ Api \ SourceItemSaveInterfaceのようになりたす



 /** * Service method for source items save multiple * Performance efficient API, used for stock synchronization * * Used fully qualified namespaces in annotations for proper work of WebApi request parser * * @api */ interface SourceItemSaveInterface { /** * Save Multiple Source item data * * @param \Magento\InventoryApi\Api\Data\SourceItemInterface[] $sourceItems * @return void * @throws \Magento\Framework\Exception\InputException * @throws \Magento\Framework\Exception\CouldNotSaveException */ public function execute(array $sourceItems); }
      
      





その実装であるSourceItemSaveは、 SaveMultipleモデルリ゜ヌスの保存を委任したす。



たた、SourceItemはリンクされた゚ンティティであり、耇合識別子SKUずSourceIdによっお決定されるため、SourceItemRepositoryにはgetメ゜ッドはありたせん。



Stock゜ヌス゚ンティティの仮想集玄のリポゞトリは暙準に芋えたす



 interface StockRepositoryInterface { /** * Save Stock data * * @param \Magento\InventoryApi\Api\Data\StockInterface $stock * @return int * @throws \Magento\Framework\Exception\CouldNotSaveException */ public function save(StockInterface $stock); /** * Get Stock data by given stockId. If you want to create plugin on get method, also you need to create separate * plugin on getList method, because entity loading way is different for these methods * * @param int $stockId * @return \Magento\InventoryApi\Api\Data\StockInterface * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function get($stockId); /** * Find Stocks by given SearchCriteria * * @param \Magento\Framework\Api\SearchCriteriaInterface|null $searchCriteria * @return \Magento\InventoryApi\Api\Data\StockSearchResultsInterface */ public function getList(SearchCriteriaInterface $searchCriteria = null); /** * Delete the Stock data by stockId. If stock is not found do nothing * * @param int $stockId * @return void * @throws \Magento\Framework\Exception\CouldNotDeleteException */ public function deleteById($stockId); }
      
      





゜ヌスずストックをマッピングするためのサヌビス



クラむアントAPIビゞネスロゞックコヌド内のボむラヌプレヌトコヌドの数を枛らすために、「 クラむアントにできるこずを䜕もさせない 」ずいうルヌルに埓っお、デヌタむンタヌフェヌスSourceStockLinkInterfaceを導入したせん 。 代わりに、SourceをStockに割り圓おるための䞀連のドメむンベヌスのサヌビスコマンドを導入したす。



その結果、3぀のコマンドが取埗されたす。



 interface AssignSourcesToStockInterface { /** * Assign list of source ids to stock * * @param int $stockId * @param int[] $sourceIds * @return void * @throws \Magento\Framework\Exception\InputException * @throws \Magento\Framework\Exception\CouldNotSaveException */ public function execute(array $sourceIds, $stockId); } interface GetAssignedSourcesForStockInterface { /** * Get Sources assigned to Stock * * @param int $stockId * @return \Magento\InventoryApi\Api\Data\SourceInterface[] * @throws \Magento\Framework\Exception\InputException * @throws \Magento\Framework\Exception\LocalizedException */ public function execute($stockId); } interface UnassignSourceFromStockInterface { /** * Unassign source from stock * * @param int $sourceId * @param int $stockId * @return void * @throws \Magento\Framework\Exception\InputException * @throws \Magento\Framework\Exception\CouldNotDeleteException */ public function execute($sourceId, $stockId); }
      
      





API vs SPI



このプロゞェクトのフレヌムワヌク内で、拡匵機胜を改善し、コンポヌネントの接続性を枛らすために、 サヌビスプロバむダヌむンタヌフェむス SPIからAPIアプリケヌションプログラミングむンタヌフェむスを明瀺的に分離するこずが決定されたした。





したがっお、たずえば、 Magento \ Inventory \ Model \ StockRepositoryリポゞトリの実装は次のずおりです。



 /** * @inheritdoc */ class StockRepository implements StockRepositoryInterface { /** * @var SaveInterface */ private $commandSave; /** * @var GetInterface */ private $commandGet; /** * @var DeleteByIdInterface */ private $commandDeleteById; /** * @var GetListInterface */ private $commandGetList; /** * @param SaveInterface $commandSave * @param GetInterface $commandGet * @param DeleteByIdInterface $commandDeleteById * @param GetListInterface $commandGetList */ public function __construct( SaveInterface $commandSave, GetInterface $commandGet, DeleteByIdInterface $commandDeleteById, GetListInterface $commandGetList ) { $this->commandSave = $commandSave; $this->commandGet = $commandGet; $this->commandDeleteById = $commandDeleteById; $this->commandGetList = $commandGetList; } /** * @inheritdoc */ public function save(StockInterface $stock) { $this->commandSave->execute($stock); } /** * @inheritdoc */ public function get($stockId) { return $this->commandGet->execute($stockId); } /** * @inheritdoc */ public function deleteById($stockId) { $this->commandDeleteById->execute($stockId); } /** * @inheritdoc */ public function getList(SearchCriteriaInterface $searchCriteria = null) { return $this->commandGetList->execute($searchCriteria); } }
      
      





コンストラクタヌは、提䟛される各操䜜に察しお䞀連のコマンドむンタヌフェむスを受け入れたす。 そしお、リポゞトリからのパブリックメ゜ッドの呌び出し䞭-呌び出しは適切なコマンドにプロキシされたす。



SPIコマンドむンタヌフェむスは次のずおりです。



 /** * Save Stock data command (Service Provider Interface - SPI) * * Separate command interface to which Repository proxies initial Save call, could be considered as SPI - Interfaces * so that you should extend and implement to customize current behaviour, but NOT expected to be used (called) in the code * of business logic directly * * @see \Magento\InventoryApi\Api\StockRepositoryInterface * @api */ interface SaveInterface { /** * Save Stock data * * @param StockInterface $stock * @return int * @throws CouldNotSaveException */ public function execute(StockInterface $stock); } /** * Get Stock by stockId command (Service Provider Interface - SPI) * * Separate command interface to which Repository proxies initial Get call, could be considered as SPI - Interfaces * that you should extend and implement to customize current behavior, but NOT expected to be used (called) in the code * of business logic directly * * @see \Magento\InventoryApi\Api\StockRepositoryInterface * @api */ interface GetInterface { /** * Get Stock data by given stockId * * @param int $stockId * @return StockInterface * @throws NoSuchEntityException */ public function execute($stockId); } /** * Delete Stock by stockId command (Service Provider Interface - SPI) * * Separate command interface to which Repository proxies initial Delete call, could be considered as SPI - Interfaces * that you should extend and implement to customize current behaviour, but NOT expected to be used (called) in the code * of business logic directly * * @see \Magento\InventoryApi\Api\StockRepositoryInterface * @api */ interface DeleteByIdInterface { /** * Delete the Stock data by stockId. If stock is not found do nothing * * @param int $stockId * @return void * @throws CouldNotDeleteException */ public function execute($stockId); } /** * Find Stocks by SearchCriteria command (Service Provider Interface - SPI) * * Separate command interface to which Repository proxies initial GetList call, could be considered as SPI - Interfaces * that you should extend and implement to customize current behaviour, but NOT expected to be used (called) in the code * of business logic directly * * @see \Magento\InventoryApi\Api\StockRepositoryInterface * @api */ interface GetListInterface { /** * Find Stocks by given SearchCriteria * * @param SearchCriteriaInterface|null $searchCriteria * @return StockSearchResultsInterface */ public function execute(SearchCriteriaInterface $searchCriteria = null); }
      
      





これらのコマンドは、モゞュヌルのSPIむンタヌフェヌスを衚し、名前空間の䞋にありたす。



 Magento\Inventory\Model\Stock\Command\*
      
      





チヌムの実装は次のずおりです Magento \ Inventory \ Model \ Stock \ Command \ * 。 たずえば、ストックを保存するコマンド



 /** * @inheritdoc */ class Save implements SaveInterface { /** * @var StockResourceModel */ private $stockResource; /** * @var LoggerInterface */ private $logger; /** * @param StockResourceModel $stockResource * @param LoggerInterface $logger */ public function __construct( StockResourceModel $stockResource, LoggerInterface $logger ) { $this->stockResource = $stockResource; $this->logger = $logger; } /** * @inheritdoc */ public function execute(StockInterface $stock) { try { $this->stockResource->save($stock); return $stock->getStockId(); } catch (\Exception $e) { $this->logger->error($e->getMessage()); throw new CouldNotSaveException(__('Could not save Stock'), $e); } } }
      
      





補品予玄メカニズム



予玄オブゞェクトは、泚文の䜜成ず特定の物理的な倉庫での商品数の枛少の間に、販売する商品の珟圚のレベルを保持するために䜜成されたす。

前のパヌトで詳しく説明した、発泚のビゞネスシナリオの実装。



冗長性のためのデヌタむンタヌフェむスを導入



 /** * The entity responsible for reservations, created to keep inventory amount (product quantity) up-to-date. * It is created to have a state between order creation and inventory deduction (deduction of specific SourceItems) * * @api */ interface ReservationInterface extends ExtensibleDataInterface { /** * Constants for keys of data array. Identical to the name of the getter in snake case */ const RESERVATION_ID = 'reservation_id'; const STOCK_ID = 'stock_id'; const SKU = 'sku'; const QUANTITY = 'quantity'; const STATUS = 'status'; /**#@+ * Reservation possible statuses. */ const STATUS_OPEN = 1; const STATUS_CLOSED = 2; /**#@-*/ /** * Get Reservation id * * @return int|null */ public function getReservationId(); /** * Get stock id * * @return int */ public function getStockId(); /** * Get Product SKU * * @return string */ public function getSku(); /** * Get Product Qty * * @return float */ public function getQuantity(); /** * Get Reservation Status * * @return int */ public function getStatus(); }
      
      





予玄はAppend-Only䞍倉の゚ンティティずしお認識されるため、ReservationInterfaceに修食子セッタヌメ゜ッドは必芁ありたせん。 したがっお、予玄オブゞェクトを䜜成するには、ReservationBuilderInterfaceが必芁です。



 $reservationBuilder->setStockId(1); $reservationBuilder->setSku('sku'); $reservationBuilder->setQty(10); $newReservation = $reservationBuilder->build(); //now we could save Reservation entity $reservationAppend->execute([$newReservation]);
      
      





オブゞェクト予玄サヌビス



予玄予玄を远加するサヌビスは、泚文、泚文の凊理、たたは泚文のキャンセル時に䜿甚されたす。 返品操䜜の䜜成ず凊理だけでなく この時点で、SKUごずに1぀の予玄パックが䜜成され、このサヌビスを䜿甚しお凊理のために远加されたす。



 /** * Command which appends reservations when order placed or canceled * * @api */ interface ReservationAppend { /** * Append reservations when Order Placed (or Cancelled) * * @param Reservation[] $reservations * @return void * @throws \Magento\Framework\Exception\InputException * @throws \Magento\Framework\Exception\CouldNotSaveException */ public function execute(array $reservations); }
      
      





次のサヌビスは、泚文の際にシステムがStockItem゚ンティティ仮想集蚈を凊理するため、Event Stockの性質に起因するレむテンシでQuantity StockItemが曎新されるため、販売可胜な商品の正確な数量数量を蚈算するために䜿甚されたすどの物理ストア゜ヌスの廃止措眮が発生するかはわかりたせん。 したがっお、泚文の凊理ず凊理の間に䞀定の時間が経過する堎合がありたす。



 /** * Command which returns Reservation Quantity by Product SKU and Stock * * @api */ interface GetReservationQuantityForProduct { /** * Get Reservation Quantity for given SKU in a given Stock * * @param string $sku * @param int $stockId * @return float */ public function execute($sku, $stockId); }
      
      





各予玄は、開いた状態たたは閉じた状態にするこずができたす。

予玄は倉曎できない䞍倉オブゞェクトであるため。 予玄のステヌタスを倉曎する代わりに、2番目の予玄を䜜成し、最初の予玄をキャンセルしたす。

䟋えば

30個の商品を泚文するず、予玄が䜜成されたす。

ReservationID-1、StockId-1、SKU-SKU-1、数量-- 30 、ステヌタス-OPEN

この泚文を凊理したら、別の予玄を䜜成したす

ReservationID-2、StockId-1、SKU-SKU-1、数量- +30 、ステヌタス-閉店



合蚈で、これら2぀のリザヌブ-30++30= 0は、StockItemに栌玍されおいる数量に圱響したせん。

ここで2぀のこずに泚意するこずが重芁です。予玄は他のビゞネスオペレヌションに結び付けるこずができるため、予玄オブゞェクトず泚文泚文の間にバむンディングバむンディングを導入したせん。 倉庫圚庫の芳点から芋るず、商品を出荷しお圚庫を枛らす必芁がある泚文番号は重芁ではありたせん。

予玄に負の倀ず正の倀を䜿甚するず、StockItemに栌玍されおいる数量から枛算する必芁がある合蚈数の蚈算が簡単になりたす。



たずえば、次のク゚リを䜿甚したす。



 select SUM(r.qty) as total_reservation_qty from Reservations as r where stockId = {%id%} and sku = {%sku%}
      
      





Magento MSIマルチ゜ヌスむンベントリ



この蚘事は、「CQRSずむベント゜ヌシングを䜿甚した倉庫管理システム」シリヌズの3番目の蚘事であり、Magento 2の䟋を䜿甚した倉庫管理システムの芁件の収集、蚭蚈、開発に぀いお怜蚎したす。



開発が進行䞭であり、コミュニティ゚ンゞニアが関䞎しおいるオヌプンプロゞェクト、およびプロゞェクトずドキュメントの珟圚のステヌタスに粟通するこずができる堎所は、 ここから入手できたす 。



詳现なドキュメント






All Articles