JackG
JackG

Reputation: 73

Using multiple repositories in application layer DDD

I'm learning about DDD and am attempting to model a basic interaction between a user and a shopping cart, and am wondering if the following code is directly aligned with DDD principles, or if there is a cleaner way to do so. I have the following two aggregate roots

class ShoppingCart {
    public id: number
    public productQuantityLimit: number
    public totalProductPriceLimit: number (will be VO in future)
    public products: List<CartItem>

    public addToCart(Product product) {
        if (productQuantityLimit > 0 && totalProductPriceLimit - product.price > 0) {
          productQuantityLimit -=1
          totalProductPriceLimit -= product.price
          this.products.append(new CartItem(product.id, product.price))
        } 
    }
}

class Product {
    public id: number
    public price: number (will be VO in future)
}

And I have an entity which is apart of the ShoppingCart aggregate root

class CartItem {
    public id: number
    public price: number (will be VO in future)
}

This CartItem represents the idea that the item in your cart is tied to your cart, but a Product is a higher order concept. So if the price of a Product changes, we do not violate the ShoppingCart's price limit invariant. In other words, if a user adds an item to their cart and then the price of that item changes, they still get the item for the original price

Now this is all good, but lets say I'm designing a REST API and I have an endpoint which adds an item to a shopping cart. That endpoint would accept a Product's, and a ShoppingCart's id, and if all invariants hold, add the item to the shopping cart. The business logic would then have to look like so:

const product = productRepository.getById(productId)
const shoppingCart = shoppingCartRepository.getById(shoppingCartId)
shoppingCart.addToCart(product)
shoppingCartRepository.save(shoppingCart)

My questions are:

  1. is it OK to inject multiple repositories into an application layer service in order to get an aggregate which is needed as a parameter in another aggregate's method?

  2. Is there a more effective way to model this where the above would not be required?

Upvotes: 0

Views: 54

Answers (1)

VoiceOfUnreason
VoiceOfUnreason

Reputation: 57307

is it OK to inject multiple repositories into an application layer service in order to get an aggregate which is needed as a parameter in another aggregate's method?

Is it "OK"? yes. Are there tradeoffs involved? Yes.

If all you are interested in is a copy of the information controlled by the product "aggregate", then it might make sense to use a read-only copy of that information, rather than passing around the "aggregate" with all of its affordances for making changes to that data.

Also, in the general case, you need to think carefully about whether you need to have a (logical) lock on that information, to prevent it from being changed while you are assuming it has a particular value.

In short, if what you really need is an affordance that allows you to exchange a productId for a read only value, then a "repository" (a facade that supports the illusion of a local collection of "aggregates") might not be the best thing to inject into your message handling logic.

Also, you might be missing some domain concepts in your model that makes things easier - i.e. the idea of a "quoted price" or "offer" - a promise to honor a particular price if accepted within a particular time window. That might be easier in so far as (a) it separates the problem of calculating an offer to propose to the customer from the problem of documenting that a customer has (provisionally) accepted that offer and (b) gives you an hook to start collecting information about which offers are accepted and which are declined, so that your sales/marketing teams can revise their strategies based on the data you are collecting.

Upvotes: 0

Related Questions