I'm not totally clear on how to stick to the suggested activity spec in a few cases, most of which map to something like this:
Eric left a comment "hello" on the topic "hey" on the album "hi"
The actor and the verb are clear, but I'm not clear on the object or target. Is the text of the comment the object and topic the target and the album extraneous metadata?
In this case, object is the comment, and target is the album.
Eric left a comment "hello" on the topic "hey" on the album "hi"
Actor: Eric (Eric's ID in your data store)
Verb: "commented"
Object: Eric's comment, "hello" (the ID of the comment in your data store)
Target: Album "hi" (the ID of the album in your data store)
Note that this doesn't include 'topic' - depending on how you have your feed groups set up, 'topic' might be another feed that you use the to field to write to (docs: https://getstream.io/docs/#targetting), or 'topic' might be the target.
It sounds like 'topic' might be a topic that would be shared across a bunch of different albums, so I'm treating that more like a 'category'. In that case, 'album' would still be the target, and 'topic' would be a feed that you also write to using the to field.
Hopefully that helps clear it up! Let me know how else I can help.
Related
I'm working on a REST API and I'm wondering what the naming convention should be for an endpoint that returns two different types of objects. In this contrived example, I've got a call to get a movie (by ID), and another to get the actors associated with a particular movie.
this.app.get('/movies/:id', moviesCtrl.getMovie);
this.app.get('/movies/:id/actors', moviesCtrl.getMovieActors);
My question is, what should be the path for a call that returns both the movie and the actors (i.e., the JSON for the movie but also appends an array of the list of actors)?
this.app.get('/movie-actors/:id', moviesCtrl.getMovieWithActors);
Now I get full picture. So I would go with the next names:
Movies without actors: /movies/:id
Movies with actors: /actors/movies/:movieId or movies_with_actors/:movieId
Only actors: /movies/:id/actors
Or you can combine 1st and 2nd endpoints and create boolean parameter /movies/:id?actors=true and /movies/:id?actors=false
But it is just a taste preference. The main idea: path should be meaningful. And the main rule is GET /movies - get all Get /movies/:id - get one
In an application that uses event sourcing is it acceptable to have aggregate-wide events?
Consider a contrived example of a blog application that provides the ability to create posts and add and remove simple tags (post would be the aggregate root).
This might result in the following events:
PostCreated: postId, "title", "content"
TagAdded: postId, "Foo"
TagAdded: postId, "Bar"
TagAdded: postId, "Baz"
TagRemoved: postId, "Bar"
Replaying the above event stream would result in a post with a title, content and two tags ("Foo" & "Baz").
Now imagine the user interface only allows you to select existing tags whilst creating a post and doesn't accept free text; only privileged users have the ability to update the master list of tags.
Now when a privileged user creates a new tag, a corresponding event needs to be created so that a) the information is actually stored in the event storage and b) at some point the read model is updated so that users creating blog posts can select the new tag in the UI.
Having an event that looks like TagCreated: postId, "NewTag" doesn't seem right to me as the information does not directly apply to a single post.
Considering that in this case the information does not warrant it's own aggregate root and will only be used in this bounded context I would expect an event along the lines of:
TagCreated("NewTag")
These events would be stored in the event storage using the same aggregate id as the previous set of events for a specific post but without an id for the specific aggregate instance.
So far this sounds like a logical way to handle the problem but was wondering if I am missing anything obvious by approaching it this way.
IMHO you're complicating your life unnecessary. Domain events are usually available cross bounded context and they should be associated with an aggregate root (AR) by referencing its id.
In your example, I'd consider Tag to be a value object, so it would require a post id. But if you want the Tag to be available as itself, then it would be an AR and so, the event would have a TagId property.
Btw, a domain event is a DTO, meant to be available everywhere, they're not a domain detail that needs to be encapsulated in an aggregate.
I think you missed the concept "tag catalog" or something like that. It could have a single aggregate (or perhaps you will sometimes have several catalogs for different user groups or something like that) with the catalog as the root, containing all the tags as value objects.
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
I am trying to do exactly same thing as post in NSFetchResultsController + sectionNameKeyPath + section order, i.e. basically use 2 tables, let's say Categories <-->> Events. Category table consists of category field only, while Event consists of name, dateTimestamp.
I defined relationship 'category' in Events table and try to use that relationship as sectionNameKeyPath when creating fetchedResultsController:
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"category.category" cacheName:#"Root"];
Finally, I pre-populated Category table with some categories upon loading of the app (and verified with .dump that table is populated correctly)
Yet, I simulator fails on:
return [[self.fetchedResultsController sections] count];
I did extensive search and most people either suggest using one of the fields in the table as sectionNameKeyPath (this works!) or transient property (works too!) However, I just want to use relationship as it seems very logical to me in this case where events belong to some categories and there could be categories without events. Am I wrong in my assumption that relationship can be used as sectionNameKeyPath? The original link at the top of the question suggests it works, but guy does not know why or how. Documentation is very weak on what can be used as sectionNameKeyPath, so any help will be highly appreciated.
A relationship gets you a pointer to a managed object. It seems logical, though, that the sectionNameKeyPath parameter should be a key path that leads to a string, since NSFetchedResultsSectionInfo's name property is a string. The fetched results controller will follow that key path for each fetched object and group the objects into sections based on what they return for that key path, and it'll also use those strings as the names of their respective sections. You can't use a managed object for the name -- you have to use some string property of the managed object.
So, your Category entity must have an attribute that distinguishes one category from another, right? Use that as the key path and (as you've seen) everything will work out.
BTW, I think it's useful to try to get out of the database (rows/fields) mindset and try to think in object-oriented terms like entity and attribute. A big selling point of Core Data is that it provides an abstraction layer that hides the storage mechanism. Thinking in terms of tables is like thinking about blocks and sectors when you're reading or writing a file.
Caleb, thank you for your answer. I do believe my understanding was wrong to some degree. What I had was an entity Category and entity Event. Category has a string field 'category', thus 'category.category' path (first 'category' is relationship in the Event entity)
What I did not take in account, though, is that if there are no events, fetchresultscontroller cannot fetch anything (similar to 'left join')
What I wanted is to show categories even if there are no events. Relationship 'category' will not return anything in this case as there is nothing to return/sort/categorize.
What I had to do (wrong or right - not sure yet) is to treat [managed] object created from Category entity as a separate object in case there are no events and place in the table. When there is one event per category, I can switch to the original method of [automatic] showing events sorted by categories.
This is interesting issue of starting point (empty entities with relationships) where I feel core data is more confusing than traditional relationship database. I also believe that's why all books/articles/reports carefully stay away from this topic. In other words, I could not find analog of "left join" in core data. May be I am wrong because I am relatively new to all this. Below is the description of the entities:
Category <-->> Event
Category - parent
Category.category - attribute of type String
Category.event - relationship to Event entity
Event - child
Event.name - attribute of type String
Event.category - relationship to Category entity
Each event belongs to one category. Category may have multiple events.
Categories should be shown even if there are no events for this category.
I was trying to put Events under fetchresultscontroller. May be I should switch to Category first and then calculate cell based on category.event relationship, not the other way around - did not try that yet.
In the context of Domain Driven Design, is a StackOverflow tag (ie. ddd ) a value object or entity?
EDIT:
Imagine, that you have to build SO website. How would you consider 'tag'?
To expand a little on awhite's answer
a tag is a value type
Why?
Because it doesn't make sense to have
var tag1 = new Tag("DDD");
var tag2 = new Tag("DDD");
Assert.AreNotEqual(tag1, tag2);
clearly they should be equal to each other because a tag has no identity except for its label. Questions and answers on the other hand are definitely entities
SO tag is most likely an entity. Tags can be created, merged, deleted and renamed. There are features like 'similar tags', user's tags etc. Some of these functions, especially life cycle, will require an identity. Classic DDD example where Person that changes his/her name is still the same person, the same identity. The same with tags where user can decide to rename "domain-driven-design" to "DDD" and it will still be the same thing. Tags also need additional attributes like tag.Id, tag.Name, tag.CreatedOn, tag.CreatedBy, tag.Locked etc. There would probably be a corresponding tags repository that can enforce name uniqueness rule.
To summarize, SO Tag is not a DDD Value Object because it is mutable and has a life cycle. More importantly, Tag is not only a characteristic of a Question (this is what I think was overlooked by other answers). It participates in a lot more relationships than that. In other words, Tag is more than just a sum of its attributes, it also has 'conceptual identity'. On the other hand TagName is a perfect example of Value Object. Its only purpose in life is to describe another entity (Tag). TagName is nothing more than just a string that may have a few built in rules like max length and case insensitive comparison. It may also make sense to simply use String instead.
Code that displays questions may use something like this:
IList<TagName> tags = question.GetTags();
Code that tags the question can look like this:
void TagQuestion(Question q, TagName tagName) {
Tag tag = _tagsRepository.FindByName(tagName);
if (tag == null) {
tag = CreateNewTag( /* capture creator, date, other rules*/);
}
q.AddTag(tag);
}
Just some additional considerations: Tags can be normalized, "DDD" should be equal to "ddd" and "DdD", and in most tag systems, spaces get replaced with "_" underscores. Also I guess the creator will be tracked for the badge system.