1

Let's use https://github.com/dotnet-architecture/eShopOnContainers as an example. There are CatalogService, BasketService and there is ApiGateway. In api gateway in BasketController there is AddItemToBasket handler (https://github.com/dotnet-architecture/eShopOnContainers/blob/dev/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs#L123) which first calls catalog service to get catalog item (by catalog item id from request), then gets or creates basket and then it updates basket with new item added, checking if items with this id has not been already added to basket, and if it has then it only updates quantity of that item in basket.

So quite amount of "business logic" happens in this controller. I know that eShopOnContainers is not strict DDD example, and I wanted to know if this particular pattern/example is "acceptable" when you follow DDD principles?

What I noticed is that it simplifies (and decouples it from catalog service) basket service because it does not have to call catalog service by itself. On the other hand there is some logic (specifically updating quantities instead of adding item which already exists in basket) that for sure should be implemented in basket service and whole basket should (I think) be implemented as aggregate.

So from DDD point of view basket service should receive the request to add new items, then call (asynchronously through event broker would be the best) catalog service and update the basket. Or eventually basket controller could call catalog service to get items from catalog and call basket api to add those items.

While I know that basket service is simplified in this repo/example, I wonder if it's common approach, regarding DDD, that if there is service A that needs some kind of read model from service B to make decision (like basket service need some data from catalog service) then instead of calling (even asynchronously) service B from A, API gateway first calls service B to fetch some read model from it and then it calls service A passing this read model / required data directly to it?

4
  • see What is the problem with "Pros and Cons"?
    – gnat
    Commented Sep 12, 2021 at 9:54
  • I updated question - finally it's not pros and cons what I want to know, but rather is this is common/acceptable approach in DDD terms.
    – user606521
    Commented Sep 12, 2021 at 10:01
  • Imo this question is not about DDD, but more about the overall architecture of an application. If I understand your question, your asking: when Service B depends on data from service A, should service B be responsible for fetching that data, or is it better to have an orchestrating service that fetches data from A and provides that to B.
    – Rik D
    Commented Sep 12, 2021 at 10:12
  • Yes exactly. I mentioned DDD because I am working on event driven architecture that follows DDD patterns. What I planned was having API gateway that only performs authentication and some view composition and all cross-microservice communication implemented by microservices themselves. Then I saw this example in eShopOnContainers api gateway and I started wondering if this is good approach to perform this kind of communication on api gw level to simplify microservices. Especially I am talking about read-models needed to make decision (services still need to communicate with domain events anywa
    – user606521
    Commented Sep 12, 2021 at 10:19

2 Answers 2

1

My take on this is that the "ApiGateway" services are acting as a "backend for front end" and converting between the front ends ViewModels and the back ends Models with some simple logic. You could add a service layer, but its unimportant to the overall architecture.

You could just as easily do this kind of "check the item Ids are valid" logic in the front end and connect direct to the back-end services, but you cant do it in the back-end services without coupling them.

The basket service accepts the itemId as an external Id from another system and it doesn't care if that other system has the item or not. Adding that kind of coupling is something that they have chosen not to do when they choose the aggregate roots for the system as a whole and you wouldnt want to add it back in for fear of ending up with one massive service, rather than multiple microservices.

This choice is a key factor in DDD and questions such as:

  • "what do i do when i need to actually deliver the items in a basket when the catalog doesn't know what they are?"
  • "how can i ensure the basket the customer is putting together only includes items from the catalogue?"

Are left to higher layer "Delivery" and "WebShop" Domain Services or Objects. You have to be careful to keep your services hierarchical, loosely coupled and avoid circular references.

A common pitfall for example would be to add a ICatalogue reference to the Basket service and then later add a IBasket reference to the Catalogue service. Everything would compile, but in reality you wire up one service to the other and have potential infinite loops, or at least inefficient calling back and forth and your choice of ARs is called into question.

7
  • Are you saying that there could be another (micro)service "WebShop" that would handle interaction between Basket and Catalog (you mentioned "domain services" which usually means services passed to aggregate methods because they can't be implemented inside aggregates - I guess you meant just "(micro)service")? As for hierarchy for me it feels all right that Basket is aware of Catalog, but Catalog does not have to know anything about Basket - I guess it would be my first choice.
    – user606521
    Commented Sep 12, 2021 at 19:09
  • 1
    essentially the apigateway is that "webshop" layer, questionable naming/design, but i guess they are trying to fit everything in. I've always thought of domain services as the opposite, services which are outside the domain objects and combine them
    – Ewan
    Commented Sep 12, 2021 at 19:39
  • 1
    base layer services should never be coupled, the question is whether the AR should include basket AND catalogue or whether they should be separated. That's a choice you have to make when designing the overall solution, but you will always end up with some boundaries like that somewhere. I guess what DDD is saying is that its OK for a basket to have an item that doesnt exist in it. the business allows for that to be a thing and so should your code.
    – Ewan
    Commented Sep 12, 2021 at 20:20
  • 1
    I think i will say yes, no coupling even by, or maybe especially by, events. Its fine for higher levels services to have logic, its fine to have higher and higher level services, but as soon as you introduce a cross Domain call at the same level of services you are in a world of orchestration pain.
    – Ewan
    Commented Sep 13, 2021 at 17:14
  • 1
    of course you could maybe think up an alternate to the hierarchy of services to prevent circular calls, you just have to stick to the rule
    – Ewan
    Commented Sep 13, 2021 at 17:18
1

Look again your question and I realize that you want to add a service that can validate the cross data between the services, and wanna put that on API Gateway, right?

It's really no need to do that. For example with your models, CatalogService is a bounded context. Catalog and Product are the aggregates in that bounded context. But in BasketService context, Product will be an entity. Therefore, I think the good solution for this is the duplication data of products in BasketService context.

Whenever a new product is added, or a product is updated in CatalogService. Domain Model will raise an event. And saga will do it roles to copy or update product data to other services. This will keep your BasketService work as expected and still can validate its own business rule, with the related data from CatalogService.

3
  • And where this saga should "live" in? In API GW or one of the services?
    – user606521
    Commented Sep 23, 2021 at 5:29
  • 1
    Saga is a part of each Bounded Context, can be considered as a services. A saga is a sequence of local transactions. Each local transaction updates the database and publishes a message or event to trigger the next local transaction in the saga. If a local transaction fails because it violates a business rule then the saga executes a series of compensating transactions that undo the changes that were made by the preceding local transactions.
    – Hung Dinh
    Commented Sep 23, 2021 at 7:42
  • 1
    For more clarification, API Gateway is a service (or can be considered as a wrapper layer) for your API endpoints. Gateway doing it jobs (load balancing, limiting api call, may apply some protection, blah blah...) and it will has no relation with every other Bounded Context's business.
    – Hung Dinh
    Commented Sep 23, 2021 at 7:47

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.