DDD: should "Comment" in an "Article" be an aggregate root? - domain-driven-design

I am starting to design a first simple application DDD-style, and I am starting to see how the concepts work together.
If I design a classic blog application, Article class will be one of my aggregate roots. I want to retrieve articles, manage and delete all the related data (publication date, authors...). I am having difficulties with comments. At first, Comment seems to be an entity that is part of the Article aggregate: a comment is created in relation to an article, if I delete an Article, I will delete related comments.
Then I want to display a small box on the blog with the latest comments posted on the blog, for any article. So it looks like I want to retrieve comments from my data store (and only comments). From my understanding of DDD ideas, that makes it an aggregate root. But that does not seem totally right as Comment seems to depend strongly on Article.
How would you model it?
Thanks.

When you think about it you will probably find various reasons why a Comment should be an Aggregate itself:
You want to list the latest comments
You may want to list all comments by a particular user
You may want comments to be nested (a comment being a reply to another comment)
You may want to approve/reject comments through an admin interface
A user may want to edit or delete his/her comment
...
I take the following as a general rule of thumb: Keep your Aggregates as small as possible. When in doubt, err on the side of more Aggregates.
By modelling it this way, you can attach the comments to multiple objects, like Article and User
Comment
string Text
string UserName
bool IsApproved
Article
string Title
string Body
...
List<CommentIds> CommentIds
User
string UserName
...
List<CommentIds> CommentIds
ListOfTenLatestComments
List<CommentIds> CommentIds

Related

How to create one-to-many and many-to-many relations in Strapi v.4?

I want to create relations between Post and Tag as follows.
Post has many Tags (one-to-many)
Tags have and belongs to many Posts (many-to-many)
I created collection types for Post and Tag.
Set them accessible for public user.
I created 1 post with 3 tags.
Now, when I try to see them at http://localhost:1337/api/posts
I don’t see nested elements, i.e. tags…
Am I missing anything ?
And lastly, I was not able to create Many-to-Many relations between Post and Tag with the following error.
I understand that Tags field already exists, but I thought this is how supposed to be set.
I got reply from Strapi forum, so decided to share here.
https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/rest/populating-fields.html
According to the docs above, to pull (in my case) 1st level nested elements I have to add populate[0] parameter like so:
http://localhost:1337/api/posts?populate[0]=tags

Fetching 'related documents' from mongodb

So I have a very specific question about the optimum way of storing & then fetching data from a MongoDB database and ill try my best to explain the use case:
I have a content publishing platform that I've built. On this platform, a user can say, write a story, and the story gets saved as a document in the 'stories' collection in the database in a structure such as this:
{
_id : s_12345,
title : This is a story,
...
}
now on the same platform, let's say, another user writes a 'news article', which gets saved as a document in a separate 'news' collection. But now, the interesting thing is, while writing this news article, the user could 'tag' a story of their choice so that this news article would show up in a 'related content' section when some user on the platform is viewing that particular story. So the data structure of this news article could be:
{
_id : n_12345,
title : This is a news article,
related_to_tag : s_12345 //id of the story
...
}
Now from my understanding as of now, there are 2 ways of doing this:
OPTION 1: when a user tries to view this story (s_12345), we make a get request to the server, fetch this particular story document from the 'stories' collection in the database, then cycle through ALL the documents in the 'news' collection and pick up all the documents that have the related_to_tag === s_12345, and then return the story document + all these related news documents to the client. However this operation seems pretty expensive to me, especially if I have, let's say, 10,000 news articles in the news collection.
OPTION 2: At the time of posting the 'news article' to the database, I also find the story (s_123456) in the stories collection, and write a reference to the news article in this story document itself, like so:
{
_id : s_12345,
title : This is a story,
related_content : n_12345
...
}
The second option seems better to me. Because then, when a user tries to get the story, I already know all the other news articles that are related to it, and simply have to run a mongoose populate function to populate these news articles. but it brings up other complications such as:
what happens when the author of the news article deletes it? that means that I will have to find the story document (s_12345), and delete the related_content reference (n_12345) as well.
Or maybe I could run a weekly cron-job that does this sort of cleanup.
Also what happens if, while I am doing this double write operation (write the news article to database + write a reference to the news article to the story document), the second operation fails for whatever reason. that would create data inconsistency.
Anyway, this is a question that I have been struggling with for quite some time now, hope I have explained my use case clearly.
Awaiting your responses!
Abrar

How does a document-based database and CouchDB in particular handle ID references?

