Domain Driven Design: How to model relations that are large but have few behaviors
Asked Answered
G

3

6

Let's say I have two entities User and Item. The sole behavior in the domain between these two entities is that a user can like an item. Since there is no restriction on how many items a user can like, this many-to-many relation can be large.

I don't think it makes sense performance-wise to have User contains a list of items they liked in its model or the other way around, since I'll have to load a potentially large collection of items in order to add just one item. From domain design point of view, it also doesn't make sense to me to have either entity reference the other in their fields as no behavior require the presence of a collection of items or users.

I do need to keep this relationship as the UI needs to display a list of items a user liked and a list of users that liked an item. The requirement can be served by a read model but I still need a domain concept to capture this relationship so that it can be persisted.

One way I can come up with is to introduce a relation type of aggregate, say UserLikeItem, and have method user.like(item) return an instance of UserLikeItem which I can then use UserLikeItemRepository to persist.

Is this a valid solution? What is the natural way in DDD to model this type of large but non-behavioral relations?

Gerda answered 29/10, 2014 at 0:49 Comment(3)
I like (pun!) your solution, even though it doesn't need to be a method on User IMO. It also allows you to capture additional info such as the time the item was liked, in what context, etc. The same AR could be used for unlike as well, but maybe you need to find a better name for it.Bottomry
I agree, the only thing I'd probably add is a LikeCounter to the Item, so that you can display the total like count for an item without having to hit the UseLikeItem data source.Gualtiero
A transaction object or a domain event "UserLikedItem" seems appropriate with what little you've given us to go on.Haslett
C
3

Since I ran into this scenario some time ago, here's my take. User and Item are part of different aggregates they don't know/care about each other (even if an item has an userId). A "Like System" (LS) is a different aggregate tracking likes from who to what.

LS doesn't really care about User or Item either, although since I can't see of a case where something else other than a user can like something, I can say that Like will always imply a userId (not the whole User concept) and a subject (Item, VIdeo, Picture, Post etc).

LS simply keeps an association of user Id and another itemId of a certain type (that can be a string, if you want that LS to not be coupled to what an Item is). Every time a user like something a command is issued: RegisterLikeForItem { UserId, ItemId, ItemType} . A LS handler will store that info then publish an event UserLikedItem. One of its handler will be a counter which will count how many users liked that item. Another handler can make a list of what items were liked by one user (this will be queried by the UI).

Each handler serves one use case and probably each has its very own storage. I know it looks quite complicated, but it's actually simple (one use case requires a handler and maybe a storage) and bets of all, it's very flexible and maintainable. And the system works with one or 1000 item types.

Commemoration answered 1/11, 2014 at 19:33 Comment(0)
F
0

I will take a dig at this :), IMO if does not have behaviour it does not live in the "domain model", and if a vast majority of your concepts do not have behaviour then you do not need a "domain model", you should probably be looking at a transaction script kind of pattern rather than domain model.

I would paraphrase your question to "how do i persist a many to many relationships in a performant manner" to this question we have multiple answers, one is what you mentioned other could be just store the List of id's within the classes.

Domain model and a object oriented representation of your persistence backend are two separate things, domain model is behaviour first, so you think about behaviour and then add the properties into the domain model which will be affected by that behaviour, else what you need is a "object oriented representation of your persistence backend" (DTO)

But it gets tricky when you have the case for a domain model, but just a couple of concepts are anemic and lack behaviour but thats a question for another day :)

Fetching answered 30/10, 2014 at 12:14 Comment(1)
I disagree with 'if does not have behaviour it does not live in the "domain model"' . Anything that has a Domain meaning/defines a Domain concept is part of the Domain, regardless if it has behaviour or it's a data structure.Commemoration
G
0

'Like' is domain fact, which can be represented by Entity.
Items have Likes -> Item contains collection of Likes.
This collection is needed only for adding and deletion of likes -> Item have LikeCount property and collection of Likes is optional for Repository query (used only in Item.AddLikeFrom(user) and Item.RemoveLikeFrom(user) methods).

It may look, that relation is non-behavioral, but in fact it's behavior of one of the relation sides.

Godliman answered 31/10, 2014 at 11:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.