DDD implementation

I'm new to Domain Driven Design, and I have some doubts about some of the concepts (hoping this is the right place to ask about this).
I know that in DDD I should avoid the anemic model, so think about the social network model , who should (maintain) the friendship between two friends?
I assume the situation is similar to a class representing users (using syntax similar to Java):

class User{ String username List<User> friends } 

So he should have a way to add a friend

 class User{ void friendship(User friend) } 

or do i need to use the service for this?

 class UserService{ void friendship(User user, User user2) } 
+7
source share
6 answers

My thought is that this is an example of what is called a “weak entity” in relational database theory. A Friendship can only be identified by the identifiers of the two User involved in the friendship, but can have its own properties, such as when it was created and what type of relationship it has.

I would make it my own entity and probably hide it behind the 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); } } 
+6
source

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); } } 

Please note that no collection is opened by the user, in accordance with the Law of Demeter . In case you need them, I would suggest IEnumerable<Username> to my friends.

In addition, in DDD, all queries and commands must be part of ubiquituous language (which is why I used Befriend instead of AddFriend ).

However, let me say that this look is too similar to nofollow -> CRUD to require DDD. If you do not need (at least) a domain expert to understand the domain, you do not need DDD at all . If you do not need DDD, this will be the most expensive mistake in your project.

change
Suppose a domain expert claims that “friendship is always mutual” (as suggested by guillaume31 ): by modeling idempotent teams, you can easily provide such a business rule. The Befriend team 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 takes a little more analysis to ensure that their arguments and their internal state provide everything they need.

+4
source

I think; Friendship is the collective root. It can be directly created in the application service, or the creation can be delegated to the domain service.

The domain service may request user aggregates for verification if user authentication is required. Or send both user aggregates to the friendship aggregation creator / constructor.

Then a repositoy of friendship can easily return a friend list for a given user.

Even the totality of friendship does not have a rich model or behavior; it has a separate consistency border.

Also, if we use eventourcing; You can listen to friendly events; and inform both users of the situation.

+3
source

This is similar to the transferFunds() problem in the banking domain - should you call this method in the source or target account? And should an Account manage another Account Balance in the first place?

DDD domain services come in handy when some kind of behavior is simply not suitable for an existing object or manipulates several objects. This does not make your domain anemic, because the service is part of the Domain, and having a limited number of objects without any behavior does not mean that your entire model is anemic anyway.

If friendships are reciprocal from the start, it might be more logical to delegate the creation of friendships to a domain service rather than having one User change another User friend list.

Otherwise, the addFriend() method will work fine.

+2
source

A weak or anemic domain model simply means that your “domain objects” are DTOs without any behavior. Basically you got a Transaction Script template where DTOs are loaded, modified and saved again. CRUD, in a different way. This is good for many applications that do not have complex enough rules to use the DDD approach.

Domain objects must encapsulate behavior. That is their essence. Any public state (perhaps it does not have public getters or setters) should be read-only, and if you want to change the state, you call a method that relates to the usage / business requirement.

All your "domain logic" should be in these classes. Domain logic is just a fancy name for describing the rules and operational parameters of the domain you choose. Be it banking, retail, HR, etc. When your domain experts explain the “Pay By Card” user case and tell you: “We cannot open until the PDC machine contacts the bank.” This is a business rule / invariant / piecewise domain logic.

Typically, you collect domain objects (consisting of objects and value objects) into aggregates that define the boundary at which a given set of rules should be executed. The object that is the root of this graph of domain objects is known as the "Aggregated Root", and only aggregates the roots so that other objects can contain links. In your case, User is Entity, and since it is the only object in Aggregate, it is also Aggregate Root. For example:

 public class User // Entity and also the Aggregate Root { private readonly IList<Friendship> _friends = new List<Friendship>(); public void Befriend(User user) { _friends.Add(new Friendship(/* Date? */, user)); } public class Friendship // Entity { // ... Date? private User _friend { get; private set; } public Friendship(/* Date? */, User friend) { _friend = friend; } } } 

This is not a good example, because theoretically you would need to call it for each of the two friends in a pair, but any transaction should only perform one operation, not two. In this case, you introduce the concept of Process Managers. These are even more objects that relate to the coordination of what is essentially a long-term transaction (where friends made friends with each other). You are likely to create Friendship (as the aggregate root), and this creation will create some kind of event-driven process in which friends are loaded, made friends and saved.

+2
source

Friendship between two users must be a separate population (AR). As an identifier for this AR, I recommend using user identifiers. So you have a small unit with low pressure that can be scaled / split. The same goes for messages sent between users. Each message is cumulative.

This is how we developed it, and it was successful.

Good luck.

0
source

All Articles