16

Functional programming promotes immutable classes and referential transparency.

Domain-driven design is composed of Value Object (immutable) and Entities (mutable).

Should we create immutable Entities instead of mutable ones?

Let's assume, project uses Scala as main language, how could we write Entities as case classes (immutable so) without risking stale status if we're dealing with concurrency?

What is a good practice? Keeping Entities mutable (var fields etc...) and avoiding great syntax of case classes?

Ben James
  • 121,135
  • 26
  • 193
  • 155
Mik378
  • 21,881
  • 15
  • 82
  • 180
  • 1
    You should look into event sourcing imo, that's a good way to achieve approach similar to DDD with OOP. – Alois Cochard Feb 11 '13 at 14:22
  • 1
    Take a look at https://www.manning.com/books/functional-and-reactive-domain-modeling which covers how to combine DDD and FP. Debasish also has some good presentations, for example check out: https://www.youtube.com/watch?v=U0Rk9Knq8Vk – Cal Apr 25 '16 at 04:30

2 Answers2

15

You can effectively use immutable Entities in Scala and avoid the horror of mutable fields and all the bugs that derives from mutable state. Using Immutable entities help you with concurrency, doesn't make things worse. Your previous mutable state will become a set of transformation which will create a new reference at each change.

At a certain level of your application, however, you will need to have a mutable state, or your application would be useless. The idea is to push it as up as you can in your program logic. Let's take an example of a Bank Account, which can change because of interest rate and ATM withdrawal or deposit.

You have two valid approaches:

  • You expose methods that can modify an internal property and you manage concurrency on those methods (very few, in fact)

  • You make all the class immutable and you surround it with a "manager" that can change the account.

Since the first is pretty straightforward, I will detail the first.

case class BankAccount(val balance:Double, val code:Int)

class BankAccountRef(private var bankAccount:BankAccount){
   def withdraw(withdrawal) = {
       bankAccount = bankAccount.copy(balance = bankAccount.balance - withdrawal)
       bankAccount.balance
   }
}

This is nice, but gosh, you are still stuck with managing concurrency. Well, Scala offers you a solution for that. The problem here is that if you share your reference to BankAccountRef to your Background job, then you will have to synchronize the call. The problem is that you are doing concurrency in a suboptimal way.

The optimal way of doing concurrency: message passing

What if on the other side, the different jobs cannot invoke methods directly on the BankAccount or a BankAccountRef, but just notify them that some operations needs to be performed? Well, then you have an Actor, the favourite way of doing concurrency in Scala.

class BankAccountActor(private var bankAccount:BankAccount) extends Actor {

    def receive {
        case BalanceRequest => sender ! Balance(bankAccount.balance)
        case Withdraw(amount) => {
            this.bankAccount = bankAccount.copy(balance = bankAccount.balance - amount)
        }
        case Deposit(amount) => {
            this.bankAccount = bankAccount.copy(balance = bankAccount.balance + amount)

        }

    }
}

This solution is extensively described in Akka documentation: http://doc.akka.io/docs/akka/2.1.0/scala/actors.html . The idea is that you communicate with an Actor by sending messages to its mailbox, and those messages are processed in order of receival. As such, you will never have concurrency flaws if using this model.

Edmondo
  • 19,559
  • 13
  • 62
  • 115
  • But aren't there risks to have a set of transformation currently in memory? Let's imagine that different threads for several background job tasks maintain different versions of the same entity. This would lead to inconsistency, wouldn't it? – Mik378 Feb 11 '13 at 14:38
  • I understand where you are coming from. Background jobs maintaining state can be a source of problems. I still wonder which use case is a valid one for maintaining a shared state among independent background jobs – Edmondo Feb 11 '13 at 14:44
  • 2
    Use-case? Bank accounts. Background jobs could be reporting, ATM-handlers, interest-rate-calcualators... – The Archetypal Paul Feb 11 '13 at 14:47
  • 1
    Yes but one could argue the business logic middleware should not be holding on to such state and rely on the persistence technology (ie RDBMS or federated cache). I think a much safer route instead of just actors if you need to store the mutation is [software transactional memory](http://doc.akka.io/docs/akka/1.3.1/java/stm.html). – Adam Gent Feb 11 '13 at 16:15
  • I agree with you 100%. Mine was a simplistic approach – Edmondo Feb 11 '13 at 16:46
  • @AdamGent, yes, but the OP was about removing mutable Entities. In DDD, if I understand correctly, the Entity will still have mutable state - even if stored in your RDBMS. I was arguing that bank accounts are a valid use case for having mutable Entity accessed by multiple controllers .... – The Archetypal Paul Feb 11 '13 at 16:50
  • I came across this article: http://blog.zilverline.com/2011/02/07/towards-an-immutable-domain-model-–-believe-the-type-part-4/ – Mik378 Feb 11 '13 at 19:52
10

This is sort of an opinion question that is less scala specific then you think.

If you really want to embrace FP I would go the immutable route for all your domain objects and never put any behavior them.

That is some people call the above the service pattern where there is always a seperation between behavior and state. This eschewed in OOP but natural in FP.

It also depends what your domain is. OOP is some times easier with stateful things like UI and video games. For hard core backend services like web sites or REST I think the service pattern is better.

Two really nice things that I like about immutable objects besides the often mentioned concurrency is that they are much more reliable to cache and they are also great for distributed message passing (e.g. protobuf over amqp) as the intent is very clear.

Also in FP people combat the mutable to immutable bridge by creating a "language" or "dialogue" aka DSL (Builders, Monads, Pipes, Arrows, STM etc...) that enables you to mutate and then to transform back to the immutable domain. The services mentioned above uses the DSL to make changes. This is more natural than you think (e.g. SQL is an example "dialogue"). OOP on the other hand prefers having a mutable domain and leveraging the existing procedural part of the language.

Adam Gent
  • 47,843
  • 23
  • 153
  • 203
  • This is how I'd do it as well. Services to model the domain behaviour and immutable data objects (case classes as ADTs). Keep the services as pure as possible (inevitably there is IO but you can carefully push it right to the edge). A lot of the common patterns in DDD exist to solve problems in managing mutable state in an imperative OO system with layers. The FP approach would seem to me to obviate the need for a lot of it. – Brian Smith Feb 11 '13 at 19:00
  • @Adam Gent Great answer :) – Mik378 Feb 11 '13 at 19:48
  • @BrianSmith Yep. I think DDD as well as some other Fowler/Evans patterns are overrated (those guys need to spend some time in the FP camp). Modern backend web application fits better with FP. [I am even a Java programmer and prefer it](https://github.com/agentgt/jirm). Also the definition of DDD is flakey. And it seems DDD despises transformations aka DTOs but in my experience you always end up needing transfer objects (ie different views). FP embraces transformations. – Adam Gent Feb 11 '13 at 20:29