0

I am trying to convert an API Response into a totally different ViewModel, for multiple components.

a) One solution is to map/pipe the Data directly in the API proxy, however then API proxy is not very reusable, if I just want plain raw API Data.

b) This model-adapter pattern may not work, since in the example the adapter creates the same Data Object Type as API. https://florimond.dev/blog/articles/2018/09/consuming-apis-in-angular-the-model-adapter-pattern/ . Our data converter below, brings out totally different one.

Looking for any other good solutions.

Regular API:

export class ProductService {
  private readonly apiUrl = environment.baseUrl + '/api/CalculateProducts/';
  constructor(private httpClient: HttpClient) { }

  getProductData(
    productValuationDtoRequest: ProductValuationDtoRequest
  ): Observable<ProductValuationResponse>  {
    return this.httpClient.request<ProductValuationResponse>('post', this.apiUrl,
      {body: productValuationDtoRequest}
    );
  }
}

Data Converter:

export class CalculateProductModelService {
  constructor() { }

  convertData(
    productValuationResponse: ProductValuationResponse): CalculateCostModel  {

    const calculateProductModel: CalculateProductModel = {
      valuationAttribute: productValuationResponse?.productValuationDetail[0]?.productValuationAttribute?.description,
      livingAreaQuantity: productValuationResponse?.productValuationDetail[0]?.quantity,
      livingAreaRate: productValuationResponse?.productValuationDetail[0]?.improvementUnitValue * 1.03,
      livingAreaValue: productValuationResponse?.productValuationDetail[0]?.attributeTotalImprovementValue,
      numberOfUnits: productValuationResponse?.numberOfUnits * 2,
      replacementCostNew: productValuationResponse?.replacementCostNew,
      goodPercentage: productValuationResponse?.percentageGood,
      goodValue: productValuationResponse?.replacementCostNew * (100 - productValuationDto?.percentageGood) / 100,
      total: productValuationResponse?.totalImprovementValue
    };
    return calculateProductModel;
  }
1
  • 1
    You should start accepting answers for your questions or at least leave a comment to the answers you think are not acceptable. Commented Sep 21, 2020 at 5:08

1 Answer 1

8
+50

If I understand the problem right, I would add a method to ProductService to return the Model, so that ProductService ends up having 2 methods, one for the raw API data and one for the model.

The new method would internally use the current getProductData method to fetch the data and the CalculateProductModelService to perform the transformation.

The code would look like

export class ProductService {
  private readonly apiUrl = environment.baseUrl + '/api/CalculateProducts/';
  constructor(private httpClient: HttpClient, 
     private productModelService: CalculateProductModelService) { }

  getProductData(
    productValuationDtoRequest: ProductValuationDtoRequest
  ): Observable<ProductValuationResponse>  {
    return this.httpClient.request<ProductValuationResponse>('post', this.apiUrl,
      {body: productValuationDtoRequest}
    );
  }

  getProductModel(
    productValuationDtoRequest: ProductValuationDtoRequest
  ): Observable<CalculateProductModel>  {
    return this.getProductData(productValuationDtoRequest).pipe(
      map(rawData => productModelService.convertData(rawData))
    );
  }
}

In the above example I use Dependency Injection to inject CalculateProductModelService into ProductService. In this way CalculateProductModelService is not dependent on http calls and can be easily tested.

Sign up to request clarification or add additional context in comments.

Comments