Most of DDD tactical design patterns belong to object-oriented paradigm, and anemic model describes the situation when all business logic is put into services rather than objects thus making them a kind of DTO. In other words anemic model is a synonym of procedural style, which is not advised for complex model.
I am not very experienced in pure functional programming, yet I'd like to know how DDD fits into FP paradigm and whether term 'anemic model' still exists in that case.
Update: Recentlry published book and video on the subject.
And one more video from Scott.
Best Answer
The way the "anemic model" problem is described doesn't translate well to FP as is. First it needs to be suitably generalized. At it's heart, an anemic model is a model which contains knowledge about how to properly use it that isn't encapsulated by the model itself. Instead, that knowledge is spread around a pile of related services. Those services should only be clients of the model, but due to its anemia they're held responsible for it. For example, consider an
Account
class that can't be used to activate or deactivate accounts or even lookup information about an account unless handled via anAccountManager
class. The account should be responsible for basic operations on it, not some external manager class.In functional programming, a similar problem exists when data types don't accurately represent what they're supposed to model. Suppose we need to define a type representing user IDs. An "anemic" definition would state that user IDs are strings. That's technically feasible, but runs into huge problems because user IDs aren't used like arbitrary strings. It makes no sense to concatenate them or slice out substrings of them, Unicode shouldn't really matter, and they should be easily embeddable in URLs and other contexts with strict character and format limitations.
Solving this problem usually happens in a few stages. A simple first cut is to say "Well, a
UserID
is represented equivalently to a string, but they're different types and you can't use one where you expect the other." Haskell (and some other typed functional languages) provides this feature vianewtype
:This defines a
UserID
function which when given aString
constructs a value that is treated like aUserID
by the type system, but which is still just aString
at runtime. Now functions can declare that they require aUserID
instead of a string; usingUserID
s where you previously were using strings guards against code concatenating twoUserID
s together. The type system guarantees that can't happen, no tests required.The weakness here is that code can still take any arbitrary
String
like"hello"
and construct aUserID
from it. Further steps include creating a "smart constructor" function which when given a string checks some invariants and only returns aUserID
if they're satisfied. Then the "dumb"UserID
constructor is made private so if a client wants aUserID
they must use the smart constructor, thereby preventing malformed UserIDs from coming into existence.Even further steps define the
UserID
data type in such a way that it's impossible to construct one that's malformed or "improper", simply by definition. For instance, defining aUserID
as a list of digits:To construct a
UserID
a list of digits must be provided. Given this definition, it's trivial to show that it's impossible for aUserID
to exist that can't be represented in a URL. Defining data models like this in Haskell is often aided by advanced type system features like Data Kinds and Generalized Algebraic Data Types (GADTs), which allow the type system to define and prove more invariants about your code. When data is decoupled from behavior your data definition is the only means you have to enforce behavior.