Search code examples
javascalaconcurrencyakkaactor

How to model aggregates relations with Akka


I struggle with relations in actor model. Let me use simple example. There are two actor types in the system: Warehouse and Item. Item is always assigned to particular Warehouse. AddStock command allows to increase stock quantity assigned to given warehouse location. Only warehouse knows own locations so Item must ask its Warehouse if the location is valid to perform own business logic. I've thought of some solutions

  1. Command AddStock contains ActorRef to warehouse shard region which allows Item to ask the Warehouse about location.
  2. Allow somehow only the Warehouse to issue command AddStock which allows to prevalidate locations. Not sure how to achieve this, I've thought about private commands or some identification process

I do not like neither solutions. It adds lot of unnecessary complexity to simple task. I think it may be issue with OOP thinking here and distributed actors world is governed by its own rules.

In my scenario I perceive both Item and Warehouse as separate aggregates. They are both implemented as Persistent Actor (with Cluster Sharding).

All is easy as long as you assume that only Warehouse may call Item::AddStock. Then Warehouse just validates the location before the call. But one may call Item directly and skip all that validation. Item has no way to know if the proper warehouse made the call. It is easy to achieve in pure OOP/DDD approach. Item is aware of warehouse ID and changing quantity would look like this

item.addStock("L2", 100, warehouse)

Item knowing warehouse id may validate if provided warehouse is the warehouse. And having warehouse may ask if location is valid. My current implementation of Item handler:

case AddStock(region, location, quantity) => {
  region ? EntityEnvelope(warehouseId, HasLocation(location)) map (
    _ => VerifiedAddStock(quantity)
  ) pipeTo self

VerifiedAddStock is Item`s private object so I am sure who is the command issuer (almost because it not enforce identity).

So the problem is how to model such relation in Actor world? It is the norm to pass around shard region ref?

One may suggest that it is design flaw itself due to improper aggregate/actor boundaries but this is just an example.


Solution

  • Does Item have any information (e.g. description) which Warehouse isn't going to care about?

    If the answer is no, then I would just treat Item as a child of the associated warehouse location (i.e. invert the direction of the relation). As long as the warehouse never gives out an ActorRef for the Item, you can be sure that only it can send messages to the Item actor (Akka Typed lets you make this static guarantee).

    If the answer is yes, I'd suggest that in the context of warehouse inventory, an Item consists of exactly the information needed in that context and, for instance, something like catalog descriptions is the responsibility of a different context (very likely a different service) and if there's something the catalog description context needs to know about inventory (e.g. whether it's in-stock anywhere or not), then it should leverage domain events emitted by the warehouse inventory.