3

I'm working on a project which utilizes DDD, Hexagonal Architecture and Microservices.

While working on StorageService, the implementation was quite easy since the features were:

  • CRUD of media files
  • automated backups

There was just one aggregate root, and nothing fancy about it.

.. but now, I'm digging into ContentService which contains: Articles, Posts, Comments, Categories, Tags and Users (fetched and cached from AuthService) - and I'm having a hard time NOT sticking all the functionality into User (aggregate root).

I mean, all of the methods I will possibly implement will have to check whether the person calling for creation of article, deletion of tag, update of post or whatever has the neccessary permissions.

Is it a "proper" thing to have it all packed into User, just because he's the "executor" of functionality? All of the methods, like:

  • user.postComment(article, post, content)
  • user.submitArticle(string title, string content)
  • user.setArticleTag(article, tag)
  • user.deletePost(post)
  • ... you get the idea
JTinkers
  • 241

2 Answers2

5

There is nothing inherently wrong in making the User object a facade to certain user related functions of the service. As long as it delegates the more complex operations to other, maybe internal classes, this does not make the User a God object. Note also the User class in the content service context is a different one than the User class in the authentification service, so the responsibility of the ContentService.User is already restricted to the specific bounded context.

However, one should also think about alternatives, because the User as a facade does not seem to be the mot natural choice for some of the operations mentioned in the question. For example

user.postComment(article, post, content)

looks strange to me. I don't know the system you are designing (probably some kind of DMS), but shouldn't a comment be something to be posted either to an article, or to a post, or to a content object? Hence why not have an Article.addComment method, a Post.addComment and Content.addComment?

user.submitArticle(string title, string content)

Isn't an article to be submitted somewhere? Maybe to a folder or some other kind of container in your DMS? So the natural place for the method here could be that container object.

user.setArticleTag(article, tag)

Why not have the article a setTag method?

user.deletePost(post)

Why not make delete a method of Post?

Maybe in all those cases you really need a user object, or maybe just a user ID. For example, you may want to store the information who added or deleted a post. For this, one can make the user (or user id) an additional parameter of the specific methods. You could also design the system in a way where there is always a connected user (maybe as a global state information in your ContentService object), so you don't need to pass the same user into each method (but that will most probably not work when this is a web app where one process will several handle requests of multiple users).

Doc Brown
  • 218,378
1

An AGGREGATE is a cluster of associated objects that we treat as a unit for the purpose of data changes -- Evans, 2003

In my experience, this is usually interpreted as an expression of domain invariants (how information changes) rather than access control (who does the changing).

Consider chess: we have players moving the white and black pieces, where each move is required to be legal (consistent with the rules of chess and the previous moves in the game).

Note that while we may have concerns about player identity (only the white player is permitted to play the white pieces) the play of the game doesn't care at all about things like the player's name, age, rating, and so on.

Therefore, the aggregate in question probably isn't going to be the player, because player has a bunch of information that we don't care about at all. Much more likely that the aggregate is going to be the game itself, because that's the subset of the domain that changes.

VoiceOfUnreason
  • 34,589
  • 2
  • 44
  • 83