8

I am traditionally a desktop app developer, but circumstance has thrust me into the role of doing web client and corresponding REST api logic for a project I am involved in. Unfortunately, I'm a one-man-show, so my opportunities to learn new patterns or techniques from co-workers is somewhat limited. When I was ramping up, I had the opportunity to work (briefly) with a contractor who exposed me to the idea that my server REST logic should be separated into a Controller (where the actual GET/PUT/POST/DELETE methods live) and a Service that does the heavy lifting. As it was explained to me, the Service might further interact with one or more Providers or Stores.

My understanding is that a Provider would wrap logic that interacts with another system, maybe another web API, or a weird bit of legacy code, or maybe some proprietary bit of hardware (like a temperature guage, for example). Additionaly, a Store would wrap the CRUD logic for actual data objects to SQL, NoSQL, text files, whatever.

Assuming all this makes sense, and is indeed how the pros do it, he further advised me to incorporate the naming into my classes, like this:

PizzaController might proxy the received web API calls to the PizzaService, which in turn could talk to both the PizzaProvider and the RefridgeratorStore.

I'm not 100% positive this is how the real world does things - but it made enough sense to me to sound credible and I've generally adopted this pattern and so far it has worked well enough to organize my logic.

But this is where a few questions comes in:

First, is this view of the separation of my classes really how others structure their code? And if I am close, but not quite, what corrections should I be making?

Second, is it legitimate for one Service to instance and leverage a second Service? For example, what if my PizzaService has to decide if we want delivery or we are going to make a pizza from scratch - it might want to either invoke the PizzaProvider -or- it might simply want to defer to the PizzaMakerService. If the PizzaService doesn't make this decision, then the decision logic would have to live earlier in the food chain (no pun intended). That would infer my PizzaController would have to decide if it should use the PizzaService -or- the PizzaMakerService; and that doesn't smell right to me.

And finally, (following the pattern I was shown) my Services frequently return a data object back to my Controller, where the Controller will map one or more properties into a ViewModel that is returned to my client. I've found that I could just as easily map the relevant bits of data into a anonymous object (C#) on the fly and return it to my client. The returned JSON is the same, so why introduce the class definition for a ViewModel at all? Is there a taboo against just crafting an anonymous object in the Controller and returning it?

I realize (in my situation) I can pretty much do anything I want - how I name classes, how I separate logic, if I use anonymous objects - it's really entirely my code. But these questions have been nagging at me for a while, and I'd like to be doing things as close to 'correctly' as possible. It's likely these questions (or a variation) have been asked and answered before, so I'll apologize now for any duplication - but for the life of me I can't seem to find any direct answers.

Thanks!

1
  • that contractor led you to the correct path, keep walking it. Commented Jul 6, 2020 at 11:08

2 Answers 2

3

First, is this view of the separation of my classes really how others structure their code?

Yes, unfortunately.

Second, is it legitimate for one Service to instance and leverage a second Service?

Services are just bags of procedures. They have no state, nor any abstraction capabilities. They are basically a flat list of procedures that operate on some external data, just like in old C times. Therefore they can call each-other anytime.

Some projects do impose an additional arbitrary (i.e. non-business) layering of services, like for authentication or authorization, etc.

And finally [...]. I've found that I could just as easily map the relevant bits of data into a anonymous object (C#) on the fly [...]. The returned JSON is the same, so why introduce the class definition for a ViewModel at all?

You're right, there is no reason. If you're only using the data to iterate all fields and do something with them indiscriminately, then using a dynamic object or a map is sufficient.

[...] and I'd like to be doing things as close to 'correctly' as possible [...]

I see your quotes :), so I wanted to leave an alternative here. The problem with the procedural approach above is that it works if you just have to adjust mostly independent pieces of "logic", but fails miserably when logic is interdependent and/or you sometimes have to change the data too. Changing the data is hard, because it can be potentially used anywhere, so you have to check everywhere.

The alternative is to include the logic with the data, so when either (or both) of them change, the impact is still likely localized.

This approach is less popular, but much more maintainable. Unfortunately there are no such "patterns" like the above for this solution, because the design in this case closely follows the business and not technical things, like repositories and controllers and such.

For example, if we are just dumping database records to json, I would just do Database.query("select...").toJson() and be done with it.

1

First, is this view of the separation of my classes really how others structure their code? And if I am close, but not quite, what corrections should I be making?

This is how many suggest to structure their code. But it is my opinion that such rigid structure might hinder some design decissions, especially when you don't understand the reasons behind this kind of structure. I personally find addition of Service to be unecessary, and if such abstraction is needed, then it is not related to the controller, but to the business logic that should be executed.

The main reason for this kind of separation is test automation. This is why Provider and Store are abstracted away. So they can be replaced by test doubles within automated tests.

Second, is it legitimate for one Service to instance and leverage a second Service?

I see why not. As long as Services are loosely coupled with Controllers and are more representation of business behavior than just second part of the Controller.

Is there a taboo against just crafting an anonymous object in the Controller and returning it?

I don't think there is anything wrong with this. But I would advise against it. C#'s big advantage is static typing. And you should leverage it. For example, some code documentation tools (like Swagger) might make use of the returned type to generate a documentation to your endpoint, which will then include the returned structure. That cannot be done with anonymous type. Also, if you have automated tests written against your Controller, then returning an anonymous type will make it more difficult to access returned data by the test.

1
  • @geo... I agree with this answer. I would add only just some tiny comments here. The Store is usually called Repository. The Provider is usually called either Adapter or Facade. The Provider is too generic and it does not tell anything about the purpose of the object itself. While the Adapter and Facade in conjunction can be used as an Anti-corruption layer between your system and the 1st / 3rd party. Commented Jul 21, 2020 at 13:40

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.