2

I have a Foo service that, among other things, retrieves data from a table called Widget. This table has about 50 columns. This service is called by a request from a browser.

Now, I have another service, Bar, that wants to call Foo. This is a Windows Service that we can trust because there is no end user. Bar also needs data from the Widget table. However, Bar only needs 3 columns of data. Bar needs this before calling Foo.

My concern is the inefficiency of Bar getting Widget, only for Foo to also do the same thing. I can think of three options:

  1. Bar makes the same repo call to get all the Widget data.
  2. Same as #1, but Bar passes Widget data to Foo.
  3. Bar makes a different repo call to only get the three columns it needs.

Number 1 is inefficient because Bar and Foo will get the same data.

Number 2 works, but Foo has to trust the caller. Foo can't trust the caller in one case, because it's from a web request. But Foo can trust the service call.

Number 3 also works, but it's slightly inefficient because it's getting Widget data, although it's a much slimmer version. And we would need DTO just for this Bar process, and the DTO would just have the three fields.

I'm leaning towards #3, but would love some input, especially if there is another approach that I'm not considering.

15
  • "Bar needs this before calling Foo" bar need the data before it can call the api that sends it the data? - WHY?
    – Ewan
    Jan 2, 2020 at 15:24
  • 1
    Efficiency is something that has to be measured. Your concern is unfounded unless you can demonstrate that you really do have a performance problem. Jan 2, 2020 at 15:25
  • In any case, I've worked with systems that have hundreds of DTO's. It's not just one DTO per service; it's one DTO per entity and sometimes multiple DTO's per entity. So the number of DTO's is probably the least of your problems. Jan 2, 2020 at 15:31
  • @Ewan Because Foo processes only for one widget. Bar needs to have all of the widgets processed, in a certain Widget order (hence the three columns it needs). So Bar gets all of the widgets, and then calls Foo for each widget to be processed.
    – Bob Horn
    Jan 2, 2020 at 15:31
  • add a getall() method to foo
    – Ewan
    Jan 2, 2020 at 15:33

3 Answers 3

1

Option 3 is the cleanest and simplest.

It is totally fine for a Repository to show different views of the same data. A large part of its value is that it hides the implementation details from clients. Maybe someday your Widget table isn't a table anymore, or it's more than one table, or it's chained to some outside service call. Maybe the three fields that Bar wants will be somewhat decoupled from the other 47 fields that Foo cares about.

Long story short, Option 1 is making a lot of assumptions about how the Repository will be implemented. Option 2 makes an extra, gratuitous dependency between Bar and Foo. Option 3 lets Bar get what it needs with the fewest unnecessary assumptions and restrictions.

-2

Basically No, it's a bad design to have multiple objects representing the same table with differing numbers of fields.

Such a design lacks flexibility, if some evolution of the logic requires access to more fields, you have to change the DTO in order to implement that logic.

If you add more services as the product grows and keep making new DTOs you end up with a plethora of different versions of the same thing.

Its also bad to have Bar making a direct call to the database instead of going through the API.

The API should hide the database from all other applications, giving you a single point at which you can ensure data consistency and manage access.

In a completely finished system, you might add extra 'lite' objects in-order to extract maximum performance as you no longer have any possible future changes to worry about

-2

Yeah it's OK, but typically it's not done that way.

There are typically two types of applications for data:

  • Providers (Services)
  • Consumers

The provider provides the data, usually all of it, and then the consumer determines how to use that data. The could provider very specific, but usually it just dumps out everything.

The problem arises in that your case only a few fields are being used. This is also compounded in the mobile arena because bandwidth is limited. So, we don't want all the data going down to the client. Typically, in those cases, we add another server (middleman) to handle the mapping of all the data to a much smaller structure that is specific to our needs.

Provider (Server) ->  Middleman (Server) -> Client

The provider just adds new fields over time, the middle man handles which fields to use, and the client displays that data.

Now, you can cut out the middleman if you own everything and yes your system will be more efficient (less hops etc.) But typically these providers are used by more than one consumer, so soon as @Ewan points out this could become a mess.

GraphQL is an example as a technology used to map out a structure from a larger orginal one, but you can easily parse JSON or XML to create smaller structures as well. Typically, we also want any business logic with the mapping as well, so our client stays as lite as possible since client side technology and frameworks evolve very rapidly.

3
  • If I understand correctly, the difference between your answer, and #3, is in how much data is retrieved. In your answer, all of the data is retrieved, in each case, and then mapped to the consumer. In the other approach, each consumer gets the fields/columns it needs, but only those fields/columns are retrieved from the DB.
    – Bob Horn
    Jan 2, 2020 at 16:05
  • @Bob - Correct. You certainly have multiple queries and DTOs where there will be some overlap, or you can just return everything and have the consumers deal with it. It depends how much you own and how far upstream you want those limits.
    – Jon Raynor
    Jan 2, 2020 at 16:30
  • I own it all. I just didn't want to retrieve a lot of data, only to get it again seconds later. And that would happen a lot in a short period of time.
    – Bob Horn
    Jan 2, 2020 at 16:32

Your Answer

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

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