I am working on understanding DDD and want to see if this idea is sound, if it is common practice, and if there are any pitfalls or unforeseen complications.

I want to effectively have one "entity" with a globally unique primary key. I want to use event sourcing. I would like have different AR classes represent this "entity" depending on the context.

For example, in an ecommerce site I'd have a few different types of pages (static content pages, category pages, product pages). I'd also have products. There should be a 1:1 mapping between a product and a product page, effectively a ProductPage subtype of Page with a single, unique ID representing both the Page and the Product components.

When in the content management context, I'd load all Page events for that ID to reconstruct my Page AR object, do any Page related actions, and save out the appropriate Page events (ex. Page.EditContent()).

When in the product management context, I'd load all the Product events for that ID to reconstruct the Product AR object, do any Product related actions, and save out the appropriate Product events (ex. Product.SetPrice()).

Then, when generating my read model, I'd load all the events for that ID to construct a ProductPage and save it to the read model database.

Is this a correct way of going about this or is this a bad idea for some reason I can't see? Or is there a better way?

  • The "correct" way is the way that most effectively satisfies your software's functional and non-functional requirements. Have you performed that evaluation? – Robert Harvey Nov 9 '17 at 16:24
  • Read side, there should be a concept of a single ProductPage, with both page details (title, url, content) and product/ecommerce details (eg. sku, price, photos). Write side, when dealing with the page, it should share logic and functionality with all other pages, not caring that there is also product information attached to the ID. Similarly, product/ecommerce functionality (pricing, ordering, shipping, etc) doesn't care about content, url, or other Page concerns. – Entith Nov 9 '17 at 16:38
  • To me, this seems like the correct approach, keeping context specific concerns and logic within that context, allowing each context to view the "entity" in the way that makes sense and ignoring irrelevant data. I just have no experience with event sourcing and don't know if this approach will end up creating a lot of accidental complexity. Previous CRUD-style projects have started with ideal ideas for organization and DRY-ness, but have quickly become much more complicated due to unforeseen issues (SQL servers lack of support for multiple cascading delete paths is my data model's worst enemy) – Entith Nov 9 '17 at 16:51
  • Your description matching entities to views seems impractical, unless you're simply invoking CRUD operations. Most non-trivial applications will defer to composite view-model type classes for their page views instead, because the page will require information from several different tables. An invoice, for example, draws from: Customer, Address, Invoice, and InvoiceLineItem tables. – Robert Harvey Nov 9 '17 at 18:08
  • I'm not sure what description you are referring to, I haven't said anything about views. – Entith Nov 9 '17 at 20:37

I recently answered a different question with a similar answer here:

Can aggregate root reference another another root?

I think you are actually approaching your problem perfectly.

I think mapping aggregate roots and entities to different classes suitable for each context is a safe an extensible approach. Whenever new guys join the team they truly don't have to worry about breaking something in the system. They can map a new aggregate root and build a feature as if they were building a separate app.

This comes with the obvious cost of much more code and the false perception that code is being duplicated. In the past I experimented with having a single 'User' class to represent users in all contexts (login, mailing, investment management, etc). It is now a several thousand line mess of cross cutting concerns that no one wants to touch.

For the read side of the system we use a different ORM all together. One suitable for fast, read-only operations without state tracking capabilities. These draw from many different sources in the data store and map to flat models that are read-only and offer no logic. Mostly this results in one query and one model per use case, which we've found is easy to maintain since different contexts rarely require the exact same data. We avoid loading multiple ARs and then mapping from those to a view model. That process is more expensive than it has to be.

There are of course exceptions to any rule. If you have a simple entity and only 1 or 2 foreseeable contexts with a 90% overlap, map it to one entity. You can always split it up at a later stage.

This is just my experience with DDD on some of the stuff I've worked on. It might not be applicable in all situations but it sounds like you know what you are doing.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.