I really like the document-based approach of storing data like blog posts as a whole document with all information needed saved inside of it. Therefore the author´s username is stored as plain text. The author himself has his own document with personal information attached to it. What happens when the author decides to change his username? Do I have to update every document the contains a blog post by that author or is this just one of the drawbacks using a document-based database?
Thanks for any suggestions!
If you need to write a query(view) with content from the blogpost and the name of the author, then the name must be included in the blog content, and therefore all blogposts must be updated.
if the name is only for information ( i mean you do not query a blogpost-content like keywords AND name of author), you can add the id into the blog document (and of course now can Query blog content AND author-id) and emit {'_id':doc.author_id} as a Value.
include_docs=true then gives you the doc of the Author (and no longer the blogpost-doc.. you have to call it explicit with the id thats in the result rows). No Need to update the blogposts.
Example:
Case 1:
Use Author by Name, you have to include the name, and therefore update ALL docs.
{
"_id":"blogpost1",
"author":"Oliver",
"keyword":"couchDB"
}
to look for all couchdb posts from oliver:
emit ([doc.author,doc.keyword],1)
call:
&key=["Oliver","couchDB"]
Case 2:
No need to query by name
{
"_id":"blogpost1",
"author_id":"author-123",
"keyword":"couchDB"
}
emit (doc.keyword,{'_id':doc.author_id})
and the authors doc:
{
"_id":"author-123",
"name":"Oliver"
}
call:
?key=["couchDB"]&include_docs=true
result:
...
{"id":"blogpost1","key":"couchDB","value":{"_id":"author-123"},"doc":{"_id":"author-123","_rev":"xxx","name":"Oliver,....

How to create and fetch relational records in core data

Total newbie question now... Suffice to say, I have searched for a completely noddy explanation but have not found anything 'dumb' enough. The problem is...
I have created a core data stack in which I have a entity called 'Client' and an entity called 'Car'. It is a one-to-many relationship.
So far i have successfully created and fetched the client list using code from apple's tutorial. Once I select a client, I then push a new tableViewController which should list the Cars for that chosen client.
First question...
I am used to sql style database programming where if I wanted to add a car to a client, I would simply add a 'ClientID' tag to the 'Car' record thereby providing the relationship to a specific client. How do I do the equivalent in core data? My understanding from my reading is adding attributes to point to other entities isnt necessary - core data maintains this relationship for you without needing additional attributes in the entities.
Second question...
As and when I have created a 'car' entity and successfully linked it to a 'Client'. How to I create a fetch which will retrieve just THAT client's cars. I could alter the code from apple to fetch ALL cars but I don't know how to fetch cars associated with a given client. From my reading, I think I need to use predicates, but apples predicate documentation stands alone and does not give clear guidance on how to use it with core data
I realise how noddy this is, but I cant find an idiots guide anywhere...
Any help/code exmaples much appreciated.
OK, I have answered my own question. For those who have found my question and would like to know the answer, it is extremely simple...
Firstly, to create a 'Car' and associate it with a 'Client'. Firstly create a 'Car' as you normally would and simply add this line of code...
newCar.client = client;
This sets the 'client' relationship on the 'Car' record to the client in question.
Secondly, I had thought that if you had a client and needed to find their cars, you would need a new fetch. But not so! Simply use the following lines of code...
NSSet *cars = client.cars;
[self setCarsArray:[cars allObjects]];
The first line uses "client.cars" o follow the object graph to determine the cars this client has and populates them in an NSSet. The second line then populates a NSArray which is declared in the current viewcontroller which can be used to for display purposes.
Sorted!!

DDD - How Can I Avoid Crossing Aggregate Boundaries Here?

We're working on a new project (re-writing existing app), and I'm running into problems with my domain model / repository design.
Here is a (simplified) version of two key portions in our domain model:
As you can see, I have an abstract concept of a Post, which can be things like a Review, a Discussion, a Photo, a Video, etc. Posts can also have comments.
I also have a abstract concept of a Location, which are obviously things like Streets, Cities, Neighbourhoods, etc.
Now, this naturally looked to me as two clear aggregate roots.
So I created two repositories, one called PostRepository, and another called LocationRepository.
This was all working fine, I can add/get any type of Post (or comment), and add/get any type of Location via one of these two repositories.
But now im in the scenario of a "landing page" for a City (for example).
On this page, I need to basically show "all posts for this location".
How is that defined? Well, a Post can be (optionally) tagged at a Location. Implementation detail, so I don't want to go too deep into data (as that's not what DDD is about), but essentially there is geospatial intelligence to work out which posts are contained in a particular location by the shape file of the location, and the latitude/longitude of the tagged Post.
But how can I retrieve this information without crossing the boundaries?
Which repository do I use? Do I need a new one?
If it matters (or for the curious), this is a web application (ASP.NET MVC), with a SQL Server 2008 database and Entity Framework 4.0.
If you need any clarification, let me know.
EDIT
We currently use a modified version of the Specification pattern in order to retrieve domain models.
For example, this is the code in our BLL to retrieve all Review's where Score >= 4:
var reviews = postRepository // GenericRepository<Post>
.Find() // IQueryable<Post>
.OfType<Review>() // IQueryable<Review>
.Where(x => x.Score >= 4)
.ToList(); // List<Review>
But now I need some code like this:
var reviews = postRepository
.Find()
.OfType<Review>()
.Where( //lat long, or Locations FK )
.ToList();
The problem is I don't know how to do the above query without adding an intermediary join-entity (LocationPost - as it's a many to many), and add a FK to the Post domain model to that.
But by doing that, I am crossing the aggregate boundaries - aren't I?
Why is this a problem?
According to Evans in his book, one AR may very well reference another AR.
(You may not however reference a child element in an AR from another AR)
Also, are locations really aggregate roots?
The definition of an aggregate root is that it acts as a boundary of concistency.
Does that fit the definition of a location?
I'd say a location is a value object.
There are pretty much two camps here regarding repositories and AR associations:
One that says that all aggregate roots have to be fetched through their respective repository, and AR's should use soft relations, eg ID's between them
And one that says that aggregate roots may very well fetch other associated aggregate roots and that a repository is merely a way to find aggregate roots.
I would bind post to the location at creation time so that for each location I can get (through a repository) a list of associated posts. It would look like this:
Creation:
var p = new Post(latitude, longitude);
var locations = locationRepository.FindByCoordinates(latitude, longitude);
foreach (var l in locations)
{
l.AssociatePost(p);
}
session.Save(p);
Retrieval:
var associatedPosts = postRepository.FindByLocation(locationId);
foreach (var p in associatedPosts)
{
Display(p);
}
Under the hood, the association between posts and location would be implemented as a many-to-many table relationship. There is one problem with this solution: adding a new location requires to scan all the posts and assign them to the new location (if applicable).
Hope that helps.
Let's say you used the Specification pattern, could you build a Post Specification using a Location object? Then you just pass the Specification to your Post Repository, and get back the result.

Resources