I'm new to Domain Driven Design and I have some doubt about some concepts (hoping that this is the right place to ask this).
I know that in DDD I should avoid an anemic model, so thinking about a social network model who should make (save) the friendship between two friends?
I imagine the situation like having a class representing the Users (using a Java-like syntax):
class User{
String username
List<User> friends
}
So, should it have a method to add a friend?
class User{
void friendship(User friend)
}
or should I have to use a service to do it?
class UserService{
void friendship(User user, User user2)
}
My thought is that this is an example of what in relational database theory is termed a "weak entity". A Friendship
could be identified solely by the identifiers of the two User
s involved in the friendship, but could have its own properties such as when it was created and what type of relationship it is.
I would make this its own entity, and probably hide it behind a facade exposed by the User
object:
class User {
protected List<Friendship> _friendships { get; private set; }
public IEnumerable<User> Friends {
get { return _friendships.Select( /* get other user */ ); }
}
public void AddFriend(User otherUser) {
// check to see if friendship exists
// if not, create friendship
// do other friendshippy things
// make sure the other user knows about our friendship
// and gets to do its friendshippy things
otherUser.AddFriend(this);
}
}
I would use something like
public sealed class Username : IEquatable<Username> { /* string wrap here */ }
public class User
{
private readonly Username _username;
private readonly HashSet<Username> _friends;
public User(Username username)
{
if (null == username) throw new ArgumentNullException("username");
_username = username;
_friends = new HashSet<Username>();
}
public Username Name { get {return _username; } }
public void Befriend(User user)
{
if (null == user) throw new ArgumentNullException("user");
_friends.Add(user.Name);
}
public bool IsFriendsOf(User user)
{
if (null == user) throw new ArgumentNullException("user");
return _friends.Contains(user.Name);
}
}
Note that no collection is exposed by User, according to the Law Of Demeter. In case you'd really need them I would expose an IEnumerable<Username>
for friends.
Moreover, in DDD, all query and commands should be part of the ubiquituous language (this is why I used Befriend
instead of AddFriend
).
However, let me say that this look a bit too CRUD to require a DDD. If you don't need (at least) a domain expert to understand the domain, you don't need DDD at all. If you don't need DDD, it becomes the most expensive error in your project.
edit
Let's suppose that the domain expert states that "friendship is always reciprocal" (as per guillaume31's suggestion): by modeling idempotent commands, you can ensure such a business rule very easily. The Befriend
command becomes:
public void Befriend(User user)
{
if (null == user) throw new ArgumentNullException("user");
if(_friends.Add(user.Name))
{
user.Befriend(this);
}
}
You can always model idempotent commands with such properties, but sometimes it requires a bit more of analysis to ensure that their arguments and their internal state provide everything they need.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With