Search code examples
scalaoopconcurrencyakkaactor

Scala Actors - any suggestions when converting OOP based approach?


I'm learning Scala and its Actors (via Akka lib) approach for handling concurrency. I'm having some questions while trying to convert typical OOP (think - Java style OOP) scenarios to Actor based ones.

Let's consider the overused e-commerce example Webstore where Customers are making Orders that contain Items. If it is simulated in OOP-style you end up with appropriately named domain model classes that interact between themselves by calling methods on each other.

If we want to simulate concurrency e.g. many customers buying items at once we throw in some sort of threading (e.g. via ExecutorService). Basically each Customer then implements Runnable interface and its run() method calls e.g. shop.buy(this, item, amount). Since we want to avoid data corruption caused by many threads possibly modifying shared data at once, we have to use synchronization. So the most typical thing to do is synchronize the shop.buy() method.

Now let's move on to Actor based approach. What I understand is that Shop and each Customer now become Actors who, instead of calling buy() method on shop directly, sends message to shop. But here come the difficulties.

  1. Should all the other domain models (Order, Item) become Actors too and all the communication between all the domain models be message driven? In other words it is a question whether it is OK or not to leave some OOP style interaction between domain models via method invoking. For example, in OOP based approach Order would typically have a reference to List which you could populate when user is buying something by calling add(item) in buy() method. Does this (and similar) interactions have to be remodeled by messaging to make most use of Actor based approach? In yet another words it is a question when do we communicate with internal state of an actor directly and when do we extract internal state to another Actor?
  2. In OOP based solution you pass to methods instances of classes. I read in documentation that in Actor model one is supposed to pass immutable messages. So if I understand correctly, instead of messaging objects themselves you message only some data that makes it possible to identify which entities have to be processed e.g. by messaging their IDs and the type of action you want to perform.

Solution

  • Answering your questions:

    2) Your domain model (including shops, orders, buyers, sellers, items) should be described with immutable case classes. Actors should exchange (immutable) commands, which may use these classes, like AddItem(count: Int, i: Item) - AddItem case class represents command and encapsulates business entity called Item.

    1) Your protocol, e.g. interaction between shops, orders, sellers, buyers etc., should be encapsulated inside actor (one actor class per protocol, one instance per state). Simply saying, an actor should manage any (mutable) state, changing between requests, like current basket/order. For instance, you may have actor for every basket, which will contain information about choosed items and receive commands like AddItem, RemoveItem, ExecuteOrder. So you don't need actor for every business entity, you need actor for every business process.

    In addition, there is some best practices and also recommendations about managing concurrency with routers.

    P.S. The nearest JavaEE-based approach is EJB with its entities (as case-classes) and message-driven beans (as actors).