1

My api is exposing information centering around an entity Company. Companies are complex, and can contain Vendors directly, or can contain sub-companies which contain vendors themselves.

A consumer of our API has asked for the addition of several new endpoints from which they can request information about a company and its vendors; the catch is, for these new endpoints, they want all the company's vendors listed and merged in with its sub-companies' vendors.

The transformation would be as follows:

Company1         ==========>     Company1
|                                |
+--Vendor1                       +--Vendor1
+--Company2                      +--Vendor2
   |                             +--Vendor3
   +--Vendor2
   +--Company3
      |
      +--Vendor1
      +--Vendor3

Currently, the architecture of the project is based on Clean-Architecture / Onion-Architecture with four major project categories:

  1. Web Api
  2. Use Cases
  3. Data Access
  4. Business Objects

The transformation logic is in actuality, much more complicated than the example above, and will be needed multiple places, so placing the logic directly in the API controller methods is obviously a no go.

Since this is an interface concern, one could make an argument for having a function in the WebAPI project which does the transformation. This would require the least new classes since the DTO which represents the data sent to the requester could be re-purposed, but this method represents the least ability to reuse (we are are considering making a non-http API as well).

The method which I am leaning towards is to create a dedicated UseCase for the merge. This seems to me to be the best balance of re-usability and simplicity.

Another option is to create the method in the Company business object itself, because what knows how to transform a company better than that company? Simple to use, all the data and logic are in one place, and very reusable. But, then we have to figure out what format to return that transformation in, and all that can get very complicated if we want to attempt to reuse the business objects already in place as the return format.

I'm sure there are other options which I have not thought of too.

What are architectural best practices / advice for where to place this entity transformation logic?

1

In short you are taking a hierarchy of objects and flattening them (a little).

Putting this in a Use Case would make sense if use cases can be composed of other use cases, or if there was only one use case that needed this behavior.

Unless you can come up with a good business reason for this consolidated view of the vendors of a company other than "I don't want to see duplicate vendors", then I would lean towards a use case for this one.

If you can come up with a good business reason, then a new business class would be the way to go, and push this into the enterprise-wide area of the architecture.

Then again, maybe KISS prevails here, and all you need is a method on a Company object that returns a unique list of vendors from the whole company hierarchy. You are free to use this in any use case you see fit.

  • Thanks for the thoughtful answer Greg. Now I am liking your idea of a separate business class MergedCompany. I have my business objects divided up into Critical and Application objects, and I'm struggling to figure out which category it would go in. As for you last "KISS" suggestion, that is a no go since the situation is much more complicated than just returning a list of vendors. – TheCatWhisperer Nov 13 '18 at 21:24
  • @TheCatWhisperer: I would only create something like "MergedCompany" if it does more than return a unique list of vendors. If that is all you need, I would strongly suggest a use case instead. – Greg Burghardt Nov 14 '18 at 20:34
  • just pushed the MergedCompany class. Put it in the Derivatives category. The good thing is, it let me put the data and the behavior in the same class. The use case would need the data separated from the data. – TheCatWhisperer Nov 14 '18 at 20:45

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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