0

Not quite sure how to approach this problem regarding DDD.

Say you have 2 domains:

  • A Product domain which is responsible for creating new and managing existing Products which a person has created. Product Aggregate Root
  • A Store domain which is responsible for assigning all created Products to a given Store to be sold, and other stuff. But the Store can also create new Products which will be assigned to themselves but also available for other Stores so that they can assign the Product to themselves. Store Aggregate Root

A Product can exist without belonging to a Store. A Store can exist without having any Products in it whatsoever (doesn't make sense I know, but this is just a simple example of a complex solution I'm working on atm.)

So when a Person comes along into the system, they can start at either end. They can start by creating new Products or they can start by adding a new Store.

Here is where it gets complicated, when they create a new Store, they are given the option to add existing Products or they can create a new Product and add it to the Store.

How do you deal with this use case. is the does the Store have a behavior to CreateNewProduct where it's responsible to setting up the new Product and then adding it to the Store. Or do you simply create a new Product outside the Store Domain as part of the Product domain, and tell the Store to AddProduct / AddExistingProduct?

UPDATE: Would something like this be appropriate as a Domain Service

public class StoreProductService {

    public Store CreateNewStoreProduct (Store store, string sku, string name, etc){

        Product newProduct = ProductFactory.CreateNewProduct(sku, name, etc);

        store.AddProduct(newProduct);

        return store;
    }
}
Shane van Wyk
  • 1,870
  • 1
  • 26
  • 62
  • 1
    You need a third domain which should act as a fascade pattern. So this third domain's job is to create store or product or associate them together. And the fascade pattern will help you to hide the complexity of the system by providing a simpler interface to access product and store domain, – adeel41 Mar 07 '16 at 21:58
  • I'm with adeel here; you need a third concept - a `ProductFactory` or suchlike. Products don't create themselves, nor do Stores create Products - they just contain them. – Stephen Byrne Mar 07 '16 at 22:05
  • Yeh I have a `ProductFactory` that creates a new product for me. But do you then let the `Store` `AddProduct(product)` or CreateNewProduct(sku, name, etc)`. Which means that the `Store` Entity will need to know about the `ProductFactory`. So obviously you will have a `Store` Application Service which deals with adding new product. So in that create method do you make use of `ProductFactory` and pass it to the store once it has been created? – Shane van Wyk Mar 07 '16 at 22:14
  • @StephenByrne - See Comment Above – Shane van Wyk Mar 07 '16 at 22:32
  • 4
    Are you confusing "domain" with "aggregate" ? – VoiceOfUnreason Mar 07 '16 at 22:36
  • @VoiceOfUnreason - Probably, I'm new to DDD. I would say that `Store` and `Product` are 2 Aggregate Roots because they can exist by themselves. But not sure if an aggregate Root is allowed to Interact with another without an Intermediate class / object / service. Ideally I would like an answer with an example of a good approach for this. – Shane van Wyk Mar 07 '16 at 22:40
  • The general rule of thumb is that if an aggregate requires another aggregate for an interaction, it is done through a domain service. The child objects in the object tree of each aggregate should never be modified directly. Any modification to the aggregate should go through the root entity in the aggregate. – Adrian Thompson Phillips Mar 08 '16 at 10:13
  • @AdrianThompsonPhillips please see updated question, would this be a appropriate approach to this problem? – Shane van Wyk Mar 08 '16 at 10:44
  • Maybe something a bit like `StoreProductService.CarryProductInStore(myProduct, myStore)`. This uses the language of the domain and allows products to be set up in advance of being assigned to any store. – Adrian Thompson Phillips Mar 08 '16 at 12:59
  • UPDATE: no, that's the wrong direction to go. – VoiceOfUnreason Mar 08 '16 at 18:52
  • @ShaneVanWyk you still haven't clarified whether `Product` and `Store` are two *Subdomains*, i.e. big functional areas that can evolve separately, each with their own business teams and possibly dev teams, or two *Aggregates* inside the same subdomain. – guillaume31 Mar 09 '16 at 12:53
  • @guillaume31 - Well they can evolve separately, So while I work on store, another dev can work in product. The business rules are that one can create a store, that store can just have a name and be empty. It will be a useless store but that's fine, the domain expert wants it that way. However, the the store can have products / services in it, I put services in here to make it more simple. A store can choose to add from an existing Catalog or create their own, but when they create their own, it needs to be added to the catalog also, So other stores can use it. – Shane van Wyk Mar 10 '16 at 22:11

2 Answers2

1

This business problem is very common. You surely can find some of examples about eCommerce systems with DDD.

Typical situation is that you have not finished developing your UL. If you discuss the Store (I won't mark it as code since I am talking about UL terms, not classes) with your domain experts, you can find out that Store only cares about Product Availability, not the Product itself.

Typically, your Product that belongs to a Product Catalogue, has little to do with the availability. It might have general description, manufacturer details, country of origin, picture, package size and weight and so on.

Product Availability, however, has, well, the availability, i.e. count of available product items. It might have some extra details, for example a count of reserved items, expected to arrive items and so on.

The fact is that often both of these concepts, despite they are rather different, are referred by domain experts as "Product". This is a typical example of two separate Bounded Contexts.

As soon as you have two different things that are called Product in two different Bounded Contexts (and physical places), you will need to keep them in sync. This is usually done by publishing domain events outside of the Bounded Contexts and updating the other side by subscribing to these events there and doing internal domain command handling inside the Bounded Context.

Alexey Zimarev
  • 17,944
  • 2
  • 55
  • 83
  • Tried looking for E-commerce Solutions , can't really find an example where there is a store involved too. Seeing an example like this, would help me immensely. However, even tho this might look like a e-commerce solution, its not, I have substituted the real domains with product and store because it is close enough, and has the same logic as what we are trying to do. I can't give an actual real example as its protected by I.P. – Shane van Wyk Mar 10 '16 at 22:14
  • The issue here is that each design is very specific to the actual domain. It is nearly impossible to give specific design advice without getting to know your domain and build UL and BCs. Essentially, as Vernon says, DDD is "developing ubiquitous language inside bounded context". In your case, since it is all secret, you are on your own, I'm afraid... e-commerce example with context mapping can be found in IDDD book. – Alexey Zimarev Mar 11 '16 at 08:31
1

You need the two domains to communicate with each other. You can communicate using various methods but one common way is to use a REST API. In your Store domain, you need something that communicates or knows how to communicate with the API. I have generally implemented these as a service that wraps api calls. The service will be able to understand your domain Ubiquitous language and will translate that into API commands. You can use domain events to possibly listen for when the product has been created and then you can associate the store with the product or you can implement some kind of polling mechanism.

An example was requested, so here goes:

Call from UI (probably from a controller)

StoreService.CreateStore(storeName: String, newProduct : StoreProduct) You could pass in primitives to represent the newproduct. This allows you to create a new product without the need for a new class for it. You subsequently don't need a shared infrastructure class or converter between your domain and UI layers.

StoreService

public StoreService
{
      public StoreService(ProductApiClient productApiClient...)...
      public void CreateStore(string StoreName, StoreProduct prod...)
      {
             var newStore = StoreFactory.Create(storeName);
             //below might be better as an asynch call but for simplicity
             //keeping it all synchronous
             var newProd = productApiClient.CreateProduct(prod);
             //check if product was created successfully, if so
             newStore.Add(newProd);
             //save
             StoreRepo.Create(newStore);
             //check if store was created successfully
       }

The main points to take from here:

  1. Create your aggregate entity using a factory (at this state it has not being persisted).
  2. You need a repo to serialize and persist your new store to your data store.
  3. productApiClient will translate between your domain model and a request to the productApi. It knows how to generate an Api request (if REST for example) given domain entities.
  4. ProductApiClient will return a domain entity from the Api Response.
  5. You add a new product to a store using the store entity.
  6. You persist your store using the repo
  7. You may choose to pick a more asynchronous approach if the call to the api is time consuming.

This example illustrates the power of DDD and the separation of concerns. Depending on the size of your project and your requirements, this might be overly tedious so fit it to your needs, skills and requirements.

Louis
  • 1,194
  • 1
  • 10
  • 15
  • Would it not be better to have application services? So for example, you have a `StoreService` which is an application service and it knows of `ProductFactory`. So from your UI layer, you will call `StoreService` `CreateNewrProduct`, this will then call `ProductFactory` `CreateProduct`, The result of that will be returned to the store to add the newly added product. – Shane van Wyk Mar 10 '16 at 22:18
  • Then you are merging the two domains or letting logic leak. Store service should not know anything about products except what the products domain exposes in its API. Your UI layer should call CreateNewStore (if interacting with the store domain) and pass it enough information to where the application service can create a new product if it needs to (using the api exposed by the product domain). Remember you may need to do things like "check for availability" or some other product specific actions.That's why you need to isolate enough to where your store domain doesn't have to know these things. – Louis Mar 11 '16 at 21:11
  • That makes sense. Are you able to update your answer with a hyperthetical example? – Shane van Wyk Mar 11 '16 at 22:52