It's rather common knowledge that aggregate root provides an interface for every state change within that aggregate. For instance, aggregate root checks some business rules before some dependent entity is modified.

Now, according to Microsoft guideline for API design, URI /customers/1/orders/99/products should be split into /customers/1/orders and /orders/99/products to avoid long and complicated resource paths.

Let's assume that:

  • The Customer is the only aggregate root in this domain,
  • Every identifier in this domain is a globally unique identifier.

I know that for this particular example, Order should probably be another aggregate root but for now let's say it's just a dependent entity in the Customer aggregate.

Now, the HTTP API needs to handle requests coming with the path /orders/99. How the endpoint handler should call the domain if the HTTP request is missing the Customer (aggregate root) context?

Is it allowed to query a database directly just to obtain the aggregate root id for an incoming entity and then normally perform the operation on aggregate? Or maybe each HTTP request should contain the aggregate root reference (url or header)? What's the good practice on this topic?

  • If your Customer aggregate is small enough that you can retrive it all at once, why do you need a seperate orders endpoint, vs just having an /customers/1/orders that also returns all the products. Or to look at it the other way, if you need to split the API, does that not suggest that the aggregate root may be to big too be retrieved from the database for every operation, and so the orders should be split into a seperate aggregate root. Jun 30, 2023 at 12:56
  • 3
    It is not allowed. The DDD police will get you.
    – Erik Eidt
    Jun 30, 2023 at 15:14
  • The question is never if you can do X. You always can do X, regardless of what others say. The real question is what would you be solving by splitting the URI this way. The what will dictate where the customer id will come from.
    – Laiv
    Jul 2, 2023 at 7:09

2 Answers 2


Is it allowed in DDD application to expose HTTP API endpoints for objects different than aggregate roots?


It's not necessarily easy; and you may have to balance certain trade offs.

First important idea: DDD is fundamentally a pattern language. There is a certain amount of freedom implied by that

you can solve the problem, in your way, by adapting it to your preferences, and the local conditions at the place you are making it

If the conditions surrounding your design are different from those Evans was in 20 years ago, the expressions of a pattern in your design is likely to differ from the expression in his.

Second important idea: resource models and domain models are different things -- see Amundsen 2016. It's normal (but not necessarily common) that your resource model might include information associated with more than one aggregate, or even more than one domain model.

Third important idea: CQRS. Reads and writes don't necessarily need to be coupled to the same representations of information - especially if you are in a context where you are sharing unlocked data (see Helland: Data on the Outside....)

Taken together, it's perfectly acceptable to respond to a "give me the current representation of /orders/99" by saying "OK, here's a copy of the web page we cached 5 minutes ago after rebuilding it from data associated with customer 1."

And if you get that data by just querying the database? that's fine.

Things that can potentially get tricky:

  • Does the resource identifier for orders give you enough information to correctly identify the aggregate?
  • What does it mean to send an unsafe (PATCH/PUT/POST/DELETE) message to the order resource, when we are expecting the domain model aggregate to ensure that there are no data conflicts?

Depending on local conditions, cost effective solutions to those problems may conflict with the Microsoft guidelines, and you may have to choose which is more important to you.


The interface of the aggregate in DDD is the only part that's public. If the aggregate is Customer which has a list of internal entities Order, it should not be possible to change Orders directly. It does not matter if the Id of Order is globally unique or not, it should not be referenced directly.

So you are right that you somehow have to get a hold of the aggregate. DDD does not dictate how you should do that. When you run into this issue however, it's a sign that your aggregate might be too big. In this example it's quite clear, but if you have the same situation in your domain, try to come up with another design that satisfies the business rules but with smaller aggregates.

Note that the aggregate pattern is a consistency boundary to ensure the state of the system is consistent when applying commands. For queries, working with aggregates is often not ideal because you don't always need all data. Patterns such as CQRS can help with this mismatch, that doesn't have to be a full-blown complex system, a simple read-only query layer goes a long way.

  • But HTTP resources are at the confluence of functional and technical concerns. Should the consistency boundary leak out down to that level? Should API clients know about it? Jun 30, 2023 at 15:18
  • @guillaume31 updated the answer, is it more clear now?
    – Rik D
    Jun 30, 2023 at 15:33
  • Thank you, it makes sense. I know that domain part shouldn't be too tightly coupled with other application layers, such as REST API for example. On the other hand I want both layers to work with my applicaton purposes: domain to fulfill business requirements and http api to fulfill clients needs. It's difficult to find a good balance.
    – Jacek
    Jun 30, 2023 at 16:23
  • @RikD not really clearer. My point was that you might be letting DDD concerns (consistency boundary/transactional) leak down to the HTTP side of things. If a user wants to change an Order, why shouldn't the endpoint be /orders/..., regardless of what happens under the hood afterwards? But I'd gladly let myself be convinced of the opposite, hence the question marks... Jul 4, 2023 at 8:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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