Are aggregate-wide events acceptable? - domain-driven-design

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.

Related

Shopware 6: How to 'tap in' to the update method of an entity?

I have tried to find a solution, but maybe I'm searching for the wrong term.
I want to modify an Entity (a custom one) when the update for the DAL is called. But I cannot seem to find what method to implement in my Entity.
What I'm looking for is a kind of 'hook'. I thought that maybe I could create a Service and look for a specific event, but there is only events for Products, Categories etc.
Should I implement an event for my Entity or is there something more general?
There is a general EntityWrittenEvent that is dispatched whenever the DAL writes an entity. When you want to listen to the written event for a specific entity you can do so by creating a EventSubscriber that listens on the {entity_name}.written event.
The event classes for Products and Categories etc. are basically just constants for the same event, but the event name is generic. E.g instead of using ProductEvents::PRODUCT_WRITTEN_EVENT in your event subscriber you can listen on product.written as that is internally the same.
Also take a look at the official docs there too product is used as an example, it works the same way for every entity (even custom ones).

Need an opinion about abstractions and update process in a domain model

I have an Agenda that holds many Card, and a Card has multiple DetailItem that hold a value like Email, Phone and a Label
So, I can do:
agenda = new Agenda()
oneCard = new Card()
item = new DetailItem(new Email("x#y.z"), new Label("Work")
oneCard.addItem(item)
agenda.addCard(oneCard)
By rule, there can only be one DetailItem with an Email instance with "x#y.z" value, so if you try to add a new item with that data it will raise an exception.
This seems to be ok until I try to update a DetailItem. I can't find a way that I feel comfortable with.
I try to think in terms of business model and not implementation details, but I cannot leave them off for a long, and they enter the domain I like it or not.
The thing is that I will have a REST interface, and I have two ways of doing things.
Send a PUT to /cards/<cardId> with a detailItems' array, fetch the Cardby ID, create a newCardwith the new data provided in the PUT, and sync the currentCard` with the new one.
Send a PUT to /cards//items/, fetch the Card, find the DetailItem, and update it the same way in option 1
If I go with option 1, I have to remove all DetailItem from current Card that don't exists in the new one. That leads with some INSERT queries produced by the ORM, and some DELETES. No UPDATE at all.
If I go with option 2, I will have many PUTs to change multiple items, which is not performant at all, and it will lead me to introduce an ID field to the DetailItem so I can identify them, which introduces something is not part of the domain!.
The only option I found was to go with option 1, and send a request like this:
{
...
detailItems: [
{
type: "email",
oldValue: "x#y.z",
oldLabel: "Work",
newValue: "a#b.c",
newLabel: "Work",
}
]
}
So I can do something like this:
card = agenda.getCardIdentifiedBy(the_identifier)
for(itemUpdate in jsonData.detailItems)
itemClass = ValueClassMapper.from(itemUpdate.type) // "email" -> Email
oldItem = new DetailItem(new itemClass(itemUpdate.oldValue), new Label(itemUpdate.oldLabel))
newItem = new DetailItem(new itemClass(itemUpdate.newValue), new Label(itemUpdate.newLabel))
card.updateItemWith(oldItem, newItem)
But, I don't know, maybe I have a wrong abstraction
Can anyone help?
Thanks in advance!
Part of the problem that you are running into here is that HTTP remote authoring semantics are at odds with the notion of an autonomous domain model.
HTTP PUT means "make your copy of this document look like my copy of this document". It is analogous to "save file", or "commit these rows to the database". The semantics of the request fit the model of an anemic data store.
On the other hand, our domain models are typically autonomous state machines, that expect to decide for themselves what state they should transition into based on new information. In other words, all domain model interactions roughly follow the following pattern:
domainModel.change(newInformation)
The model then changes its own data structures in response to this information (with the application code responsible for parsing the new information into a data structure that the domain model will understand).
This doesn't mean that you can't use HTTP and REST with domain models; but it does mean that the resource model looks very different from what you have described here.
The most straightforward approach would be to POST the new information to your agenda resource (autonomous domain entities really don't want you trying to directly modify their internals).
POST /agenda/12345
Content-Type: application/json
{
...
}
It would then be your application's responsibility to parse the body of the request, and then to pass that parsed representation along to your agenda domain entity, the domain entity would then decide how/if to integrate the new information with the information that it already knows about.
Another approach would be to treat the new information as a new document to be stored, where each new document would have its own URI:
PUT /messages/67890
Content-Type: application/json
{
...
}
Where the changes to /agenda/12345 are a side effect of storing the message -- see Webber 2011. Please be aware that this approach can make cache invalidation more challenging.

DDD/CQRS: Combining read models for UI requirements

Let's use the classic example of blog context. In our domain we have the following scenarios: Users can write Posts. Posts must be cataloged at least in one Category. Posts can be described using Tags. Users can comment on Posts.
The four entities (Post, Category, Tag, Comment) are implemented as different aggregates because of I have not detected any rule for that an entity data should interfere in another. So, for each aggregate I will have one repository that represent it. Too, each aggregate reference others by his id.
Following CQRS, from this scenario I have deducted typical use cases that result on commands such as WriteNewPostCommand, PublishPostCommand, DeletePostCommand etc... along with their respective queries to get data from repositories. FindPostByIdQuery, FindTagByTagNameQuery, FindPostsByAuthorIdQuery etc...
Depending on which site of the app we are (backend or fronted) we will have queries more or less complex. So, if we are on the front page maybe we need build some widgets to get last comments, latest post of a category, etc... Queries that involve a simple Query object (few search criterias) and a QueryHandler very simple (a single repository as dependency on the handler class)
But in other places this queries can be more complex. In an admin panel we require to show in a table a relation that satisfy a complex search criteria. Might be interesting search posts by: author name (no id), categories names, tags name, publish date... Criterias that belongs to different aggregates and different repositories.
In addition, in our table of post we dont want to show the post along with author ID, or categories ID. We need to show all information (name user, avatar, category name, category icon etc).
My questions are:
At infrastructure layer, when we design repositories, the search methods (findAll, findById, findByCriterias...), should have return the corresponding entity referencing to all associations id's? I mean, If a have a method findPostById(uuid) or findPostByCustomFilter(filter), should return a post instance with a reference to all categories id it has, all tags id, and author id that it has? Or should my repo have some kind of method that populates a given post instance with the associations I want?
If I want to search posts created from 12/12/2014, written by John, and categorised on "News" and "Videos" categories and tags "sci-fi" and "adventure", and get the full details of each aggregate, how should create my Query and QueryHandler?
a) Create a Query with all my parameters (authorName, categoriesNames, TagsNames, if a want retrive User, Category, Tag association full detailed) and then his QueryHandler ensamble the different read models in a only one. Or...
b) Create different Queries (FindCategoryByName, FindTagByName, FindUserByName) and then my web controller calls them for later
call to FindPostQuery but now passing him the authorid, categoryid, tagid returned from the other queries?
The b) solution appear more clean but it seems me more expensive.
On the query side, there are no entities. You are free to populate your read models in any way suits your requirements best. Whatever data you need to display on (a part of) the screen, you put it in the read model. It's not the command side repositories that return these read models but specialized query side data access objects.
You mentioned "complex search criteria" -- I recommend you model it with a corresponding SearchCriteria object. This object would be technnology agnostic, but it would be passed to your Query side data access object that would know how to combine the criteria to build a lower level query for the specific data store it's targeted at.
With simple applications like this, it's easier to not get distracted by aggregates. Do event sourcing, subscribe to the events by one set of tables that is easy to query the way you want.
Another words, it sounds like you're main goal is to be able to query easily for the scenarios you describe. Start with that end goal. Now write your event handler to adjust your tables accordingly.
Start with events and the UI. Then everything else will fit easily. Google "Event Modeling" as it will help you formulate ideas sound what and how you want to build these style of applications.
I can see three problems in your approach and they need to be solved separately:
In CQRS the Queries are completely separate from the Commands. So, don't try to solve your queries with your Commands pipelines repositories. The point of CQRS is precisely to allow you to solve the commands and queries in very different ways, as they have very different requirements.
You mention DDD in the question title, but you don't mention your Bounded Contexts in the question itself. If you follow DDD, you'll most likely have more than one BC. For example, in your question, it could be that CategoryName and AuthorName belong to two different BCs, which are also different from the BC where the blog posts are. If that is the case and each BC properly owns its own data, the data that you want to search by and show in the UI will be stored potentially in different databases, therefore implementing a query in the DB with a join might not even be possible.
Searching and Reading data are two different concerns and can/should be solved differently. When you search, you get some search criteria (including sorting and paging) and the result is basically a list of IDs (authorIds, postIds, commentIds). When you Read data, you get one or more Ids and the result is one or more DTOs with all the required data properties. It is normal that you need to read data from multiple BCs to populate a single page, that's called UI composition.
So if we agree on these 3 points and especially focussing on point 3, I would suggest the following:
Figure out all the searches that you want to do and see if you can decompose them to simple searches by BC. For example, search blog posts by author name is a problem, because the author information could be in a different BC than the blog posts. So, why not implement a SearchAuthorByName in the Authors BC and then a SearchPostsByAuthorId in the Posts BC. You can do this from the Client itself or from the API. Doing it in the client gives the client a lot of flexibility because there are many ways a client can get an authorId (from a MyFavourites list, from a paginated list or from a search by name) and then get the posts by authorId is a separate operation. You can do the same by tags, categories and other things. The Post will have Ids, but not the extra details about those IDs.
Potentially, you might want more complicated searches. As long as the search criteria (including sorting fields) contain fields from a single BC, you can easily create a read model and execute the search there. Note that this is only for the search criteria. If the search result needs data from multiple BCs you can solve it with UI composition. But if the search criteria contain fields from multiple BCs, then you'll need some sort of Search engine capable of indexing data coming from multiple sources. This is especially evident if you want to do full-text search, search by categories, tags, etc. with large quantities of data. You will need to use some specialized service like Elastic Search and it won't belong to any of your existing BCs, it'll be like a supporting service.
From CQRS you will have a separeted Stack for Queries and Commands. Your query stack should represent a diferente module, namespace, dll or package at your project.
a) You will create one QueryModel and this query model will return whatever you need. If you are familiar with Entity Framework or NHibernate, you will create a Façade to hold this queries togheter, DbContext or Session.
b) You can create this separeted queries, but saying again, if you are familiar with any ORM your should return the set that represents the model, return every set as IQueryable and use LET (Linq Expression Trees) to make your Query stack more dynamic.
Using Entity Framework and C# for exemple:
public class QueryModelDatabase : DbContext, IQueryModelDatabase
{
public QueryModelDatabase() : base("dbname")
{
_products = base.Set<Product>();
_orders = base.Set<Order>();
}
private readonly DbSet<Order> _orders = null;
private readonly DbSet<Product> _products = null;
public IQueryable<Order> Orders
{
get { return this._orders.Include("Items").Include("Items.Product"); }
}
public IQueryable<Product> Products
{
get { return _products; }
}
}
Then you should do queries the way you need and return anything:
using (var db = new QueryModelDatabase())
{
var queryable = from o in db.Orders.Include(p => p.Items).Include("Details.Product")
where o.OrderId == orderId
select new OrderFoundViewModel
{
Id = o.OrderId,
State = o.State.ToString(),
Total = o.Total,
OrderDate = o.Date,
Details = o.Items
};
try
{
var o = queryable.First();
return o;
}
catch (InvalidOperationException)
{
return new OrderFoundViewModel();
}
}

