0

A little background of my problem. I have a set of the following services:

  • AdapterService - intended for loading certain products from an external system
  • ApiGateway - accepts requests from UI. In particular, now there is only one request that receives product data to display product in UI from Product Service
  • ProductService - data storage service for various products. The service itself does not specifically know what kind of product it specifically stores. All types of products are created dynamically by other services that are responsible for these products. Product data is stored as a key-value map (technically it is a json string in DB column)

There is a schema for service interations

So, services in BLUE zone are mine (they can be changed in any way). RED zone describes services of another team (they can't be changed).

Whats the problem

To load product from external system I want to use SpecialProductDto which will store product data. I can use some validation features like Spring annotations and so on. Then to load the product from Adapter Service to ProductService I must transform SpecialProductDto to Map<String, Object> because ProductSerivcie requires it via API.

When I would get product info for UI through ApiGateway, I will need to call ProductService api for getting product that return attribues in Map<String, Object> and then transform this data to some UIReponse which contains some part of product data (because I dont need all product information, just only name and price for example).

But I also want to use SpecialProductDto in my ApiGateway service, because it seems working with Map<String, Object> is error prone... I practically need to fetch data blindly from Map to construct UIResponse. And what if some attribute names will be changed? With Map I only will know it when the request would be made from UI but using special DTO I get such exception in compilation time.

Question

So, what is the best practiсe or maybe patterт should I use in such situation? At the moment I see the following solutions:

  1. Duplicate DTOs in both AdapterService and ApiGateway services. So, any changes in one class must be supported in another
  2. Use Map<String, Object> at my own peril and risk, hoping that nothing will change there
  3. Share SpecialProductDTO between ApiGateway and AdapterSerivce in some separate library and service (seems to be antipattern because of sharing someting can make a lot of problems)

Сan anyone help?

Yury
  • 3
  • 4
  • I don't quite follow the transformation to 'Map because ProductService requires it via API' - what is the communication protocol between the blue and red zone? – crizzis Apr 25 '21 at 16:39

1 Answers1

0

In my opinion, there's nothing wrong on duplicating DTOs.

Also, there's nothing wrong on providing the DTO in a separate library to be imported on each project, you will only be sharing the ProductService's contract and that's it. It does not cause any tight coupling between the Api Gateway and the Adapter. If the contract changes, then it must be changed on all of it's consumers (api gateway and adapter), simple as that.

About using Maps: usually I don't recommend this, because, like you said, you will not be able to take advantages of the built-in Bean Validations that Spring (and other frameworks) provides, but not only that, you'll also, depending on the situation, be using lots of casts and type conversions, which is not good and can be prevented by using DTOs.

Also, be aware that a DTO, in my opinion, should not be named with the suffix of 'DTO'. That's because a name like SpecialProductDTO doesn't clearly states where this object is being used or should be used.

Instead, prefer a something like CreateSpecialProductRequest - this indicates that this object is used when creating a Special Product. Another example is CreateSpecialProductResponse which just represents the response (if needed) after a Special Product creation. Take a look at this StackOverflow answer: Java data transfer object naming convention?

Matheus
  • 3,058
  • 7
  • 16
  • 37