Retrieving a value object without Aggreteroot

I'm developing an application with Domain Drive Design approach. in a special case I have to retrieve the list of value objects of an aggregate and present them. to do that I've created a read only repository like this:
public interface IBlogTagReadOnlyRepository : IReadOnlyRepository<BlogTag, string>
{
IEnumerable<BlogTag> GetAllBlogTagsQuery(string tagName);
}
BlogTag is a value object in Blog aggregate, now it works fine but when I think about this way of handling and the future of the project, my concerns grow! it's not a good idea to create a separate read only repository for every value object included in those cases, is it?
anybody knows a better solution?
You should not keep value objects in their own repository since only aggregate roots belong there. Instead you should review your domain model carefully.
If you need to keep track of value objects spanning multiple aggregates, then maybe they belong to another aggregate (e.g. a tag cloud) that could even serve as sort of a factory for the tags.
This doesn't mean you don't need a BlogTag value object in your Blog aggregate. A value object in one aggregate could be an entity in another or even an aggregate root by itself.
Maybe you should take a look at this question. It addresses a similar problem.
I think you just need a query service as this method serves the user interface, it's just for presentation (reporting), do something like..
public IEnumerable<BlogTagViewModel> GetDistinctListOfBlogTagsForPublishedPosts()
{
var tags = new List<BlogTagViewModel>();
// Go to database and run query
// transform to collection of BlogTagViewModel
return tags;
}
This code would be at the application layer level not the domain layer.
And notice the language I use in the method name, it makes it a bit more explicit and tells people using the query exactly what the method does (if this is your intent - I am guessing a little, but hopefully you get what I mean).
Cheers
Scott

Core Data: Can relationship be used for sectionNameKeyPath?

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.

Resources